qtractor-1.5.11/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215124701674015263 xustar0030 mtime=1767080892.771639157 30 atime=1767080892.771639157 30 ctime=1767080892.771639157 qtractor-1.5.11/CMakeLists.txt0000644000175000001440000007263015124701674015263 0ustar00rncbcuserscmake_minimum_required (VERSION 3.15) project (Qtractor VERSION 1.5.11 DESCRIPTION "An Audio/MIDI multi-track sequencer" HOMEPAGE_URL "https://qtractor.org" LANGUAGES C CXX) set (PROJECT_TITLE "${PROJECT_NAME}") string (TOLOWER "${PROJECT_TITLE}" PROJECT_NAME) set (PROJECT_COPYRIGHT "Copyright (C) 2005-2025, 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 libvorbis(file) availability. option (CONFIG_LIBVORBIS "Enable libvorbis interface (default=yes)" 1) # Enable libmad availability. option (CONFIG_LIBMAD "Enable libmad interface (default=yes)" 1) # Enable libsamplerate availability. option (CONFIG_LIBSAMPLERATE "Enable libsamplerate interface (default=yes)" 1) # Enable librubberband availability. option (CONFIG_LIBRUBBERBAND "Enable librubberband interface (default=yes)" 1) option (CONFIG_LIBRUBBERBAND_R3 "Enable librubberband-R3 interface (default=yes)" 1) # Enable libaudio support. option (CONFIG_LIBAUBIO "Enable libaubio interface support (DEPRECATED) (default=no)" 0) # Enable minibpm support. option (CONFIG_MINIBPM "Enable minibpm interface support (default=yes)" 1) # Enable liblo availability. option (CONFIG_LIBLO "Enable liblo interface (default=yes)" 1) # Enable libz availability. option (CONFIG_LIBZ "Enable libz interface (default=yes)" 1) # Enable LILV support. option (CONFIG_LIBLILV "Enable LILV interface support (default=yes)" 1) # Enable SUIL support. option (CONFIG_LIBSUIL "Enable SUIL interface support (default=yes)" 1) # Enable SSE optimization. option (CONFIG_SSE "Enable SSE optimization (default=yes)" 1) # Enable LADSPA support. option (CONFIG_LADSPA "Enable LADSPA plug-in support (default=yes)" 1) # Enable DSSI support. option (CONFIG_DSSI "Enable DSSI plug-in support (default=yes)" 1) # Enable VST support. option (CONFIG_VST2 "Enable VST2 plug-in support (default=yes)" 1) option (CONFIG_VESTIGE "Enable VeSTige header support (default=yes)" 1) # Enable VST3 support. option (CONFIG_VST3 "Enable VST3 plug-in support (default=yes)" 1) # Enable LV2 support. option (CONFIG_LV2 "Enable LV2 plug-in support (default=yes)" 1) option (CONFIG_LV2_EVENT "Enable LV2 plug-in MIDI/Event support (DEPRECATED) (default=no)" 0) option (CONFIG_LV2_ATOM "Enable LV2 plug-in MIDI/Atom support (default=yes)" 1) option (CONFIG_LV2_CVPORT "Enable LV2 plug-in CVPort support (DUMMY) (default=yes)" 1) option (CONFIG_LV2_WORKER "Enable LV2 plug-in Worker/schedule support (default=yes)" 1) option (CONFIG_LV2_UI "Enable LV2 plug-in UI support (default=yes)" 1) option (CONFIG_LV2_EXTERNAL_UI "Enable LV2 plug-in External UI support (default=yes)" 1) option (CONFIG_LV2_STATE "Enable LV2 plug-in State support (default=yes)" 1) option (CONFIG_LV2_STATE_FILES "Enable LV2 plug-in State Files support (default=yes)" 1) option (CONFIG_LV2_STATE_MAKE_PATH "Enable LV2 plug-in State Make Path support (default=no)" 0) option (CONFIG_LV2_STATE_FREE_PATH "Enable LV2 plug-in State Free Path support (default=yes)" 1) option (CONFIG_LV2_PROGRAMS "Enable LV2 plug-in Programs support (default=yes)" 1) option (CONFIG_LV2_MIDNAM "Enable LV2 plug-in MIDNAM support (default=yes)" 1) option (CONFIG_LV2_PRESETS "Enable LV2 plug-in Presets 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) option (CONFIG_LV2_TIME "Enable LV2 plug-in Time support (default=yes)" 1) option (CONFIG_LV2_TIME_POSITION "Enable LV2 plug-in Time/position support (default=yes)" 1) option (CONFIG_LV2_OPTIONS "Enable LV2 plug-in Options support (default=yes)" 1) option (CONFIG_LV2_BUF_SIZE "Enable LV2 plug-in Buf-size support (default=yes)" 1) option (CONFIG_LV2_PARAMETERS "Enable LV2 plug-in Parameters support (default=yes)" 1) option (CONFIG_LV2_UI_TOUCH "Enable LV2 plug-in UI Touch interface support (default=yes)" 1) option (CONFIG_LV2_UI_REQ_VALUE "Enable LV2 plug-in UI Request-value support (default=yes)" 1) option (CONFIG_LV2_UI_IDLE "Enable LV2 plug-in UI Idle interface support (default=yes)" 1) option (CONFIG_LV2_UI_SHOW "Enable LV2 plug-in UI Show interface support (default=yes)" 1) option (CONFIG_LV2_UI_GTK2 "Enable LV2 plug-in UI GTK2 native support (default=yes)" 1) option (CONFIG_LV2_UI_GTKMM2 "Enable LV2 plug-in UI GTKMM2 native support (default=yes)" 1) option (CONFIG_LV2_UI_X11 "Enable LV2 plug-in UI X11 native support (default=yes)" 1) # Enable CLAP support. option (CONFIG_CLAP "Enable CLAP plug-in support (default=yes)" 1) # Enable JACK session support. option (CONFIG_JACK_SESSION "Enable JACK session support (default=yes)" 1) # Enable JACK latency support. option (CONFIG_JACK_LATENCY "Enable JACK latency support (default=yes)" 1) # Enable JACK metadata support. option (CONFIG_JACK_METADATA "Enable JACK metadata support (default=yes)" 1) # Enable NSM support. option (CONFIG_NSM "Enable NSM support (default=yes)" 1) # Enable unique/single instance. option (CONFIG_XUNIQUE "Enable unique/single instance (default=no)" 0) # Enable gradient eye_candy. option (CONFIG_GRADIENT "Enable gradient eye-candy (default=yes)" 1) # Enable debugger stack_trace option (assumes --enable-debug). option (CONFIG_STACKTRACE "Enable debugger stack-trace (default=no)" 0) # Enable Wayland support option. option (CONFIG_WAYLAND "Enable Wayland support (NOT RECOMMENDED) (default=no)" 0) # 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 () # 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) if (NOT CONFIG_QT6) find_package (Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS X11Extras) endif () if (CONFIG_XUNIQUE) find_package (Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Network) endif () find_package (Qt${QT_VERSION_MAJOR}LinguistTools) include (CheckIncludeFile) include (CheckIncludeFiles) include (CheckIncludeFileCXX) include (CheckStructHasMember) include (CheckFunctionExists) include (CheckLibraryExists) include (CheckTypeSize) # Checks for libraries. if (WIN32) check_function_exists (lroundf CONFIG_ROUND) else () find_library (MATH_LIBRARY m) if (MATH_LIBRARY) set (CMAKE_REQUIRED_LIBRARIES "${MATH_LIBRARY};${CMAKE_REQUIRED_LIBRARIES}") # link_libraries (${MATH_LIBRARY}) check_function_exists (lroundf CONFIG_ROUND) else () message (FATAL_ERROR "*** math library not found.") endif () find_library (DL_LIBRARY dl) if (NOT DL_LIBRARY) message (FATAL_ERROR "*** dl library not found.") endif () endif () # Check for IEEE 32bit float optimizations. set (CONFIG_FLOAT32 1) # Check for SSE optimization. if (CONFIG_SSE) if (CMAKE_SYSTEM_PROCESSOR MATCHES "^x86" OR CMAKE_SYSTEM_PROCESSOR MATCHES "^i[3456]86") check_include_file (xmmintrin.h HAVE_XMMINTRIN_H) if (HAVE_XMMINTRIN_H) add_compile_options (-msse -mfpmath=sse) endif () endif () endif () 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 () # Check for LADSPA headers. if (CONFIG_LADSPA) check_include_file (ladspa.h HAVE_LADSPA_H) if (NOT HAVE_LADSPA_H) set (CONFIG_LADSPA 0) endif () endif () # Check for DSSI headers. if (CONFIG_DSSI) check_include_files ("stddef.h;dssi.h" HAVE_DSSI_H) if (NOT HAVE_DSSI_H) set (CONFIG_DSSI 0) endif () endif () # Check for VST headers. if (CONFIG_VST2 AND NOT CONFIG_VST2SDK) check_include_file (aeffectx.h HAVE_AEFFECTX_H) if (NOT HAVE_AEFFECTX_H AND CONFIG_VESTIGE) set (VESTIGE_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src/vestige) set (CMAKE_REQUIRED_INCLUDES "${VESTIGE_INCLUDES};${CMAKE_REQUIRED_INCLUDES}") include_directories (${VESTIGE_INCLUDES}) check_include_file (vestige.h HAVE_VESTIGE_H) if (NOT HAVE_VESTIGE_H) set (CONFIG_VESTIGE 0) endif () set (CONFIG_VST2 ${CONFIG_VESTIGE}) else () set (CONFIG_VESTIGE 0) endif () endif () # Find package modules include (FindPkgConfig) # Check for JACK libraries. pkg_check_modules (JACK REQUIRED 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_LIBJACK 1) set (CMAKE_REQUIRED_LIBRARIES "${JACK_LIBRARY};${CMAKE_REQUIRED_LIBRARIES}") # Check for JACK session event callback availability. if (CONFIG_JACK_SESSION) check_function_exists (jack_set_session_callback CONFIG_JACK_SESSION) endif () # Check for (new) JACK latency support availability. if (CONFIG_JACK_LATENCY) check_function_exists (jack_port_get_latency_range CONFIG_JACK_LATENCY) endif () # Check for JACK metadata support availability. if (CONFIG_JACK_METADATA) check_function_exists (jack_get_property CONFIG_JACK_METADATA) endif () # Check for jack_set_port_rename_callback. check_function_exists (jack_set_port_rename_callback CONFIG_JACK_PORT_RENAME) else () message (FATAL_ERROR "*** JACK library not found.") set (CONFIG_LIBJACK 0) endif () # Check for ALSA libraries. pkg_check_modules (ALSA REQUIRED IMPORTED_TARGET alsa) if (ALSA_FOUND) find_library (ALSA_LIBRARY NAMES ${ALSA_LIBRARIES} HINTS ${ALSA_LIBDIR}) endif () if (ALSA_LIBRARY) set (CONFIG_LIBASOUND 1) #set (CMAKE_REQUIRED_LIBRARIES "${ALSA_LIBRARY};${CMAKE_REQUIRED_LIBRARIES}") else () message (FATAL_ERROR "*** ALSA library not found.") set (CONFIG_LIBASOUND 0) endif () # Check for SNDFILE libraries. pkg_check_modules (SNDFILE REQUIRED IMPORTED_TARGET sndfile) if (SNDFILE_FOUND) find_library (SNDFILE_LIBRARY NAMES ${SNDFILE_LIBRARIES} HINTS ${SNDFILE_LIBDIR}) endif () if (SNDFILE_LIBRARY) set (CONFIG_LIBSNDFILE 1) #set (CMAKE_REQUIRED_LIBRARIES "${SNDFILE_LIBRARY};${CMAKE_REQUIRED_LIBRARIES}") else () message (FATAL_ERROR "*** SNDFILE library not found.") set (CONFIG_LIBSNDFILE 0) endif () # Check for VORBIS libraries. if (CONFIG_LIBVORBIS) pkg_check_modules (VORBIS IMPORTED_TARGET vorbis) if (NOT VORBIS_FOUND) message (WARNING "*** VORBIS library not found.") set (CONFIG_LIBVORBIS 0) endif () endif () if (CONFIG_LIBVORBIS) pkg_check_modules (VORBISENC IMPORTED_TARGET vorbisenc) pkg_check_modules (VORBISFILE IMPORTED_TARGET vorbisfile) pkg_check_modules (OGG IMPORTED_TARGET ogg) endif () # Check for MAD libraries. if (CONFIG_LIBMAD) pkg_check_modules (MAD IMPORTED_TARGET mad) if (NOT MAD_FOUND) message (WARNING "*** MAD library not found.") set (CONFIG_LIBMAD 0) endif () endif () # Check for SAMPLERATE libraries. if (CONFIG_LIBSAMPLERATE) pkg_check_modules (SAMPLERATE IMPORTED_TARGET samplerate) if (NOT SAMPLERATE_FOUND) message (WARNING "*** SAMPLERATE library not found.") set (CONFIG_LIBSAMPLERATE 0) endif () endif () # Check for RUBBERBAND libraries. if (CONFIG_LIBRUBBERBAND_R3) pkg_check_modules (RUBBERBAND IMPORTED_TARGET rubberband>=3.0.0) if (NOT RUBBERBAND_FOUND) set (CONFIG_LIBRUBBERBAND_R3 0) set (CONFIG_LIBRUBBERBAND 1) endif () endif () if (CONFIG_LIBRUBBERBAND) pkg_check_modules (RUBBERBAND IMPORTED_TARGET rubberband) if (NOT RUBBERBAND_FOUND) message (WARNING "*** RUBBERBAND library not found.") set (CONFIG_LIBRUBBERBAND 0) endif () endif () # Check for AUBIO libraries (DEPRECATED) if (CONFIG_LIBAUBIO) set (CONFIG_MINIBPM 0) else () set (CONFIG_MINIBPM 1) endif () if (CONFIG_LIBAUBIO) pkg_check_modules (AUBIO IMPORTED_TARGET aubio>=0.4.1) if (NOT AUBIO_FOUND) message (WARNING "*** AUBIO library not found.") set (CONFIG_LIBAUBIO 0) set (CONFIG_MINIBPM 1) endif () 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 ZLIB libraries. if (CONFIG_LIBZ) pkg_check_modules (ZLIB IMPORTED_TARGET zlib) if (NOT ZLIB_FOUND) message (WARNING "*** ZLIB library not found.") set (CONFIG_LIBZ 0) endif () endif () # Check for VST3 SDK. if (CONFIG_VST3 AND NOT CONFIG_VST3SDK) set (CONFIG_VST3SDK ${CMAKE_CURRENT_SOURCE_DIR}/src/vst3) endif () if (CONFIG_VST3 OR CONFIG_LV2_UI_X11) pkg_check_modules (XCB xcb) if (NOT XCB_FOUND) message (WARNING "*** XCB library not found.") endif () endif () if (CONFIG_VST3) find_library (PTHREAD_LIBRARY pthread) if (NOT PTHREAD_LIBRARY) message (WARNING "*** pthread library not found.") 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 () # 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_LIBLILV 0) set (CONFIG_LV2_UI 0) endif () if (CONFIG_LV2_UI) 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) endif () if (CONFIG_LV2_UI) # Check for LV2 external UI instance access. if (CONFIG_LV2_EXTERNAL_UI) if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/instance-access/instance-access.h HAVE_LV2_INSTANCE_ACCESS_H) else () check_include_file (lv2/instance-access/instance-access.h HAVE_LV2_INSTANCE_ACCESS_H) endif () if (NOT HAVE_LV2_INSTANCE_ACCESS_H) set (CONFIG_LV2_EXTERNAL_UI 0) endif () endif () endif () endif () if (NOT CONFIG_LV2_UI) set (CONFIG_LIBSUIL 0) set (CONFIG_LV2_EXTERNAL_UI 0) set (CONFIG_LV2_UI_TOUCH 0) set (CONFIG_LV2_UI_REQ_VALUE 0) set (CONFIG_LV2_UI_IDLE 0) set (CONFIG_LV2_UI_SHOW 0) set (CONFIG_LV2_UI_GTK2 0) set (CONFIG_LV2_UI_GTKMM2 0) set (CONFIG_LV2_UI_X11 0) endif () # Check for optional LILV library. if (CONFIG_LIBLILV) pkg_check_modules (LILV IMPORTED_TARGET lilv-0) if (LILV_FOUND) find_library (LILV_LIBRARY NAMES ${LILV_LIBRARIES} HINTS ${LILV_LIBDIR}) endif () if (LILV_LIBRARY) set (CONFIG_LIBLILV 1) set (CMAKE_REQUIRED_LIBRARIES "${LILV_LIBRARY};${CMAKE_REQUIRED_LIBRARIES}") check_function_exists (lilv_file_uri_parse CONFIG_LILV_FILE_URI_PARSE) check_function_exists (lilv_world_unload_resource CONFIG_LILV_WORLD_UNLOAD_RESOURCE) else () message (WARNING "*** LILV library not found.") set (CONFIG_LIBLILV 0) endif () endif () if (NOT CONFIG_LIBLILV) set (CONFIG_LV2 0) set (CONFIG_LV2_EVENT 0) set (CONFIG_LV2_ATOM 0) set (CONFIG_LV2_CVPORT 0) set (CONFIG_LV2_WORKER 0) set (CONFIG_LV2_UI 0) set (CONFIG_LV2_EXTERNAL_UI 0) set (CONFIG_LV2_STATE 0) set (CONFIG_LV2_STATE_FILES 0) set (CONFIG_LV2_STATE_MAKE_PATH 0) set (CONFIG_LV2_STATE_FREE_PATH 0) set (CONFIG_LV2_PROGRAMS 0) set (CONFIG_LV2_MIDNAM 0) set (CONFIG_LV2_PRESETS 0) set (CONFIG_LV2_PATCH 0) set (CONFIG_LV2_PORT_EVENT 0) set (CONFIG_LV2_PORT_CHANGE_REQUEST 0) set (CONFIG_LV2_TIME 0) set (CONFIG_LV2_TIME_POSITION 0) set (CONFIG_LV2_OPTIONS 0) set (CONFIG_LV2_BUF_SIZE 0) set (CONFIG_LV2_PARAMETERS 0) set (CONFIG_LV2_UI_TOUCH 0) set (CONFIG_LV2_UI_REQ_VALUE 0) set (CONFIG_LV2_UI_IDLE 0) set (CONFIG_LV2_UI_SHOW 0) set (CONFIG_LV2_UI_GTK2 0) set (CONFIG_LV2_UI_GTKMM2 0) set (CONFIG_LV2_UI_X11 0) set (CONFIG_LIBSUIL 0) endif () # Disable libsuil upon Qt >= 6.0.0 ... if (CONFIG_LIBSUIL AND QT_VERSION_MAJOR GREATER_EQUAL 6) set (CONFIG_LIBSUIL 0) endif () # Check for LV2 new UI instantiation availability (libsuil). if (CONFIG_LIBSUIL) pkg_check_modules (SUIL IMPORTED_TARGET suil-0) if (SUIL_FOUND) find_library (SUIL_LIBRARY NAMES ${SUIL_LIBRARIES} HINTS ${SUIL_LIBDIR}) endif () if (SUIL_LIBRARY) set (CONFIG_LIBSUIL 1) set (CMAKE_REQUIRED_LIBRARIES "${SUIL_LIBRARY};${CMAKE_REQUIRED_LIBRARIES}") check_function_exists (suil_instance_get_handle CONFIG_SUIL_INSTANCE_GET_HANDLE) else () message (WARNING "*** SUIL library not found.") set (CONFIG_LIBSUIL 0) endif () endif () if (CONFIG_LV2_EVENT) if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/event/event.h HAVE_LV2_EVENT_H) else () check_include_file (lv2/event/event.h HAVE_LV2_EVENT_H) endif () if (NOT HAVE_LV2_EVENT_H) set (CONFIG_LV2_EVENT 0) endif () endif () if (CONFIG_LV2_ATOM) 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) endif () set (CONFIG_LV2_ATOM_FORGE_OBJECT ${CONFIG_LV2_ATOM}) set (CONFIG_LV2_ATOM_FORGE_KEY ${CONFIG_LV2_ATOM}) endif () if (NOT CONFIG_LV2_ATOM) set (CONFIG_LV2_ATOM_FORGE_OBJECT 0) set (CONFIG_LV2_ATOM_FORGE_KEY 0) set (CONFIG_LV2_STATE 0) set (CONFIG_LV2_OPTIONS 0) set (CONFIG_LV2_TIME_POSITION 0) set (CONFIG_LV2_PORT_EVENT 0) endif () if (CONFIG_LV2_WORKER) if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/worker/worker.h HAVE_LV2_WORKER_H) else () check_include_file (lv2/worker/worker.h HAVE_LV2_WORKER_H) endif () if (NOT HAVE_LV2_WORKER_H) set (CONFIG_LV2_WORKER 0) endif () endif () if (CONFIG_LV2_STATE) if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/state/state.h HAVE_LV2_STATE_H) else () check_include_file (lv2/state/state.h HAVE_LV2_STATE_H) endif () if (NOT HAVE_LV2_STATE_H) set (CONFIG_LV2_STATE 0) endif () endif () if (NOT CONFIG_LV2_STATE) set (CONFIG_LV2_STATE_FILES 0) set (CONFIG_LV2_STATE_MAKE_PATH 0) set (CONFIG_LV2_STATE_FREE_PATH 0) set (CONFIG_LV2_PRESETS 0) set (CONFIG_LV2_PATCH 0) set (CONFIG_LV2_TIME 0) endif () if (CONFIG_LV2_STATE_FREE_PATH) if (CONFIG_LV2_OLD_HEADERS) set (CMAKE_EXTRA_INCLUDE_FILES "lv2/lv2plug.in/ns/ext/state/state.h") else () set (CMAKE_EXTRA_INCLUDE_FILES "lv2/state/state.h") endif () check_type_size (LV2_State_Free_Path LV2_STATE_FREE_PATH) if (NOT HAVE_LV2_STATE_FREE_PATH) set (CONFIG_LV2_STATE_FREE_PATH 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_MIDNAM) check_include_file (lv2_midnam.h HAVE_LV2_MIDNAM_H) if (NOT HAVE_LV2_MIDNAM_H) set (CONFIG_LV2_MIDNAM 0) endif () endif () if (CONFIG_LV2_PRESETS) if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/presets/presets.h HAVE_LV2_PRESETS_H) else () check_include_file (lv2/presets/presets.h HAVE_LV2_PRESETS_H) endif () if (NOT HAVE_LV2_PRESETS_H) set (CONFIG_LV2_PRESETS 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_TIME) if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/time/time.h HAVE_LV2_TIME_H) else () check_include_file (lv2/time/time.h HAVE_LV2_TIME_H) endif () if (NOT HAVE_LV2_TIME_H) set (CONFIG_LV2_TIME 0) endif () endif () if (NOT CONFIG_LV2_TIME) set (CONFIG_LV2_TIME_POSITION 0) endif () if (CONFIG_LV2_OPTIONS) if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/options/options.h HAVE_LV2_OPTIONS_H) else () check_include_file (lv2/options/options.h HAVE_LV2_OPTIONS_H) endif () if (NOT HAVE_LV2_OPTIONS_H) set (CONFIG_LV2_OPTIONS 0) endif () endif () if (NOT CONFIG_LV2_OPTIONS) set (CONFIG_LV2_BUF_SIZE 0) endif () if (CONFIG_LV2_BUF_SIZE) if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/buf-size/buf-size.h HAVE_LV2_BUF_SIZE_H) else () check_include_file (lv2/buf-size/buf-size.h HAVE_LV2_BUF_SIZE_H) endif () if (NOT HAVE_LV2_BUF_SIZE_H) set (CONFIG_LV2_BUF_SIZE 0) endif () endif () if (CONFIG_LV2_PARAMETERS) if (CONFIG_LV2_OLD_HEADERS) check_include_file (lv2/lv2plug.in/ns/ext/parameters/parameters.h HAVE_LV2_PARAMETERS_H) else () check_include_file (lv2/parameters/parameters.h HAVE_LV2_PARAMETERS_H) endif () if (NOT HAVE_LV2_PARAMETERS_H) set (CONFIG_LV2_PARAMETERS 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 () if (CONFIG_LV2_UI_TOUCH) if (CONFIG_LV2_OLD_HEADERS) check_struct_has_member ("LV2UI_Touch" touch lv2/lv2plug.in/ns/extensions/ui/ui.h HAVE_LV2_UI_TOUCH) else () check_struct_has_member ("LV2UI_Touch" touch lv2/ui/ui.h HAVE_LV2_UI_TOUCH) endif () if (NOT HAVE_LV2_UI_TOUCH) set (CONFIG_LV2_UI_TOUCH 0) endif () endif () if (CONFIG_LV2_UI_REQ_VALUE) if (CONFIG_LV2_OLD_HEADERS) check_struct_has_member ("LV2UI_Request_Value" request lv2/lv2plug.in/ns/extensions/ui/ui.h HAVE_LV2_UI_REQ_VALUE) else () check_struct_has_member ("LV2UI_Request_Value" request lv2/ui/ui.h HAVE_LV2_UI_REQ_VALUE) endif () if (NOT HAVE_LV2_UI_REQ_VALUE) set (CONFIG_LV2_UI_REQ_VALUE_FAKE 1) endif () endif () if (CONFIG_LV2_UI_GTK2) pkg_check_modules (GTK2 gtk+-2.0) if (NOT GTK2_FOUND) message (WARNING "*** GTK2 libraries not found.") set (CONFIG_LV2_UI_GTK2 0) set (CONFIG_LV2_UI_GTKMM2 0) endif () endif () if (CONFIG_LV2_UI_GTKMM2) pkg_check_modules (GTKMM2 gtkmm-2.4) if (NOT GTKMM2_FOUND) message (WARNING "*** GTKMM2 libraries not found.") set (CONFIG_LV2_UI_GTKMM2 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_TITLE} ${PROJECT_VERSION} (Qt ${QT_VERSION})") message ("\n Build target . . . . . . . . . . . . . . . . . . .: ${CONFIG_BUILD_TYPE}\n") show_option (" JACK Audio Connection Kit support . . . . . . . ." CONFIG_LIBJACK) show_option (" ALSA MIDI Sequencer support . . . . . . . . . . ." CONFIG_LIBASOUND) show_option (" General audio file support (libsndfile) . . . . ." CONFIG_LIBSNDFILE) show_option (" Ogg Vorbis audio file support (libvorbis) . . . ." CONFIG_LIBVORBIS) show_option (" MPEG-1 Audio Layer 3 file support (libmad) . . . ." CONFIG_LIBMAD) show_option (" Sample-rate conversion support (libsamplerate) . ." CONFIG_LIBSAMPLERATE) show_option (" Pitch-shifting support (librubberband) . . . . . ." CONFIG_LIBRUBBERBAND) if (CONFIG_LIBAUBIO) show_option (" Beat-detection support (libaubio) (DEPRECATED) . ." CONFIG_LIBAUBIO) else () show_option (" Beat-detection support (minibpm) . . . . . . . . ." CONFIG_MINIBPM) endif () show_option (" OSC service support (liblo) . . . . . . . . . . ." CONFIG_LIBLO) show_option (" Archive/Zip file support (zlib) . . . . . . . . ." CONFIG_LIBZ) show_option (" IEEE 32bit float optimizations . . . . . . . . . ." CONFIG_FLOAT32) show_option (" SSE optimization support (x86) . . . . . . . . . ." CONFIG_SSE) show_option (" LADSPA plug-in support . . . . . . . . . . . . . ." CONFIG_LADSPA) show_option (" DSSI plug-in support . . . . . . . . . . . . . . ." CONFIG_DSSI) show_option (" VST2 plug-in support . . . . . . . . . . . . . . ." CONFIG_VST2) show_option (" VST3 plug-in support . . . . . . . . . . . . . . ." CONFIG_VST3) show_option (" CLAP plug-in support . . . . . . . . . . . . . . ." CONFIG_CLAP) show_option (" LV2 plug-in support . . . . . . . . . . . . . . ." CONFIG_LV2) show_option (" LV2 plug-in support (liblilv) . . . . . . . . . ." CONFIG_LIBLILV) show_option (" LV2 plug-in UI support . . . . . . . . . . . . . ." CONFIG_LV2_UI) show_option (" LV2 plug-in UI support (libsuil) . . . . . . . . ." CONFIG_LIBSUIL) show_option (" LV2 plug-in External UI support . . . . . . . . ." CONFIG_LV2_EXTERNAL_UI) show_option (" LV2 plug-in MIDI/Event support (DEPRECATED) . . ." CONFIG_LV2_EVENT) show_option (" LV2 plug-in MIDI/Atom support . . . . . . . . . ." CONFIG_LV2_ATOM) show_option (" LV2 plug-in Worker/Schedule support . . . . . . ." CONFIG_LV2_WORKER) show_option (" LV2 plug-in State support . . . . . . . . . . . ." CONFIG_LV2_STATE) show_option (" LV2 plug-in State Files support . . . . . . . . ." CONFIG_LV2_STATE_FILES) show_option (" LV2 plug-in State Make Path support (DANGEROUS) ." CONFIG_LV2_STATE_MAKE_PATH) show_option (" LV2 plug-in State Free Path support . . . . . . ." CONFIG_LV2_STATE_FREE_PATH) show_option (" LV2 plug-in Programs support . . . . . . . . . . ." CONFIG_LV2_PROGRAMS) show_option (" LV2 plug-in MIDNAM support . . . . . . . . . . . ." CONFIG_LV2_MIDNAM) show_option (" LV2 plug-in Presets support . . . . . . . . . . ." CONFIG_LV2_PRESETS) 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 (EXPERIMENTAL) . ." CONFIG_LV2_PORT_CHANGE_REQUEST) show_option (" LV2 plug-in Time support . . . . . . . . . . . . ." CONFIG_LV2_TIME) show_option (" LV2 plug-in Time/position support . . . . . . . ." CONFIG_LV2_TIME_POSITION) show_option (" LV2 plug-in Options support . . . . . . . . . . ." CONFIG_LV2_OPTIONS) show_option (" LV2 plug-in Buf-size support . . . . . . . . . . ." CONFIG_LV2_BUF_SIZE) show_option (" LV2 plug-in Parameters support . . . . . . . . . ." CONFIG_LV2_PARAMETERS) show_option (" LV2 plug-in UI Touch interface support . . . . . ." CONFIG_LV2_UI_TOUCH) show_option (" LV2 plug-in UI Request-value support . . . . . . ." CONFIG_LV2_UI_REQ_VALUE) 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 GTK2 native support . . . . . . . ." CONFIG_LV2_UI_GTK2) show_option (" LV2 plug-in UI GTKMM2 native support . . . . . . ." CONFIG_LV2_UI_GTKMM2) show_option (" LV2 plug-in UI X11 native support . . . . . . . ." CONFIG_LV2_UI_X11) message ("") show_option (" JACK Session support . . . . . . . . . . . . . . ." CONFIG_JACK_SESSION) show_option (" JACK Latency support . . . . . . . . . . . . . . ." CONFIG_JACK_LATENCY) show_option (" JACK Metadata support . . . . . . . . . . . . . ." CONFIG_JACK_METADATA) message ("") show_option (" Non/New Session Management (NSM) support . . . . ." CONFIG_NSM) message ("") show_option (" VeSTige header support . . . . . . . . . . . . . ." CONFIG_VESTIGE) show_option (" Unique/Single instance support . . . . . . . . . ." CONFIG_XUNIQUE) show_option (" Gradient eye-candy . . . . . . . . . . . . . . . ." CONFIG_GRADIENT) show_option (" Debugger stack-trace (gdb) . . . . . . . . . . . ." CONFIG_STACKTRACE) message ("\n Install prefix . . . . . . . . . . . . . . . . . .: ${CONFIG_PREFIX}\n") qtractor-1.5.11/PaxHeaders/ChangeLog0000644000000000000000000000013215124701674014275 xustar0030 mtime=1767080892.772263542 30 atime=1767080892.771639157 30 ctime=1767080892.772263542 qtractor-1.5.11/ChangeLog0000644000175000001440000061172615124701674014302 0ustar00rncbcusersQtractor - An Audio/MIDI multi-track sequencer ---------------------------------------------- ChangeLog 1.5.11 2025-12-30 An End-of-Year'25 Release. - Refactored Clip/Tempo Adjust.. tempo/beat-detection function to Breakfastquay::minibpm as a submodule, in alternative to the now being deprecated (lib)aubio. 1.5.10 2025-12-19 An Early Winter'25 Release. - Fixed 'mime-info' file to the correct MIME-type icon names. - Improved/updated audio clip recording latency compensation. - Save/load all plugin's paths always as relative only to declared search paths (cf. View/Options.../Plugins/Paths) - Audio Insert pseudo-plugins now have an extra user supplied input parameter: Latency (frames). - Default Clip Fade-In/Out types (Linear, Quadratic, Cubic) are now settable as user preference options (cf. View/Options.../ /General/Clips). - Updated to CLAP v1.2.7 - Updated to VST3 SDK v3.8.0.build.66 1.5.9 2025-10-31 A Halloween'25 Release. - Main menu and toolbar Edit/Select Mode/Clip, Range, Rectangle and Automation actions are now self-toggled when triggered. - Slightly better positioning and centering when just clicking but not dragging the main and MIDI editor thumb-views around. - Mixer: temporarily hide/show either Audio or MIDI buses, from the respective Inputs and/or Outputs rack pane. - Add underlying platform name (eg. xcb, wayland) to Qt version string. 1.5.8 2025-08-29 A Late Summer'25 Release. - When selecting an Aux-Send pseudo-plugin, also highlight the respective target output bus mixer strip. - Mitigate and compensate for padding and start-delay/latency to (lib)RubberBand time-stretching and pitch-shifting processing. - Avoid warning when auto-saving an extracted archive/zip session. - Fixed all empty/void audio clips created when aborting an armed recording session; revisited. - Added new Track/Height/Minimize menu item. - Fixed initial Aux-Send audio bus I/O matrix functionality; also when input channel count is greater than output count. - Fixed WSOLA time-stretching crashing on greater-than-2-channels /stereo audio clips. - Fix misaligned LV2 Atoms. 1.5.7 2025-07-19 A Summer'25 Release. - Give some slack time to stabilize, when updating or changing audio bus channel counts, mitigating the probability to fault when unregistering old and registering new PipeWire/JACK ports in immediate succession. - Transport/Loop Set behavior slightly changed, now toggling the loop range if the edit-head/tail match current loop-start/end. - Introducing Aux-Send audio bus I/O matrix functionality. - Corrected greater-than-2-channel audio clip waveform drawing, while on certain zoom levels, an old bug lurking there since dawn, quite an example to the evil of too-early optimization ;) - Draw all audio clip peaks+rms waveforms slightly compressed, so that low-level signals have improved visibility. - Warn when saving any type of session into an extracted archive directory. 1.5.6 2025-06-06 An End-of-Spring'25 Release. - MIDI clip editor (aka. piano-roll): Transport/Step/Note/Backward, Forward, now plays the step-selected notes, provided send/Preview is on and playback is not rolling. - MIDI Tools: Normalize now finally fixed to an absolute Value, ie. without a Percent(age) factor applied. - MIDI Controller Observer: 'Hook' attribute is now on by default. - MIDI Controller Send pseudo-plugin introduced: accessible from plugins menu/Inserts/MIDI/Add Controller... 1.5.5 2025-05-21 A Mid-Spring'25 Release. - Fixed negative time values displayed in tool-tips, when dragging the rubber-band (lasso) to the left, over the absolute beginning of the timeline. - A warning is now issued when an audio self connection is made (eg. connecting an output bus (or insert send) directly into an input bus (or insert return). - Files item selection mode switching corrected. - Mitigate or postpone a 'too many open files' condition as much as possible. - Let muted clips show the "[Mute]" tag as the name label prefix, so that it's visible even on small clips. - Stepping up next development cycle (Qt >= 6.9) 1.5.4 2025-04-04 An Early Spring'25 Release. - Fixed non-zero clip offset conversion on tempo(BPM) time-scale changes. - MIDI clip step input and overdubbing now aggregated and fully undo/redo-able. - Allow MIDI step input to extend the clip length automatically; also avoid step input event duplicates (eg. playing chords in quick succession) leading to potential double-free segfault or crash. - Fixed MIDI track state when clips under record/overdubbing are simply removed. - Fixed all empty/void audio clips that are created when aborting an armed recording session. - MIDI clip editor (aka. piano-roll): simply allow a MIDI track to be a ghost of itself. - In addition to clips and markers, automation curves and tempo- -map nodes now also contribute to the total session length and status. - Fixed command line parsing (QCommandLineParser/Option) to not exiting the application with a segfault when showing help and version information. 1.5.3 2025-02-09 A Mid-Winter'29 Release. - MIDI clip editor (aka. piano-roll): mouse cursor pointer shape now follows the current edit/draw mode permanently. - Attempt to improve MIDI SPP accuracy by postponing MIDI Continue command in one 16th note at playback (re)start. - Specific to (lib)RubberBand time-stretching and pitch-shifting, formant preserve and finer (R3) engine processing are now added to audio clip/playback options. - Resume normal playback state if rolling when transport rewind or fast-forward is disengaged. - Custom Style Sheet files (*.qss): all url() paths are considered relative to style-sheet file location. - Enforce a fixed size when LV2 plug-in UI no-user-resize feature is explicitly requested.) 1.5.2 2025-01-17 A New-Year'25 Release. - Duplex MIDI Clock mode is not allowed anymore. - Immediate and consecutive plugin parameter changes are now merged into a single undo-able command, reflecting only the first value change in the series, dropping the previous old algorithm, which was dead wrong if not utterly defective. - Unique track names resolve to the first line only. - Help/Shortcuts... Search tool gets implemented; all changed MIDI controller shortcuts are reverted to their previous settings, when discarding or dismissing the dialog. - Fixed missing MIDI SPP in some cases. 1.5.1 2024-12-30 An(other) End-of-Year'24 Release. - Fixed/corrected an awfully bad MIDI metronome and clock timing, reminiscent from the higher resolution MIDI queueing (in place since v0.9.30, meaning a two year long disgrace :(). 1.5.0 2024-12-16 An End-of-Year'24 Release. - Clip/Cross Fade may now apply to all (multiple) selected clips. - Fixed the status-bar session and MIDI clip length BBT format, when in presence of multiple tempo or time-signature changes. - Introducing MIDI clip editor (aka. piano-roll) new Transport/Step /Note/Backward and Forward menu actions, to move the play-head to previous and next note events, respectively. - MIDI clip editor (aka. piano-roll) menu Edit/Select Mode/Edit On, Off and Draw actions are now self-toggled when triggered. - Plug-in presets menu: now sorted alphabetically. - When summoned from the menu, the View/Tempo Map-Markers... dialog positions itself to the current play-head location, instead of the absolute beginning of the timeline. - Introducing new application custom theming option: View/Options... /Display/Options/Custom/Icons theme (directory or folder). - After a shameful long time, adding a brand new audio clip via the Clip/New... dialog, is now finally fixed and functional. - Mixer: reduced track names up to first line break. - Double-click on slider for default value, replicating the behavior of middle-click. - Fixed bug: Aux-Send loses state when reordered in a strip. - Create/Add new bus below that which is used as source in View/Buses dialog. - Mitigate truncated bus names in Aux-Send bus dialog. - Fixed yet another old bug regarding the flush of all pending MIDI Note-Off events when playback stops, shuts-off or panics, especially relevant when playback is resumed anywhere but the absolute beginning of the timeline (and also after a first loop turn around). - Schedule an actual and complete refresh on main View/Refresh..., especially when changing a custom color theme palette on-the-fly. 1.4.0 2024-11-01 A Halloween'24 Release. - Improved color contrast on track and clip title labels, when given track foreground and background colors are too similar in lightness. - Clip/Split now also applies to multiple selected clips, on any other track than current, if the split point (play-head) is found within. - Clip Merge/Export... audio clips now taking the internal audio resolution (ie. 64 frames/period), independent to former JACK /Pipewire buffer-size/period. - New Clip Mute state property introduced. - New MIDI clip tool option: Normalize / Compress. - Prepping up next development cycle (Qt >= 6.8) 1.3.0 2024-10-04 An Early-Fall'24 Release. - Use timebase-aware JACK API for relocation; provide `bar_start_tick` in JACK Transport/Timebase BBT information. - Always reset the target bus when copying or moving an Aux-Send insert into an audio output bus. - Mixer: fixed dangling track removal after one of its buses has been previously deleted." - Fixed MIDI clip offset resizing, most especially when drag-moving the left-edge. - Audio Aux-Sends inserted on audio output buses are not restricted to later buses anymore; the only restriction now is that no cyclic or loop-back routes are allowed; audio output buses are now sorted internally in-place for correct processing order. - Update all Aux-Send inserts whenever their respective output bus gets renamed or deleted. - User preference option View/Options.../General/Options/Reverse keyboard modifiers role (Shift/Ctrl), now also applying to the play-head and/or edit-head/tail re-positioning in the timeline. - Plug-in name/title alias makes its debut. - Fixed a partial port-name filtering issue on the MIDI Connections widget. - Last selected automation curve color is now persistent and the default for all later automation curves. - Connections: connector line colors are now uniquely mapped on a (readable/output) client name basis. 1.2.0 2024-08-29 A Mid-Summer'24 Release. - Check whether a LV2 plug-in UI no-user-resize feature is being explicitly requested. - Auto-backward is now strictly to the play-head position playback was last started. - MIDI clip editor (aka. piano-roll) undo/redo command stack is now held at the MIDI clip instance level and thus shared and the same to all respective linked clips; as bonus, it also survives the main session undo/redo command stack as well. - MIDI clip editor (View/) Drum Mode option is now persistent on a clip basis and across sessions. - Fixed NSM session initialization disabling auto-save and the new session template features altogether. - Fixed crash when removing a MIDI track that is currently set as ghost to open MIDI clip editor(s), or it's been duplicated just recently. - New MIDI clip tools: Resize / Join, Split notes. - Session templates do not impose an audio sample-rate anymore, now being hopefully sample-rate agnostic; also, the edit-head/ tail cursors, loop-start/end and punch-in/out ranges and state are now simply ignored on loading and saving session templates. - MIDI tracks now show the respective audio output bus name, or dedicated port name whether applicable, under the 'Bus' column of main tracks left pane, above plugins list-box. - Mixer: also highlight both input and output bus strips, directly related to the current highlighted track. 1.1.1 2024-08-05 A Summer'24 Hotfix Release. - Fixed an incredibly severe bug, introduced very recently, that deletes all MIDI files belonging to active clips, when closing and discarding a modified session without saving. - Fixed MIDI clip recording when note-off events are missing or not transmitted. - Fixed a relatively old crash-bug that manifests on undoing several free-hand drawn events (Edit/Select Mode/Edit Draw) in the MIDI clip editor (aka. piano-roll). 1.1.0 2024-08-02 A Summer'24 Release. - Fixed MIDI clip step-input when play-head is located beyond or after the end of the active looping/cycle range. - Fixed whole clip selection, implied after dragging the lasso over the left and before the beginning of timeline. - Clip/Unlink is now a undo/redo-able command. - All sessions now honor their designated resolution property (PPQN aka. ticks-per-beat) not subordinated to former ALSA sequencer queue anymore, which runs on a higher resolution still. - Avoid removing MIDI Track/Channel tree items from the Files view (eg. via direct [Del] keyboard shortcut when in focus). - Fixed a probable old issue of spilled and duplicated shortcut entries (Help/Shortcuts...) between main tracks/timeline and MIDI clip editor (aka. piano-roll) windows. - Fixed fade-in/out curve types of clips when copy-pasted over the main tracks timeline. - Fixed general plugin scan/cache optimization in face of new plugins added and/or removed. - Fixed VST3 Plug-in main/active buses channel count inventory; also, on updating host parameters, only save and load custom modified parameter values from current state. - Fixed a missing display and port-name filtering issue, that was introduced recently to the MIDI Connections widget only. 1.0.0 2024-06-21 An Unthinkable Release. - Making up the unthinkable (aka. v1.0.0) - General plugin scan/cache optimization. - Improved legibility to all clip title labels (color contrast). - Save/load the correct order and labeling of audio/MIDI send/ /return pseudo-plugin inserts. - Fixed a display and port-name filtering issue that was present ever since on the Connections widget. 0.9.91 2024-05-03 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-12 A Spring'24 Release Candidate. - Prepping the unthinkable (aka. v1.0.0-rc1) - MIDI Controller mappings are now shown on floating tool-tips. - Custom color themes are now file based (*.conf); legacy still preserved ntl. - Add default GM, GS and XG standard instruments definition file. - Old generic "Portuguese" translation (pt) has been corrected to the more proper "Portuguese (Brazil)" locale (pt_BR). - Up and Down arrow-keys may now be used to change event values on the MIDI clip editor current selection (eg. note velocities). - MIDI clip editor now featuring lollipops for all kind of candy event values ;). - Make the minimum width of events on the MIDI clip editor larger, depending on screen resolution and horizontal zoom setting. - Avoid issuing equivalent MIDI track channel volume and panning via GM standard controllers (CC#7 and CC#10 resp.) to mitigate recursive or positive feedback loops. - Refined mouse-wheel control step size on the sliders of mixer strips and generic/stock plugin editor dialogs. - Fixed the build checks on whether to use old or newer style of LV2 include headers. - Introducing colored strips on the time ruler headers for loop and punch recording ranges. - Fixed an off-by-one(-pixel) mispositioning of selected events, while on the MIDI clip editor (aka piano-roll). 0.9.39 2024-01-27 A Winter'24 Release. - Added build checks on whether to use old or newer style of LV2 include headers. - Introducing new Transport/Step/Backward and Forward menu actions, to move the play-head backward and forward, in bar/beat/fraction (snap-per-beat) steps, respectively. - Introducing View/Options.../Display/Custom/Style sheet (*.qss) application theming option. - Improved MIDI clip editor centering to current mouse pointer position in main timeline (tracks-view); on both horizontal and vertical axes. - LV2 Plug-in Control Input Port-change request extension feature support added. - Fixed an old one-off plug-in parameter change command aliasing (undo/redo). - Updated copyright headers into the New Year (2024). 0.9.38 2023-12-21 An End-of-Year'23 Release. - Track Properties (dialog): auto-background color option added. - Mitigate undifferentiated disablement of up/down arrow buttons, on tempo and time-signature entry spin-boxes. - MIDI clip editors (aka. piano-roll) are now raised and centered to current mouse pointer position in main timeline (tracks-view). - Remember last Marker color setting in Tempo Map/Markers dialog. - A colored ribbon is now featured on tracks-list Bus column and mixer-strip headers; ribbon colors are designated for Audio and MIDI tracks or buses, given by the respective '10dB' and 'Over' meter colors. - Buses'In'/'Out' suffixes are back on mixer-strip header tooltips. 0.9.37 2023-12-05 An End-of-Autumn'23 Release. - Fixed an ancient hack on session initialization to wait to JACK service start up, which hang up while on pipewire-jack >= 1.0.0. - Add default audio metronome sample files. - Fixed automation curve rescaling across multiple, more than just one or two, tempo-map node changes. - New MIDI Tool Resize / Legato duration option added. - MIDI Clip Editor (aka. piano-roll) thumb-view now taking tempo-map changes into account. - Audio metronome is enabled only when both the bar and beat sample files are provided. 0.9.36 2023-11-10 An Autumn'23 Release. - Multiple audio and/or MIDI files may now appear as arguments on the command line and get immediately imported into a new session; also, any audio or MIDI file may now be promptly imported into a new session from the main File/Open... dialog. - New MIDI clip tool option added: Rescale/Invert. - Introducing brand new MIDI clip tool: Tempo ramp. - Disable or better hide the Add Tracks option on the MIDI tracks export dialog. - Fixed a yearlong bug in MIDI file import/export of tempo, time and key signatures and markers, to severely wrong locations. - Enable Aux-Send inserts on output buses, but list possible target buses only; audio output buses are now listed as possible targets iif they follow or succeed the source one (in internal processing order). - Fixed MIDI tracks latency compensation, now working as expected. - Show current/total plugins latency on the Track properties dialog, - Fixed MIDI Step-input for non-zero offset clips. 0.9.35 2023-09-14 An End-of-Summer'23 Release. - MIDI Step-input is now finally featured on the MIDI clip editor (aka. piano-roll), provided Clip/Record is on and playback is not rolling; current snap-to-beat applies; starts/resets to play-head; Edit/Insert/Step to advance a single step/rest. - Notes keyed in the MIDI clip editor's virtual piano keyboard may now be recorded, especially while "overdubbing". - The official VST3 plug-in SDK is now included in the source tree as Git submodules. - Fixed a rounding error on current BBT information passed to plugins and to JACK transport/timebase. - When on Track/Auto Deactivate mode, plugins now show a dull, dimmed lit, green (fake-)LED when in auto-deactivated state. - Attempt to actually (de)activate plugins once on (de)instantiation. - Preppings to next development cycle (Qt >= 6.6) 0.9.34 2023-06-08 A Spring'23 Release. - Fixed the snap-to-beat of new notes entry on the MIDI clip editor (aka. piano-roll) due on time signature changes. - Start JACK transport rolling only when metronome Count-in ends. - Fixed an allegedly old and incorrect 0dBfs notch position on MIDI track/buses audio meter sidekicks. - Send/return and Aux-Send inserts now show the proper name and the target output bus name on the properties editor title respectively. - Fixed a zero-day blunder that was keeping the real-time process cycle from having the uniform block-size of 64 frames per period; (thus, probably ineffective since v0.9.30). - Multiple MIDI clip tools may now be applied simultaneously, in a single shot, in the following priority order: quantize, transpose, normalize, resize, rescale and timeshift. - Plugin inventory scan now slightly optimized to an allegedly lesser aggressive cache-invalidation algorithm. - Do not send/Preview notes on the MIDI clip editor (aka. piano-roll) when playback is currently rolling. - Send all pending MIDI Note-Off events when playback stops/shuts-off (and/or the Panic! button is hit). - Fixed the internal MIDI file player queue to the highest resolution possible (PPQN aka ticks-per-beat). 0.9.33 2023-03-27 An Early-Spring'23 Hotfix Release. - Fixed MIDI recording delay and recorded clip lengths when metronome Count-in is in effect for recording. 0.9.32 2023-03-25 An Early-Spring'23 Release. - Downgraded JACK timebase BBT information to nominal PPQN resolution (aka. ticks-per-beat). - Make sure all previously saved connections to identical ALSA MIDI hardware devices are now discriminated and properly restored, even though the target deviceds have the very same name. - Fix drag'n'drop in drum mode MIDI clip editor. - Quick hack to get latency compensation when recording. - Introducing count-in to audio and MIDI metronomes: View/Options... /Audio|MIDI/Metronome/Count-in; Transport/Count-in. - Corrected MIDI metronome bar/beat note durations. - Give an early reponse upon opening any NSM session. 0.9.31 2023-01-26 A Winter'23 Release. - Fixed a off-by-one rounding error on MIDI clip offset and lengths that were leaving some clips unlinked on load. - LXVST_PATH environment variable now accrues to VST_PATH for Linux- native VST2 plug-ins search path and not taking over in precedence anymore. - Fixed an old mistake on custom aliased CLAP and VST3 plugin paths. 0.9.30 2022-12-30 An End-of-Year'22 Release. - Plugin latency/delay compensation now in effect immediately after changing track option (cf. Track/Properties... /Plugins/Latency compensation). - Shade-off regions not-in-view from the thumb-views. - Improved MIDI queue time drift correction resilience and stability against in-flight tempo changes. - The main real-time process cycle now runs on uniform block-sizes, in strides of 64 frames per period, meaning a higher resolution automation, independent of buffer-size. - The internal main MIDI engine gets its ALSA sequencer queue to a higher resolution (PPQN aka ticks-per-beat) and not subordinated to the current session's anymore. - Although being deprecated to use, JACK Session support is hopefuly fixed, once again. - Better discriminate CLAP Plug-in specific note events and strict MIDI dialect event processing. 0.9.29 2022-10-05 An Early-Autumn'22 Release. - Capture same time(stamp) note-off tracking and postponing is now on trial, hopefully mitigating a legato issue, reported to MIDI wind instruments (EWI). - Fixed an old window parenting (aka z-level) issue, related to the Add Track and Track Properties dialogs and whether called from the main or mixer menus. - Fixed the out-of-process plugin scanner terminating too soon and sometimes miss some very last results. - Fixed another old bug in MIDI note-off messages not being sent while playing after a loop region. - Fixed a pretty ancient bug in the VST(2.x) plug-in program names inventory, present when building to the VeSTige header (which is still the default). 0.9.28 2022-09-03 A Late-Summer'22 Release. - Complete overhaul of the current host time(base)/BBT information delivered to plug-in types that matter: VST, VST3, CLAP and LV2. - Improved key-signature editing and display on Tempo Map / Markers (time-scale) management dialog. - Fixed plugin selection, when creating and switching initial track type, from audio to MIDI and vice-versa. - Fixed typos and updated some old MIDI GM2 Controller names. - Add current system user-name to the singleton/unique application instance identifier (when explicitly opted in at build configure time). 0.9.27 2022-07-07 An Early-Summer'22 Release. - CLAP plug-in host support introduced. - Reviewed LV2 plug-in UI Touch feature/interface. - Auto-unlink MIDI clips when pasted/placed with Ctrl+click/Enter. - Fixed LV2 plug-ins UI X11 (native) initial size. - Fixed implicit deactivation when a plugin is removed from chain. - Fixed audio clip export, normalize and tempo-adjust when audio file number of channels is disparately greater than respective track's output bus count. - Fixed one killer lurking in MIDI Controller... modeless dialog instantiation. - Fixed non-effective automation curve node editing. - Track/Export Tracks... dialog ranges are not capped to current session-end anymore. - Fixed MIDI clip editor vertical-zooming when using the [Ctrl+] mouse-wheel. - Set auto-backward play-head location also when clicking on main track-view header/time-ruler and on thumb-view. - Fixed LV2 plug-in buffer-size initialization, esp. affecting the ZynReverb LV2 playback. - Export Tracks dialog last range selection is now remembered. - Fixed the out-of-process plugin scanner path resolution on some self-container(ized) formats eg. AppImage and possibly Flatpak. 0.9.26 2022-04-09 A Spring'22 Release. - Main application icon is now presented in scalable format (SVG). - Have even more tolerance to JACK buffer-size changes, prompting for a complete session reload, only when exceeding the double of the previous/current size. - Added an additional status-bar label to show the session current buffer-size (in frames per period). - Migrated command line parsing to QCommandLineParser/Option (Qt >= 5.2) - Make last recorded clip current and suitable target for immediate loop recording takes switch or navigation. - Number of takes is now shown on clip titles and tooltips. - Fixed in-flight transport mode changes. - Fixed translations path to be relative to application runtime. 0.9.25 2022-01-09 A Winter'22 Release. - Hopefully fixed an old MIDI off-timing bug noticeable only when exporting (Track/Export Tracks/Audio...) on large buffer-sizes (>= 2K frames/period). - Clip/File Loop Set menu command is now a toggle. - Fixed problem with punch-in/out and loop-recording being lost when stopping the play-head right after and between the loop-start and punch-in points, even though at least one cycle or take is through. - Dropped autotools (autoconf, automake, etc.) build system. - A more verbose warning question is issued, on whether to continue saving to an existing zip/archive directory and accept to replace and erase all its current data in the future. - Fixed potential crash on session close or application exit, when some plugins have been removed. 0.9.24 2021-10-16 An Autumn'21 Release. - A new option has been added to reset/resend all MIDI track/channel and buses controllers on playback start (cf. View/Options.../MIDI/ Playback/Reset all controllers on playback start). - Whenever possible, avoid suggesting Save As... to an extracted archive/zip directory. - Fixed an old nasty mistake when renaming session names and then saving into an archive/zip bundle file (.qtz). - Fixed Mixer multi-row automatic layout consistency, when adding new or removing existing tracks or buses. 0.9.23 2021-07-10 An Early-Summer'21 Release. - Dropped the 'Activate' option on the plug-in Selection dialog, now being as always on by default. - Have some tolerance for JACK buffer-size changes, only prompting to a complete session reload, if increasing in double the initial period size. - Introducing plug-in blacklisting, on user discretion (in View /Options.../Plugins/Blacklist) and on inventory scan (crashed plug-ins are now automatically blacklisted). - Added special support for LV2 UI GTK2 plugins based on Gtkmm 2.4 framework. - All builds default to Qt6 (Qt >= 6.1) where available. - CMake is now the official build system. 0.9.22 2021-05-14 A Spring'21 Release. - Fixed one terribly old and overlooked mistake that was preventing MIDI tracks volume and panning automation to take effect on audio export. - All packaging builds switching to CMake. 0.9.21 2021-03-18 An End-of-Winter'21 Release. - Ignore snap while ALT key is pressed, on the main track-view and the MIDI clip editors (aka. piano-roll). - Fixed a FTBFS when native LV2 UI GTK2 support is disabled. - Fix IPlugView leaks for VST3 plugins. 0.9.20 2021-02-12 A Winter'21 Release. - Fixed and improved automation curve recording, whenever playback is rolling (and also when isn't:)). - Fixed parsing/loading of large session bundle archive/zip files (.qtz > 2GB). - Fixed LV2 plug-in UI X11 (native) resize. - Make NSM state file names independent to session display names, keeping backward compatibility for old sessions. - Exiting, quitting or closing the main window while under NSM, now promptly asks whether to save, discard or cancel as usual. - Re-improved Mixer multi-row layout. - Fix incorrect destruction order for VST3 modules. 0.9.19 2020-12-20 A Winter'20 Release. - Session directory auto-name option added to the session properties dialog, as convenience. - Loading and saving a LV2 plugin's state has been vastly improved. IMPORTANT CAVEAT: From this moment onwards, when loading any newer saved sessions into older versions of the program, all LV2 plugins won't get their state restored correctly. - Track colors saturation introduced as yet another eye-candy option (cf. View/Options.../Display/Track color saturation) - Fixed VST3 number of channels query/report. - Fixed immediate crash when loading untitled or unnamed Instrument Definitions files (*.ins): base file-name is now taken as default instrument definition name or title. - Tempo (BPM) entry may now be specified with arbitrary precision, to at most 3 decimal positions in fractional part, while integer whole values are displayed with no decimal point. - Added option to keep MIDI clip editor windows (aka. piano-roll) always on top of the main window (cf. View/Options.../General/ Keep editor windows always on top). - MIDI clip editor status-bar labels are not stretched to whole text size anymore, most specially for the current file complete path. 0.9.18 2020-10-30 A Fall'20 Release. - When under NSM, all top-level windows, main, mixer and connections, will always start hidden. - Plugin editors (GUIs) that are currently open on a track are now brought up as top-level windows immediately when a track is made current or highlighted (and Track / Auto Monitor is in effect). - MIDI clip editor mouse hovering effect extended to whole current note line on main view (piano-roll eye-candy++); also, the white keys on MIDI clip editor's virtual piano keyboard, are now fully highlighted. - Plugin search paths (cf. View/Options.../Plugins/Paths) now showing all the default and actual existing paths, instead of a blank list. - Undimmed octave divider lines on the piano-roll. - Fixed potential crash on changing audio output buses channel count. - Note names display (inside note rectangles) are now an option on the MIDI clip editor (aka. piano-roll; menu View > Note Names). 0.9.17 2020-09-15 An End-of-Summer'20 Release. - Early fixing to build for Qt >= 6.0.0 and the C++17 standard. - Fixed crash when changing an auto-monitored audio track's number of channels due on switching its audio output bus. - Avoid a complete track re-open when changing properties, unless either input or output buses are changed. - Fixed custom track icon selection when none is currently set. 0.9.16 2020-08-07 A Summer'20 Release. - Experimental / High resolution plugin automation (14-bit) options is now also removed from View > Options... > Plugins dialog. - Audio Clip / Export... now takes into account custom fixed gain property (shamefully missing for the whole last decade to date). - Finally indulged on dummy LV2 plug-in CVPort support, just to avoid certain immediate and sudden crashes when inserting those kind of 'non-functional' plugins accidentally. - Bring the Track Export dialog down to Clip Merge/Export as well, for optional file type and format selection. - When enabled, do auto-save upon adding or inserting a new plugin. - Make sure LV2 plug-in UI GTK2 and X11 native support is selected first by default on top to libsuil. - Both out-of-process plugin inventory scan and LV2 Dynamic manifest options have been removed from View > Options... > Plugins dialog. - Fixed deprecated stuff on an early preparation for Qt6. 0.9.15 2020-06-27 An Early-Summer'20 Release. - Fixed MIDI tracks export that were missing the end-of-export tail parameter and bailing out always with default SMF format anyway. - Fixed VST3 component/controller inter-connection. - LV2 Atom/Port-event host notification support has been retouched, but still unofficial though. - Fixed VST3 audio-processor initialization/activation when a plugin has no inputs or outputs present. - Let main window pseudo-asynchronous stabilization re. menus, tools and status bars, just faster and immediate. - Fixed MIDI track monitor reinstantiation and reset. 0.9.14 2020-05-07 A Mid-Spring'20 Release. - Export file type, format and quality are now specific options on the Track/Export Tracks.../Audio, MIDI dialogs. - LV2 plug-in UI GTK2 and X11 in Qt5 host native support in addition and alternative to libsuil. - Generic plug-in/Properties... dialog now showing each parameter/property automation status on a skeuomorphic aka. fake and tiny LED ;) - LV2 Plug-in Patch parameter/properties automation and MIDI Controller assignment/learn are now a possibility. - LV2 Atom/Port-event host notification support has been implemented (unofficial). - Fix clean-up of any recording leftovers. - JACK Transport latency is now taken into account for recording latency compensation. - Attempt to force correct audio clip offsets due on recording latency compensation are not quantized to MIDI metronomic time-scale anymore. - LV2 Plug-in MIDNAM support introduced. - Use Shift or Ctrl keyboard modifiers with the mouse-wheel to change any Direct Access plug-in parameters. 0.9.13 2020-03-28 A Spring'20 Release. - All meters background color are now customize-able (cf. View/Options.../Display/Meters, color level "Back"). - Automatic mixer grid layout (multi-row) is now in effect permanently--being an option no more. - Always show plugins and meters on track list/left pane as permanent standard now. - LV2 UI Request-value feature/interface support has been implemented. - Audio output monitoring meters are now shown/hidden auto-magically on MIDI tracks and/or buses--no need for some user preference option anymore (ie. View/ Options.../Plugins/Instruments/Show audio output monitoring meters, is now gone). - Default track height has been slightly increased. - Track / Duplicate Track... now also takes a MIDI track's audio meters setting into account. - VST3 plug-in support introduced. - Fixed first bar/measure position drawing on the time-scale/grid across time-signature changes on the MIDI clip editor (aka. piano-roll). - Plugins and meters on track list/left pane, are now being set on as default--maybe going stapled in some near future ;) - Make man page compression reproducible (after request by Jelle van der Waa, while on the Vee-Ones, thanks). - Avoid resetting top or left position when zooming with mouse pointer is in main tracks or MIDI clip editor (piano-roll) views. - Make libaubio a build dependency on Debian/Ubuntu; also fix cross-build check to sizeof(float). - Bumped copyright headers into the New Year (2020). 0.9.12 2019-12-28 The Winter'19 Release. - Basic key-signature has been added to tempo, time-signature and location markers map. - MIDI Clip editor (aka. piano-roll) horizontal and vertical splitter sizes, widths and heights resp. are now preserved as user preferences and also to session state. - Second attempt to fix the yet non-official though CMake build configuration. 0.9.11 2019-11-09 The Mauerfall'30 Release. - MIDI Instrument and patch, bank and program names are now correctly updated on their respective track -list (left pane) columns. - Avoid copying/replicating dirty MIDI clip files, for yet untitled/scratch sessions. - Transport/Backward commands now honoring edit-tail, loop-end and punch-out points, when playback is not rolling. - A session name (sub-)directory is now suggested on every new session properties dialog. - Avoid adding any extraneous clip replica when Ctrl +dragging on either of its edges. - 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, thanks). - Fixed a potential crash-effect in switching MIDI output buses on tracks that are set to show audio output monitoring meters. 0.9.10 2019-10-12 Autumn'19 Beta. - Fixed initial session tempo override when importing a standard MIDI file. - An alternate time-signature/meter option is being served to the MIDI clip editor (aka. piano-roll) and allowing for some poly-rythm/meter scenarios on a per MIDI clip basis. - Fixed MIDI "overdub" recording on offset clips. - MIDI bank and program settings now propagating to all MIDI track's clips resp. - Fixed MIDI file format default setting other than SMF Format 0. - Escape key may now be used to reset time and tempo /time-sgnature spin-box controls. - Play-head time and tempo/time-signature controls are now featured in MIDI clip editor toolbars (aka. piano-roll); time display format is also separated from the tracks main application view and defaults to BBT as being most convenient. - All items in the MIDI clip editor's event list are now enabled, selectable and editable, no matter the filter settings for the event views. - Added alternate yet non-official CMake build option. - Improved MIDI clip editor (aka. piano-roll) position and size persistence across session state. - Fix HiDPI display screen effective support (Qt >= 5.6). - Mixer, Connections and MIDI clip editor top-level windows shall have no parent, unless when set as always-on-top tool windows. (REGRESSION) - Make sure compiler flags comply to c++11 as standard. 0.9.9 2019-07-24 Summer'19 Beta. - Fixed editing and display of 'Pgm Change' events on the MIDI clip editor (aka. piano-roll). - Introducing tempo/beat-detection support on the Clip / Tempo Adjust... dialog (provided libaubio >= 0.4.1 is available); and now also featured with some rough visual clues ;). - Updated for the newer Qt5 development tools (>= 5.13). - Imply asking for a brand new filename (ie. Save As...) whenever the session file original sample-rate differs from the current audio device engine (ie. JACK). - Configure updated to check for qtchooser availability. - Fix MIDI through for LV2 plug-ins that have no MIDI output event/atom ports. 0.9.8 2019-05-31 Spring'19 Beta. - Plugin-lists and respective plugins state may now be exported and/or imported as XML files. - When in Drum Mode, Key and Scale are meaningless and thus functionally disabled from the MIDI clip editor (aka. piano-roll). - MIDI clip editor's View > Ghost Track menu option is now finally a reality: show any existing MIDI track and its respective clips in the background as dimmed, semi-transparent aka. ghost events. - Minor update to Debian packaging control file. - Make sure partially selected clips are reset to whole when Shift/Ctrl keyboard modifiers are in effect, to prevent extraneous clip splits or cutaways afterwards. 0.9.7 2019-04-16 Spring-Break'19 Release. - Re-defined all main application UNIX signal handling. - Fixed possible crash in drawing clips while rare loop-recording/takes situations. - Main window stabilizing is now kind of asynchronous re. menus, tools and status bars. - MIDI Controller's Latch mode attribute on tracks and plugins are now properly saved/loaded as meant to be. 0.9.6 2019-03-20 Pre-LAC2019 Release Frenzy. - Fixed LV2 UI Touch feature/interface implementation. - Plugin latency/delay compensation is now introduced as an option to tracks only (cf. Track/Properties... /Latency compensation). - Refactored all singleton/unique application instance setup logic away from X11/Xcb hackery. - A bit lesser of a naive file-name numbering is now in place to avoid as much as possible, the piling up of internal curve/automation files when saving existing sessions. - Maybe just informational but the original MIDI file format is now correctly reported across ref-counted linked MIDI clips. 0.9.5 2019-02-14 Valentines'19 Hotfix. - HiDPI display screen support (Qt >= 5.6; patch by Hubert Figuiere, thanks). - Fixed for DSSI plug-ins (eg. fluidsynth-dssi) loss of configuration state: clear internal config/state keys on release virtual method. (REGRESSION) - Fixed for NSM (and JACK) sessions not saving the correct file references/symlinks of clips that are recorded or created during the initial and scratch session. 0.9.4 2019-02-07 Winter'19 Beta. - Drag-moving and copy-pasting existing clips, while over the main track-view, now shows the respective (audio wave-shapes and MIDI piano-rolls) graphical representations, as much as possible. - For good and bad, session name changes now trickle down to respective audio/MIDI file names as well. - Audio output monitoring meters may now be shown on MIDI tracks and buses as a default user preference option (View/ Options.../Plugins/Instruments/Show audio output monitoring meters) and also in plugin list context sub-menu (Audio/Meters). - Custom color (palette) themes can be exported to and imported from external files. - LV2 plug-in UI GTK2 and X11 in Qt5 host native support are now enabled on configure by default. (REGRESSION) - Fixed minimum input value as 10% (was 1%) for audio clip time-stretching in the Clip / Edit... dialog. 0.9.3 2018-12-07 End of Autumn'18 Beta. - Auto-backward now skips the end-of-session location. - Audio clip time-stretching and pitch-shifting limits are now 10-fold in either direction. - Custom color (palette) theme editor introduced; color (palette) theme changes are now effective immediately, except on default. - Old deprecated Qt4 build support is no more. - Recover audio and MIDI dedicated port connections when changing any of the Metronome, Player and/or Control option settings. - Fix MIDI track (and bus) bank/program reset to none. - Anti-glitch micro-fade-in is disabled on audio clips with zero offset. - Audio and MIDI file players also stopped on Transport / Panic command. - LV2 plug-in UI GTK2 and X11 in Qt5 host native support are now disabled on default configure. - Get rid of symlink duplicates on the default plugin search paths. - According to Debian policy and guidelines, the out-of process plugin scanner (qtractor_plugin_scan) is now installed to $LIBDIR/qtractor. 0.9.2 2018-09-09 Summer'18 Beta. - AppData/AppStream metadata is now settled under an all permisssive license (FSFAP); also updated to be the most compliant with latest freedesktop.org specification and recommendation. - Fix build for Qt >= 5.11.0 (by David Geiger, thanks); also for some g++ >= 8.1.1 warnings and quietness. 0.9.1 2018-05-29 Pre-LAC2018 Release Frenzy. - Displaying MIDI note(on) events as diamonds instead of simple rectangles (aka. Drum Mode) is now being introduced as an optional MIDI track property (Drums) and as a MIDI clip editor (piano-roll) visual option (cf. View/Drum Mode). - Extended multi-selection is now supported on all the Connections client/port lists, allowing for multiple (dis)connections at once. - Added LV2 UI sample-rate option support. - Always reset all internal dedicated MIDI controllers, eg. MIDI track volume (CC#7) and panning (CC#10), on Transport/Panic and after rendering export to aud1io (ie. Track/Export Tracks/Audio...) as needed to reset MIDI instrument plugins to nominal session state. - Fix, detect and preserve MIDI Bank-select method across MIDI track/clips editing operations. - Fixed MIDI track and clip note min/max display range while recording and also when duplicating tracks. - Added "All files (*.*)" filter to every file requestor dialog, wherever missing. - The tiny zoom-magnifier icons have been revamped. 0.9.0 2018-03-22 Early Spring'18 Beta. - New View/Options.../Plugins/Experimental/Show plugins on track list/left pane option is in effect on tracks that are tall enough in height for their plugins list to fit on the Bus column. - Fixed a day-zero bug over the MIDI Insert (Send/Return) pseudo-plugin, which was duplicating MIDI events onto the next LV2 plugin in chain, causing strange hanging notes, mutes, retriggerings and what not. - MIDI track and clip note min/max range display now fixed. - MIDI Program Change events (PC) now have their proper program number as parameter, instead of value, on the internal MIDI event representation. - Merging MIDI clips while on SMF Format 0 has been fixed: was merging always onto the same MIDI channel (2), most often the wrong one, resulting in an empty or blank clip. - When importing from standard MIDI files (SMF), set track names from Mtrk TRACKNAME meta-events instead of filename. - Avoid asking to save as to existing or just newly created clip file-names, whenever possible. - Disable singleton/unique application instance setup logic when the display server platform is not X11. - Whether to use native file browser/requester dialogs is now an effective option when launching under NSM session management (was once disabled initially). - Content files are now saved as symlinks when saving to JACK and/or NSM session directories/folders. - Trying to get CC14 MSB+LSB (course+fine) running status on, no matter whether each pairing event are under 200ms apart. - Possible VST plug-in GUI reparenting hack/fix on Qt5/Xcb. 0.8.6 2018-01-30 Winter'18 Beta. - Added LV2_UI_updateRate option support. - Added brand new option to deactivate plugins only if they can produce sound cf. main menu Track/Auto Deactivate (by Andreas Müller aka. schnitzeltony, thanks). - Workaround native file dialogs hang up by setting parent widget to NULL; it should be noted that dialogs now get an own entry in the task-bar (also by Andreas Müller aka. schnitzeltony, thanks). - Added ARM NEON acceleration support (by Andreas Müller aka. schnitzeltony, thanks). - Track count "limit" and a "Delta" mode flag, for momentary and encoded controllers support, have been added to MIDI Controllers generic mapping (cf. View/Controllers...; after an original pull-request by Holger Dehnhardt, thanks). - A little hardening on the configure (autoconf) macro side. - Pinned current/hi-lighted track dangling after removal. - An anti-flooding timer is now in place in MIDI Controller assignment (aka. MIDI learn) dialog. - Add MMC Track input monitor support. - New user preference option: View/Options.../General/Options/Reverse keyboard modifiers role (Shift/Ctrl), applied to main transport re-positioning commands: Transport/Backward, Forward, etc. - VST Time/Transport information is now also updated as on playing when in audio export aka. freewheeling mode. - LXVST_PATH environment variable now takes precedence over VST_PATH as Linux-native VST plug-ins search path. - MIDI Controllers mapped to non-toggling shortcuts now work as one-shot triggers, independent of MIDI event value. 0.8.5 2017-12-04 Autumn'17 Beta. - Audio clip gain and panning properties are now taken into consideration when hash-linking (aka. ref-counting) their back-end buffers. - New out-of-process plug-in inventory scan and cache option, replacing the old (aka. dummy) VST plug-in scan option and extending its function to all other plug-in types: LADSPA, DSSI and also LV2 (cache only). - A File System browser and tree-view is finally integrated as a dockable-widget on the main application window (cf. main menu View / Window / File System). - Drag-and-dropping of session, audio and MIDI files over the main track-list (left pane) is now possible, allowing for yet another quick means to open a new session or add new tracks to the current session. - MIDI input/capture time-stamping has been fixed as much to avoid missing inbound events, when play-head is near the loop-end point and the loop-start is set below the absolute first half-a-second (<0.5sec). - LV2 Time/Transport speed information is now set on rolling when in audio export aka. freewheeling mode. - Added *.SF3 to soundfont instrument files filter, on View > Instruments... > Import... file dialog. - A brand new View/Options.../Display/Meters/Show meters on track list/left pane option has been added. 0.8.4 2017-09-20 End of Summer'17 Beta. - Assigned MIDI Controllers to plug-in's Activate switch are now finally saved and (re)loaded properly across sessions. - Audio clip panning option property is now being introduced. - Out-of-process (aka. dummy) VST plug-in inventory scanning now restarts automatically and resumes processing in case of a premature exit/crash; VST plug-in inventory scan/cache persistency is now in place. - Desktop entry specification file is now finally independent from build/configure template chains. - Updated target path for freedesktop.org's AppStream metainfo file (formerly AppData). - Changing the View/Options.../Display/Custom/Style theme takes effect immediately unless it's back to "(default)". - Slightly slower but better approximation to IEEE 32bit floating point cubic root ie. cbrtf(). 0.8.3 2017-06-30 Stickiest Tauon Beta. - Make sure any just recorded clip filename is not reused while over the same track and session. (CRITICAL) - LV2 Plug-in worker/schedule interface ring-buffer sizes have been increased to 4KB. - Fixed track-name auto-incremental numbering suffix when modifying any other track property. - WSOLA vs. (lib)Rubberband time-stretching options are now individualized on a per audio clip basis. - Long overdue, some brand new and fundamental icons revamp. - Fixed a tempo-map node add/update/remove rescaling with regard to clip-lengths and automation/curve undo/redo. - Fixed a potential Activate automation/curve index clash, or aliasing, for any plug-ins that change upstream their parameter count or index order, on sessions saved with the old plug-in versions and vice-versa. 0.8.2 2017-05-10 Stickier Tauon Beta. - Track-name uniqueness is now being enforced, by adding an auto-incremental number suffix whenever necessary. - Attempt to raise an internal transient file-name registry to prevent automation/curve files to proliferate across several session load/save (re)cycles. - Track-height resizing now meets immediate visual feedback. - A brand new user preference global option is now available: View/Options.../Plugins/Editor/Select plug-in's editor (GUI) if more than one is available. - More gradient eye-candy on main track-view and piano-roll canvases, now showing left and right edge fake-shadows. - Fixed the time entry spin-boxes when changing time offset or length fields in BBT time format that goes across any tempo/time-signature change nodes. - French (fr) translation update (by Olivier Humbert, thanks). 0.8.1 2017-02-17 Sticky Tauon Beta. - The View/Options.../Display/Dialogs/Use native dialogs option is now set initially off by default. - All tempo and time-signature labels are now displayed with one decimal digit, as it was in mostly everywhere else but the time ruler/scale headers. - JACK transport tempo and time-signature changes are now accepted, even though playback is not currently rolling; also, changing (JACK) Timebase master setting (cf.View/ Options.../General/Transport/Timebase) will take effect immediately, not needing nor warning for session restart anymore. - Track/Navigate/Next and Previous menu commands, finally fixed to wrap around the current track list. - Current session (JACK) transport mode option switching is now being made accessible, from the main menu and drop-down toolbar buttons, as well as user configurable PC-keyboard and/or MIDI controller shortcuts (cf. Transport/Mode/None, Slave, Master, Full). - Fixed some auto-backward play-head position flip-flopping, when opening a new session while the previous was still on rolling/playing state, hopefully. - Added French man page (by Olivier Humbert, thanks). - MIDI clip changes are now saved unconditionally whenever the editor (piano-roll) is closed or not currently visible. - Audio clip peak/waveform files re-generation performance, scalability and resilience have been slightly improved. - Some sanitary checks have been added to audio clip peak/ waveform re-generation routine, as much to avoid empty, blank, zero or negative-width faulty renderings. - Do not reset the Files tree-view widgets anymore, when leaving any drag-and-drop operation (annoyingly, all groups and sub-groups were being closed without appeal). - Make builds reproducible byte for byte, by getting rid of the configure build date and time stamps. 0.8.0 2016-11-21 Snobbiest Graviton Beta. - MIDI clip tools redo/undo processing refactored as much to avoid replication over multiple hash-linked clips; MIDI clip editor's floating selection/anchor event stability has been also improved, in regard to MIDI tools processing range. - Auto-backward play-head location, when playback was last started, is now shown on main track-view, as a momentary dark-red vertical line marker. - LV2 plug-in parameter optimization: stuff consecutive series of plug-in's parameter value changes, as much as possible into one single undo/redo command. - LV2_STATE__StateChanged is now recognized as a regular atom notification event and raising the current session dirty flag, as normal behavior. - Adjusting clip selection edges is now possible and honored while on the the main track-view canvas. - Audio peak file caching and rendering, as far as audio clip wave-forms are concerned, have been refactored and optimized a couple of notches higher, on the ephemeral and rather marginal throughput front ;). - Fixed a potential crash on the singleton/unique application instance setup. - Edit/Select Mode tool-buttons moved into single drop-down tool-button on the main and MIDI editor's tool-bar. - Do not reset the current clip selection when updating the main track-view extents eg. while zooming in or out. - Automation curve node editing auto-smoothing revisited; also fixed input MIDI RPN/NRPN running status processing, which was crippling some plug-in automation curve nodes, when saved in high-resolution 14-bit mode. - Fixed the visual play-head position (vertical red line) while zooming in or out horizontally. - Almost complete overhaul on the configure script command line options, wrt. installation directories specification, eg. --prefix, --bindir, --libdir, --datadir and --mandir. - LV2 Plug-in worker/schedule fix: make request/response ring-buffer writes in one go, hopefully atomic (suggested patch by Stefan Westerfeld, while on SpectMorph, thanks). 0.7.9 2016-09-21 Snobbier Graviton Beta - JACK buffer-size change handling has been deeply improved, now doing an immediate session restart, while preserving all external connections as much as possible. - Introducing an audio and MIDI metronome anticipatory offset, kind of latency compensation, to respective option settings cf. View/Options.../Audio, MIDI/Metronome/Offset (latency). - Fixed LADSPA plug-in preset switching, incidentally broken as NOP, ever since late Haziest Photon's crash-landed. - MIDI Track/Instrument cascading menus have been found empty broken on Qt5 builds, now fixed. - MIDI RPN/NRPN running status and RPN NULL reset command are now supported (input only). - Fixed a sure immediate crash on removing audio buses that are current targets of any active Aux-Send inserts. - Fixed yet another old bummer that was reaping off assigned MIDI controllers on existing track's gain/volume or panning controls, when adding any single new track. - Fixed missing feedback on MIDI controllers assigned to any of monitor, record, mute and solo track/bus state buttons. - Eye-candy warning: the current clip, not necessarily the one currently selected, is now highlighted with a solid outline; linked MIDi clips are also highlighted with an alternate dashed outline. - SFZ file conversion, and bundling of the respective sample files, is now supported when saving as zip/archive (*.qtz). - Fixed track monitor, record, mute and solo dangling states, on Track/Duplicate command. - Slight regression on the LV2 State Files abstract/relative file-path mapping, trading QFileInfo::canonicalFilePath() for QFileInfo::absoluteFilePath(), and thus skipping all symlink dereferences in the process. - Fixed a one first linking/ref-counting glitch, affecting recently recorded MIDI clips which might have their initial clip length still un-quantized to MIDI resolution (BBT). - A brand new and discrete MIDI clip editor command tool has been added: MIDI Tools/Transpose/Reverse. - Discretely fixed MIDI Controllers catch-up algorithm. - Fixed a borderline mistake on plug-in parameter port index mapping to its corresponding symbolic name, especially if newer plug-in versions are loaded on older saved sessions. 0.7.8 2016-06-23 Snobby Graviton Beta - MIDI file track names (and any other SMF META events) are now converted to and from the base ASCII/Latin-1 encoding, as much to prevent invalid SMF whenever non-Latin-1 UTF-8 encoded MIDI track names are given. - MIDI file tempo-map and location markers import/export is now hopefully corrected, after almost a decade in mistake, regarding MIDI resolution conversion, when different than current session's setting (TPQN, ticks-per-quarter-note aka. ticks-per-beat, etc.) - Introducing LV2 UI Show interface support for other types than Qt, Gtk, X11 and lv2_external_ui. - Prevent any visual updates while exporting (freewheeling) audio tracks that have at least one plugin activate state automation enabled for playback (as much for not showing messages like "QObject::connect: Cannot queue arguments of type 'QVector'"... anymore). - The common buses management dialog (View/Buses...) sees the superfluous Refresh button finally removed, while two new button commands take its place: (move) Up and Down. - LV2 plug-in Patch support has been added and LV2 plug-ins parameter properties manipulation is now accessible on the generic plug-in properties dialog. - Fixed a recently introduced bug, that rendered all but one plug-in instance to silence, affecting only DSSI plug-ins which implement DSSI_Descriptor::run_multiple_synths() eg. fluidsynth-dssi, hexter, etc. 0.7.7 2016-04-27 Haziest Photon Beta - LV2 UI Touch feature/interface support added. - MIDI aware plug-ins are now void from multiple or parallel instantiation. - MIDI tracks and buses plug-in chains now honor the number of effective audio channels from the assigned audio output bus; dedicated audio output ports will keep default to the stereo two channels. - Plug-in rescan option has been added to plug-ins selection dialog (yet another suggestion by Frank Neumann, thanks). - Dropped the --enable-qt5 from configure as found redundant given that's the build default anyway (suggestion by Guido Scholz, thanks). - Immediate visual sync has been added to main and MIDI clip editor thumb-views (a request by Frank Neumann, thanks). - Fixed an old MIDI clip editor contents disappearing bug, which manifested when drawing free-hand (ie. Edit/Select Mode/Edit Draw is on) over and behind its start/beginning position (while in the lower view pane). 0.7.6 2016-04-05 Hazier Photon Beta - Plug-ins search path and out-of-process (aka. dummy) VST plug-in inventory scanning has been heavily refactored. - Fixed and optimized all dummy processing for plugins with more audio inputs and/or outputs than channels on a track or bus where it's inserted. - Fixed relative/absolute path mapping when saving/loading custom LV2 Plug-in State Presets. 0.7.5 2016-03-21 Hazy Photon Beta - Beat unit divisor, aka. the denominator or lower numeral in the time-signature, have now a visible and practical effect over the time-line, even though the standard MIDI tempo(BPM) is always denoted in beats as quarter-notes (1/4, crotchet, seminima) per minute. - Fixed an old hack on LV2 State Files abstract/relative file-path mapping when saving custom LV2 Presets (after a related issue on Fabla2, by Harry Van Haaren, thanks). - Default PC-Keyboard shortcuts may now be erasable and re- assigned (cf. Help/Shortcuts...). - New option on the audio/MIDI export dialog, on whether to add/import the exported result as brand new track(s). - Introducing brand new track icons property. - Old Dry/Wet Insert and Aux-Send pseudo-plugin parameters are now split into separate Dry and Wet controls, what else could it possibly be? :) - Brand new MIDI Insert and Aux-Send pseudo-plugins are now implemented with very similar semantics as the respective and existing audio counterparts. - Implement LV2_STATE__loadDefaultState feature (after pull request by Hanspeter Portner aka. ventosus, thanks). - Plugi-ins search paths internal logic has been refactored; an alternative file-name based search is now in effect for LADSPA, DSSI and VST plug-ins, whenever not found on their original file-path locations saved in a previous session. - Finally added this brand new menu Clip/Cross Fade command, aimed on setting fade-in/out ranges properly, just as far to (auto)cross-fade consecutive overlapping clips. 0.7.4 2016-01-28 Tackiest Gluon Beta - Eye-candy warning: muted/non-soloed tracks are now shaded over the main thumb-view. - Faster and crispier VST plugin editor (GUI) idle cycles. - Fixed all core processing when any plugin has more audio outputs than channels on a track/bus where it's inserted. - Added one decimal digit to all percentage input spin-boxes on the MIDI Tools dialog. - Added brand new and global option to disable the so called "catch-up" default behavior (cf. View/Controllers.../Sync). - Fixed some track control issues related to MIDI Controllers generic mapping (cf. View/Controllers...). - Try making Help/Shortcuts... dialog window modeless, as far as under MIDI Controller, Inputs/Outputs Connections window also gets accessible enough. - Fixed some vertical scrolling and play-head line re-drawing glitches introduced by the recent unlimited slack to editing beyond current contents length on main tracks view. - Added one decimal digit to the Pitch-shift spin-box on audio Clip/Edit... properties dialog window. - Added application keywords to freedesktop.org's AppData. - Fixed local keyboard shortcuts on the Files organizer widget actions and context-menu. - Improved Mixer multi-row layout (patch by Holger Marzen aka. bluebell, thanks). - Fixed the Ctrl+drag/cloning left of a clip when towards near the beginning of session. 0.7.3 2015-12-29 Tackier Gluon Beta - Slight increase on the number of decimal digits for the plugin parameters while on the generic plugin properties dialog. Also applied to automation curve node value editing. - Unlimited slack is now in effect on editing over and beyond the current session or clip contents length, on both the main tracks and MIDI clip editor (piano-roll) views. - Ctrl+click and dragging the left or right edges of a clip will now make it spill over and replicate as many clip clones as it fits in the left or right horizontal extent. - Added View/Note Type and Value Type command menus to the MIDI clip editor (aka. piano-roll) which opens the possibility for discrete shortcuts to switching views eg. Note Velocity and Controller views (after a kind request by yubatake, thanks). - Fixed the conversion and/or override of MIDI clip offsets when moving and copy/pasting across tempo/time-signature changes. - Fixed MIDI file track/channel duration estimator, which was giving quite wrong and way too short reads. - Fixed a drag-and-drop bug over the main tracks view, when new tracks were being inserted at the top and not to the bottom as is normally indicated by the floating visual placeholder. - Fixed LV2UI_Resize handle from extension_data(LV2_UI__resize), now passing LV2UI_Handle in first argument to ui_resize(), as found correct and needed for resizable/scaleable LV2 UI's, most specially to ssj71's so called Infamous Plugins, thanks. 0.7.2 2015-12-10 Tacky Gluon Beta - Yet another audio/MIDI time drift correction fix, now giving it some slack while turnaround looping on tempo changes. - Prevent x11extras module from use on non-X11/Unix platforms. - MIDI Track/Instrument cascading pop-up menus have been added, to main and MIDI clip editor windows. - VST Plugin preset/bank files support (FXB/FXP) is now being integrated to the generic Plugin/Properties widget dialog. - Added new Track/Duplicate menu command. - Added simple XRUN red indicator to status bar. - Make sure program change/presets are not selected on possibly multi-timbral instrument plugins when inserted on a MIDI bus. - Prefer Qt5 over Qt4 by default with configure script. - Fixed a potential crash-bug on first enabling either once the audio or MIDI metronomes. 0.7.1 2015-10-09 Meson Dope Beta - Fixed an ages old MIDI track/channel instrument mapping (bank, program) issue that prevented normal all-shut-up messages from being sent to MIDI output buses/ports on playback stop. - Messages standard output capture has been improved again, now in both ways a non-blocking pipe may get. - Fixed port on MIDI 14-bit controllers input caching. - Fixed false value readings on the MIDI clip editor (aka. piano -roll) tool-tips, when dragging a note velocity or controller value outside the acceptable nominal range (eg. 0-127). - Added LV2_BUF_SIZE__nominalBlockLength option support (patch by falktx aka. Filipe Coelho, thanks). - Fixed wrong initial tempo/time-signature display when session is loaded from command line. - LV2 plug-in UI GTK2 and X11 in Qt5 host native support added. - Transport/Auto Backward feature now honoring (auto return) to same current location precedence as Transport/Backward. - Single/unique application instance control adapted to Qt5/X11 (cf. configure --enable-xunique). - MIDI Tools/Transpose, Resize duration display format (frames, time or BBT) have been fixed. - Build fix for Qt5.5 (patch by KaOS, thanks). - MIDI Tools/Quantize et al. are tentatively being corrected to take event times as relative to THE beginning of session, instead of MIDI clip start location. 0.7.0 2015-07-24 Muon Base Beta - Complete rewrite of Qt4 vs. Qt5 configure builds. - Revised MIDI Controlllers catch-up algorithm. - Mixer multi-row layout gets a little bit of a fairness fix. - Non-continuous MIDI Controllers now have their Hook and Latch options disabled as those are found not applicable, - As an alternative to PC-keyboard shortcuts, MIDI controllers are now also assignable and configurable for any of the main menu command actions, all from the same old configuration dialog (Help/Shortcuts...). - Fixed missing Track and Clip sub-menus from Edit/context-menu that were found AWOL ever since after the Lazy Tachyon beta release (> 0.6.6). - An off-by-one bar position (as in BBT, bar, beat and ticks) has been purportedly fixed as long as LV2 Time/Position atom event transfer goes. - French (fr) translation line to desktop file added (patch by Olivier Humbert, thanks). - A new top-level widget window geometry state save and restore sub-routine is now in effect. - Improved MIDI clip editor resilience across tempo and time- signature changes. - Keyboard shortcuts configuration (Help/Shortcuts...) now lists complete menu/action path where available. - Fixed in-flight VST plugin editor (GUI) resizing. - Added support to LV2UI_portMap extension, found really handy for the cases where you have multiple plugins with different port configurations and a single common UI to drive them all (pull request by Hanspeter Portner aka. ventosus, thanks). 0.6.7 2015-05-27 Lepton Acid Beta - MIDI clip editor (aka. piano-roll) position, size, and view /event type criteria are now persistent, across session and user preferences application state. - Generic plugin form widget position is now also preserved across open/save session cycles. - MIDI clip editor resilience is about to get an improvement, re. it doesn't close on stopping record/overdub anymore. - Introducing (JACK) Timebase master setting as an option to Transport mode (cf. View/Options.../General/Transport /Timebase). - LV2 plug-in MIDI/Event support now slanted for deprecation. - Spanish (es) translation added, by David Reyes Pucheta. - It's live: audio track export (cf. Track/Export Tracks/ Audio...) has been deeply refactored to finally include MIDI track/instrument plugins rendering (aka. freeze) on selected audio output buses on mix-down. - MIDI file player now does (N)RPN 14-bit controller events. - Track properties dialog output bus switch fix/optimization; also fixed multiple DSSI instance reference count on close. - Fixed for some strict tests for Qt4 vs. Qt5 configure builds. - German (de) translation update (by Guido Scholz, thanks). 0.6.6 2015-03-29 Lazy Tachyon Beta - MIDI clip record/reopen to/from SMF format 0 has been fixed. - LV2 and VST plugins GUI editor widget position is preserved across hide/show cycles. - Added application description as freedesktop.org's AppData. - Added a "Don't ask this again" prompt option to zip/archive extrated directory removal/replace warning messages. - MIDI clip editor (aka. piano-roll) gets lingering notes properly shown while on record/overdubbing. - Current highlighted client/port connections are now drawn with thicker connector lines. - Fixing segfaults due to QClipboard::mimeData() returning an invalid null pointer while on Qt5 and Weston. - Return of an old hack/fix for some native VST plugins with GUI editor, on whether to skip the explicit shared library unloading on close and thus avoid some mysterious crashes on session and/or application exit. - Force reset of plugin selection list when any of the plugin search paths change (in View/Options.../Plugins/Paths). - Recursive VST plugin search is now in effect for inventory and discovery on path sub-directories (VST only). - Non-dummy scannig for regular VST, non-shell plugins, were doomed to infinite-loop freezes on discovery, now fixed. 0.6.5 2015-01-30 Fermion Ray Beta - Connections lines now drawn with anti-aliasing; connections splitter handles width is now reduced; the MIDI connections splitter pane sizes are now saved and restored properly. - Extended multi-selection is now featured on the track-list (main left-pane), primarily allowing for group mute/solo (and monitor) switching. - Track-list (left pane) header column widths are now saved and made persistent across application power cycle (double -click reverts to the old original default). - Minor fixes on the MIDI clip event list editor, also making sure the current event is visible on the piano-roll view. - As long to prevent asynchronous mistakes to JACK transport state changes, an internal slack-delay is now introduced after self-initiated transport commands (eg. start/stop). - The MIDI clip editor (aka. piano-roll) was missing to clear or reset the current selection when no shift/ctrl keyboard modifier is in effect. - VST-shell sub-plugins are now supported (as suggested by abique aka. Alexandre Bique, thanks). - MIDI clip record/overdubbing is now possible (Clip/Record on the main menu or File/Record from the MIDI clip editor. - Make sure some audio sample file encodings (eg. old Ogg Vorbis) does not head-start on audio peak generation. 0.6.4 2014-11-24 Baryon Throne Beta - Fixed some old loop-recording clip drawing glitches. - Current assigned track/channel instrument definition names for MIDI controllers, note keys, RPN and NRPN, are now in effect on the MIDI clip editor drop-down lists, whether available. - Clip/Take/Range... input dialog values are now properly sanitized as long to prevent invalid take/folding ranges. - Audio capture/export file type default now set to "wav". - Extending punch-in/out over loop-recording/takes modes. - Make audio tracks monitoring always flow while playback is rolling, independently of their mute/solo state. - Fixed undo/redo conversion of audio clip offsets under (automatic) time-stretching eg. due on tempo changes. (ticket by Holger Marzen, thanks). - Latch/momentary MIDI Controllers toggle mode introduced (a request by AutoStatic aka. Jeremy Jongepier, thanks). - JACK client/port pretty-name (metadata) support is being seamlessly introduced. - Audio frame/MIDI time drift correction is now an option on View/Options.../MIDI/Playback/Enable MIDI queue time drift correction. - Transport auto-backward feature now honoring last position playback was started. - Introducing brand new application user preferences on View/Options.../Display/Options/Custom style and color themes (eg. "KXStudio", by Filipe Coelho aka. falkTX). - Mixer widget gets automatic multi-row strip layout. - Clip fade-in/out now follows time-stretch resizing, via shift/ctrl+click and drag one of its edges. - Fixed a typo causing FTBFS when VST plug-in support is explicity disabled (./configure --disable-vst). 0.6.3 2014-09-22 Armed Hadron Beta - Make the mouse-wheel to scroll the plugin list views, when not hovering a direct-access parameter slider. - Mixer widget gets (un)dockable Inputs and Outputs panels, also with their respective title captions. - Plugin instantiation is now constrained as much to prevent any audio channel output overriding. - Existing plugin presets may now be selected right(-click) from plugin list context-menu (ticket by Harry van Haaren, thanks). - So-called "painting" over multiple selected event values, while on the MIDI clip editor view pane below the main piano-roll (eg. note velocities, controller values, etc.) is now split into two similar painting modes, whether the sub-menu Edit/Select Mode/Edit Draw is set on (free-hand) or off (linear). - Drag-and-copy of plug-in instances across tracks or buses (ie. cloning) now also copies the direct access parameter setting (ticket by Holger Marzen, thanks). - File/Save As... now prompts and suggests an incremental backup name for existing sessions files. - Zooming in/out increment is now augmented by whether shift /ctrl keyboard modifiers are set (on a ticket request by Holger Marzen, thanks). - LV2 Time position event messages for plugin atom ports that support it is now being implemented. - Attempt to break extremely long audio file peak generation on session close or program exit (as reported by EternalX, thanks again). - MIDI Controllers Hook and Invert properties are now properly saved for tracks (after bug report by Nicola Pandini, thanks). - A segmentation fault when closing with VST plugins open has been hopefully fixed (after a patch by EternalX, thanks). - Messages standard output capture has been slightly improved as for non-blocking i/o, whenever available. - Automation curve node editing has been slightly improved in regard to time positioning and resolution. 0.6.2 2014-07-07 Boson Walk Beta - Prevent linear and spline automation curve modes for all integer valued subjects. Also, make sure those values are rounded to the nearest integer away from zero. - Fixed save of LV2 Presets for plugins with state files. - A man page has beed added (making up Gürkan Sengün's work on debian, thanks). - When moving plugins by eg. drag-and-dropping across tracks, automation curves were being left behind, maybe leading to unpredictable mistaken behavior. Hopefully, not anymore. - Translations install directory change. - Automation curves are now automatically re-adjusted to tempo map node changes (after a ticket by Holger Marzen, thanks). - Audio/MIDI files or plugins found missing on session load are now subject for an explicit modal warning message and prompt for an immediate session backup salvage. - Changing instrument plugin programs is now an undo/redo-able command operation, especially for DSSI but also for plugins that come with the LV2 Programs interface extension support (http://kxstudio.sourceforge.net/ns/lv2ext/programs). - Drawing, selecting and/or resizing of MIDI note events that extend across tempo/time-signature changes is now made a bit more correctly over the MIDI clip editor (aka. piano-roll), especially regarding to current snap-to-beat setting (after an outstanding ticket by yubatake, thanks). - Once again, audio frame/MIDI time drift correction has been slightly refactored to improve MIDI input monitor and timing. - Discrete automation curve node values may now be edited via a numerical entry floating spin-box on double-click (as yet another request by AutoStatic aka. Jeremy Jongepier, thanks). - Pressing shift/ctrl keyboard modifiers while double-clicking on a plugin list entry now briefly reverses the current View /Options.../Plugins/Editor/Open plugin's editor (GUI) by default option preference. - Fixed an old crash lurker when switching output buses that implied a change on the number of audio channels, while on tracks that have (auto-)monitor turned on and at least one active plugin in chain (yet another ticket by AutoStatic aka. Jeremy Jongepier, thanks). - MIDI Controller assignment (aka MIDI learn) and/or automation of plugins (de)activation state has been added (as requested by AutoStatic aka. Jeremy Jongepier, thanks). - LV2 UI Idle and Show interfaces support added. - Allow the build system to include an user specified LDFLAGS (patch by Alessio Treglia aka. quadrispro, thanks). O.6.1 2014-04-29 Bitsy Sweet Beta - New user preference option added as View/Options.../Display /Transport/Hold auto-scrolling (follow play-head) on edits (after requests by Holger Marzen and Louigi Verona, thanks). - All color chooser dialogs were missing proper titles. - Audio peak file re-generation and clean-up has been hopefully fixed and cache optimized (re. drawing audio clip waveforms). - Fixed initial session snap-per-beat setting on main toolbar. - Clip/Export...'ed files are now made persistent, no questions asked (after a ticket by Oliver Kester, thanks). - Portuguese (pt) translation added (by Esteban Viveros, thanks). 0.6.0 2014-03-21 Byte Bald Beta - New user option added: on whether to save plugins automation values with higher resolution as possible, using 14-bit NRPN: cf. View/Options.../Plugins/Experimental/High resolution plugin automation (default=off). - Generic native plugin dialogs now shows an additional "About" last page where authorship credits are due. - A new user preference option is now in place for whether to use desktop environment's own native file requester/browser dialogs (View/Options.../Display/Dialogs/Use native dialogs). - A bit of slack have been introduced to put "Follow Playhead" (aka. auto-scroll view mode) on hold, while doing in-flight selection edit moves. - Fixed some user interface related annoyances while on the MIDI Controllers mappings (ie. View/Controllers...). - Fixed port origin on MIDI RPN/NRPN 14-bit controllers input. - A discretionary plug-in unique identifier have been devised for when more than one from the same type are inserted on a bus or track chain, avoiding destructive clashing of automation data. - Horizontal scrolling shift+mouse-wheel direction now reversed. - LV2 Dyn(amic)-manifest support is now optional (default=off); cf. View/Options.../Plugins/Experimental/LV2 Dynamic Manifest support). - The following options, although decieved on View/Options... as global configuration options, were always and still are proper session instance properties: (JACK) Transport mode, MMC mode, MMC device, MIDI SPP and MIDI Clock modes, are now shown there reflecting the current open session state. - A couple of run-time circumventions have been hacked in, both strictly related to when NSM session management is in charge: 1) the new session template feature is disabled (was aborting initial NSM new client additions); 2) the native (as from the desktop environment eg. KDE) file browser/requester dialogs are disabled (were taking too long to list the current directory on first time invocation). - Update current automation/curve nodes selection while changing horizontal (time axis) zoom levels. - One liner's attempt to make it consistent behaviour on resizing and moving multiple selected notes or events while on the MIDI clip editor (aka. piano-roll; after a ticket request from Daniel MacDonald aka. danboid, thanks). - Introducing tiny quarter-note/crotchet/seminima/beat icon on all snap-to-beat selection items get a new icon :). - Corrected some audio buffering boundary conditions that were causing dead-loops/freezes while merging some audio clips. - Session auto-save period was chronically reduced to one third of its user setting; non critical but fixed now. 0.5.12 2013-12-31 Mike November - Fixed another off-by-one MIDI plugin chain timing bug, delaying the actual playback of the first MIDI event, whenever it started at the same and as long as the initial play-head position (after another ticket by Holger Marzen, thanks again). - A new LV2 host convention is in effect on plugins that expose multiple AtomPort's in either direction: only the first input or output declared AtomPort is eligible to convey regular MIDI data events (introduced while trying Frank Kober's forthcoming QmidiArp LV2 suite of plugins, thanks). - Zooming with shift/ctrl+mouse-wheel and pointing over the main tracks view or MIDI clip editor time ruler header's now fixed. - A rare freezing loop condition has been fixed on the oldest of MIDI file save/write routines (after a ticket and solution by Tuomas Airaksinen, thanks again). - Copy/paste now preserving clip names (ticket by Holger Marzen, thanks). - Scaling of whole tempo map with a given factor has been added, through a spin-box and button in Tempo Map / Markers dialog, after a long overdue patch by Tuomas Airaksinen, thanks again. - When issued from main tracks view (ie. Clip/Tools menu) MIDI clip tools are now a bit more crash-resilient re. undo/redo- ability. - Session auto-save / crash-recovery support is now on trial as in View/Options.../General/Session/Auto-save current working session every 10 minutes (default on). - Plugin-list chain context-menu now includes MIDI instrument audio output options to existing non-dedicated output buses, any other than the previous hard-wired default (eg. Master). - Tracks export now allows for the mix-down/merge of multiple audio/MIDI output buses (Track/Export Tracks). - When adding new tracks, try preserving the last selected bus names, while not the default Master ones (after a suggestion from danboid aka. Daniel MacDonald, thanks). - More preparations for Qt5 configure build. - Brand new thumb-(over)view toolbar widget added to MIDI clip editor window (aka. piano-roll overview). - General MIDI RPN/NRPN 14-bit controller support is now being introduced on real-time MIDI input parsing, on standard MIDI file I/O, on MIDI Controller options and, of course, on MIDI clip and event list editor. - Pretty innocent nitpick on most thread cleanup routines. 0.5.11 2013-10-07 Lima Oscar - Adding a track now inserts it after the current one, if any; one can also drag and move a track below the last one in the track list (main view left pane). - Extended Edit/Select Mode/Automation: multi-selection mode, cut, copy, paste and delete of current track's automation curve nodes, now reached implementation ready status. - Another old silent bug bites the dust: changing track names were dropping any track gain/volume and panning automation curves when saving the session. - A primeval processing bug has been sorted out: aux-sends to audio output buses that just appear to be after the input bus where they're inserted were being left muted and silent (on a ticket follow-up by Holger Marzen, thanks). - Fixed a sure crash bug exposed when processing of aux-send plugins when inserted too early on audio input buses chain (after a ticket report by Holger Marzen, thanks). - Allow the build system to include an user specified CFLAGS (patch by Cristian Morales Vega, thanks). - Shift/Ctrl keyboard modifiers now set to extend current clip selection while in main track view's Edit/Select Mode/Range, Rectangle modes. - Main Edit/Select Mode/Automation icon retouched to look a bit more obvious and intuitive, hopefully ;) - Allow to change the velocities/values of the current selected events which have the exact same onset times and hide beyhond each other on the MIDI clip editor's pane below the main view piano-roll (ie. the one that represents MIDI event values as a bar chart). - Fixed some problematic playback/export muting and annoying cleanup freezing, due on audio tracks with too many clips eg. more than hundred clip splits (hopefully fixes an issue reported by Louigi Verona, thanks). - LV2 UI resize feature support/control added. - Fixed dedicated MIDI control and MIDI metronome port connection restore conflict (thanks to jhammen catch & patch:). - New user preference option added: reverse middle-button role to Shift/Ctrl keyboard state, in special regard to edit-head/tail vs. play-head positioning while on the main track and MIDI clip editor (aka. piano-roll) views. 0.5.10 2013-07-18 Kilo Papa - Default drum-key note names are now properly showing on MIDI tracks that are assigned to known drum/percussive instrument patches (eg. SoundFont 2 (.sf2) bank 128). - Time display format (frames, clock-time or BBT) may now be changed from the context-menu on any time entry spin-box. - LV2 plugin support is now tightly tied to liblilv; the same tie applies to LV2 plugin UI support and libsuil and vice-versa. - Mixer buses racks (ie. left/input and right/output panes) are now both kept fixed-width when whole mixer window is resized. - Unconditional LV2 Dyn(amic)-manifest support has been added. - Main track-view Edit/Insert, Remove/Range dialog is now being introduced with optional applicability to Clips, Loop, Punch in/out, Automation, Tempo-map and/or Markers. - New range removal editing tool, split/moving clips backward at the specified edit-head/tail interval (Edit/Remove/Range, Track Range)--by Tuomas Airaksinen, thanks. - Andy Fitzsimon's original icon from opencliparts.org makes it through as the default standard scalable format (SVG). - Automation's back in effect on Track/Export Tracks.../Audio. - Reversed Shift/Ctrl keyboard modifier roles on middle-button clicking over the main track and MIDI clip editor views (aka. piano-roll) in regression to original old semantics. - Color selection actions now have a brand new palette icon. - Make sure main track-view and MIDI clip editor selection is only cleared on specific discrete commands. - Try keeping the original session file in most recent files menu list, despite current version auto-incremental backup mode is in effect. - Fixed non-zero clip offsets upon tempo/time-scale changes. - Some sympathy to extreme dark color (read black) schemes is now indulged on empty backgrounds. 0.5.9 2013-06-06 Juliet Quebec - Auto-incremental version numbering of backup session files; brand new versioning mode option added. - Fixed one long as ever MIDI file export bug, which was about washing all self-induced MIDI bank-select (CC#0, CC#32) and program-change (PC#) events into MIDI channel 1, regardless of the source track/channel. - Fixed initial sample-rate settlement on plugin chains for all tracks and buses whenever a session file original sample-rate differs from the current audio device engine (ie. JACK). - New German (de) translation added (by Guido Scholz, thanks). - Now accepting JACK transport tempo and time-tignature changes while in slave transport mode. - Fix ref-count and auto-removal of created files that result from clip merge/export when session is closed but not saved. - MIDI track instrument bank/program selection, from plugin UIs that support it, is now effectively and complete. - Custom connections for dedicated audio outputs from all plugin chains in MIDI tracks or buses are back in business, hopefully restored gain on session load (ticket by yuba, thanks). - Track Properties dialog now dirty after any plugin related change (another head-up by yuba, thanks again). - Tempo Map/Markers dialog is not set initially dirty anymore. - Audio buses plugin state's persistance were found AWOL: while being properly saved as usual, were being restored to default on every session reload (as noticed by yuba, thanks). - NSM (Non Session Management) support is being introduced. - The MIDI clip editor (aka. piano-roll) gets a brand new rescale mode: ctrl+dragging the right edge of a note now rescales all times and duration of all the subsequent selected notes (after an interesting idea/ticket by Joel Leclerc, thanks). - The new MIDI clip editor rescale mode also applicable to event values (eg. note velocities, pitch-bend), while ctrl+dragging the edges of an event vertical bar. - VST plugin (GUI) idle timer makes a comeback. - Hard-wired LV2 UI selection order for plugins which supply more than one option (lv2_external_ui gets always honored first then, as provided via libsuil, X11, GTK and last but not least, Qt4). 0.5.8 2013-03-19 India Romeo - Dropped old audio ramping spin-locks as its glitching reduction weren't that effective anymore. - Audio bus and track gain may now be set for amplification again, from +0dB up to +6dB, while using the Mixer strip sliders/faders (an old function found missing ever since pre-TYOQA). - Basic LV2 X11 UI support has been added through libSUIL but only really effective if plugins doesn't support the LV2 External UI extension in addition which takes precedence on any case. - Improved precision tolerance on the Tempo Map / Markers dialog. - Reinstated and fixed (old) warning and impending re-conversion on loading session files which the original sample-rate differs from current audio device engine (aka. JACK). - LV2 Plugin State/Preset name discrimination fix (after a ticket by Jiri Prochaszka aka. Anchakor, thanks:) - Linked/ref-counted audio clips must not overlap and now must have a buffer-size worth of a gap between each other. - Something fishy has been detected in the SSE (not so) optimized code from SoundTouch's-inspired WSOLA time-stretching. - Splitting clips apart is now easier than ever: a brand new entry enters the main menu scene: Edit/Split (Ctrl+Y) splits up clips according to current range/rectangular selection. - Audio clip offsets are now properly corrected when time-stretching is applied via Shift/Ctrl+dragging any of the clip edges. - One semi-colon typo was hiding proper descrimination of peak files used to draw distinct waveforms of time-stretched audio clips. - Track automation curves are now also affected by Edit/Insert/Range commands. - Finally, some visual feedback is shown while audio track export is running, in he form of a main status progress bar. - New user option: save backup versions of existing session files. - Default session directory now set to regular file's path on load. - A convenient minimum slack duration has been fixed for MIDI SysEx messages. - LV2 Time/position information is now asynchronously fed back into their parameter (control input) ports when designated. - LV2 State is now properly restored for plugins inserted on buses, probably solving the Calf Fluidsynth SoundFont information missing on buses ticket, reported by Albert Graef, thanks. - Fixed an immediate null pointer crash on creating a parentless new group while on the files organizer widget. - Preparations for future Qt5 migration. 0.5.7 2012-12-27 Hotel Sierra - Loop and punch in/out ranges may now get set simultaneously, may even overlap each other. However, punch in/out range recording will always prevail over any loop recording takes. - LV2 Worker/Schedule now supporting multi-instance vs. single-UI scenarios. - LV2 Options and Buf-size extension support added. - Fixed an off-by-one-tick bug on MIDI file input which was leaving spurious zero-length runt notes at the end of a MIDI clip, if a note-on coincides with the ending/split point (on ticket issued by Jonathan H. Pickard, thanks). - Good old SLV2 library support for LV2 plug-ins (libslv2) is now irrevocably deprecated, or better said, completely wiped-out from the LV2 host code, now considered extinct. - LV2 UI parameter updates are now asynchronously detached from the source GUI widget thread, in attempt to improve cross-GUI-toolkit responsiveness, specially focused on LV2 plugins with a GTK based UI (eg. amsynth, triceratops, etc.). - Make sure LV2 UI parameters (input control ports) get updated when loading a genuine LV2 state preset. - Improved the (custom) tempo spin-box widget signal(ing) processing and dispatching. - Show proper pointing cursor and location tool-tip while dragging any of the time ruler markers (including latest location markers/bar). - On saving as an archive/zip session file (suffix .qtz) include only those files that are actually referenced by live clips arrangement. - Ongoing integration of location markers infrastructure (time-scale and MIDI-file support). - Free-hand/linear retouching of event values, while on the MIDI clip editor's view pane below the main piano-roll (eg. note velocities), is now possible provided the target events are selected, otherwise the usual painting edit sub-mode applies (cf. menu Edit/Select Mode/ Edit Draw). - The MIDI clip editor now senses which target view pane has focus for general selection commands (cf. Edit/Select/None, All, Invert, Range) whether the main piano-roll or the event value (velocities) view. - Mouse middle-button clicking is back in business on main track-view and on MIDI clip editor views (piano-roll) as an immediate play-head (re)positioning command or merge/reset the edit-head/tail cursors if Shift/Ctrl keyboard modifiers are pressed. - Formerly protected, class qtractorClip::FadeFunctor is now public in an shot-in-the-dark attempt to fix clang builds (as reported by Jekyll Wu, thanks). - Override all sub-classed widgets mouse-pointer event handlers to be isolated from base widget style and/or window management. 0.5.6 2012-10-02 Golf Tango - Day 1 post-release fix: LV2 plugins with a GTK GUI were not shown due to their respective widget (GdkWidget) being prematurely and unexpectedly destroyed when raised (via libsuil). - The dedicated audio outputs setting for instrument plugins was not in effect as default global option for new MIDI tracks, now fixed, hopefully (thanks to danboid aka. Daniel MacDonald persistence). - An immediate Files widget cleanup option is now made available, as to select and remove all unused file entries. - MIDI track (un)solo/mute reset clip event sequence fixing; definite regression to MIDI track's occasional muting bug while clip editing and playback is rolling. - Apologies due on this one: Shift/Ctrl keyboard modifiers are back in business to change MIDI events currently selected in one single step while on MIDI clip editor (aka. piano-roll). Sorry. - Japanese (ja) translation added (by Takashi Sakamoto). - General time-scale bar, beat and tempo rounding fixing. - Keyboard shortcuts configuration (Help/Shortcuts...) now checks and warns for already assigned key sequences. - The View/Snap menu is now listed as fully accessible actions, making each snap-per-beat setting assignable to a keyboard shortcut (after a feature request by danboid aka. Daniel MacDonald, thanks). - Fixed MIDI resize tool, now applying symmetric proportional changes to pitch-bend event values [-8191, +8191]. - Fixed re-loading of LV2 Presets that are newly created/saved during current session; actually delete and remove a LV2 Preset if found in the file-system (as suggested by Jiri Prochaszka aka. Anchakor, thanks again). - Preset drop-down list is now sorted, on plugin properties dialog. - After a long absence, edit(blue) cursor vertical lines are back on the MIDI clip editor views (aka. piano-roll). - LV2 1.0.0 compliance phase II: LV2 State Files support completed. - Updated to newer LV2 External UI extension support (now maintained by Filipe Coelho aka. falktx). - Brand new editing tool: empty range insertion, split/moving clips or events forward the specified edit-head/tail interval, defaults to next measure or bar (cf. menu Edit/Insert/Range, Track Range). - Don't show the waiting cursor anymore when loading any plugins which might have a nice native editor (GUI) and options are set to show it. - MIDI clip editor now hopefully rid of random but persistent muting, a slight regression to editing while playback is rolling. - A MIDI file player's bug, evident while transport rolling in looped mode, has been fixed with yet another oneliner patch (kind by Jiri Prochaszka aka. Anchakor, thanks again). - LV2 plugins with a Qt4 GUI (guess what or whom?) are now guaranteed to some reasonable window type like Qt::Widget but not Qt::Dialog nor Qt::Popup, preventing those from standing always on top while on some window managers or desktop environments (a simple addition by Jiri Prochaszka aka. Anchakor, thanks). - MIME type icons support for session (*.qtr *.qtr), template (*.qtt) and zip/archive (*.qtz) are now being introduced. - LV2 State Files feature support is shamelessly getting real trial, now letting files referenced in LV2 plugin states to be seamlessly included when saving to a zip/archive file bundle (.qtz suffix). - Added some virtual destructor stubs to shut up gcc 4.7 warnings. - Improved plugin state resilience, now finally with parameter name matching, specifically targeted on loading old sessions or plugin preset files (.qtx), preserving old saved plugin states as much as possible in face of rogue or discrete parameter index renumbering, due naturally on any plugin's life cycle ie. upgrades etc.;) - Added some more snap-to-beat divisors (Beat/14, /21 and /28) due on feature request ticket, while dropping highest, probably useless one (Beat/128). - New French (fr) translation added (by Yann Collette, thanks). - Slight late optimization on vertical zebra/grid lines drawing. 0.5.5 2012-06-15 Foxtrot Uniform - Auto-monitored MIDI tracks were missing their pass-through to their respective MIDI output bus plugin chains, now fixed and letting any multi-timbral instrument plugin to get a peek from auto-monitoring. - New user option/preference to whether to open a plugin's editor (GUI) by default, when available (cf. View/Options.../Plugins/Editor). - Clicking and/or dragging for rubber-band selection on main track-view canvas doesn't change the edit-head and -tail positions anymore. - Backward and Forward transport commands now have an additional stop at first clip start point. - LV2 Atom/MIDI buffering support is finally entering the scene; LV2 Worker/Schedule support is also included in a bold attempt to convey non-MIDI event transfers between plugin and its UI. - MIDI Clip editor (aka. piano-roll) and MIDI Tools fix: avoid note-on events of zero velocity, which conventionally equates to a dangling note-off event and dropped into oblivion sooner or later. There's no more need for Shift/Ctrl keyboard modifier to change in one single step all the MIDI events that are currently selected (now consistent with drag-move). - LV2 Presets support now entering effective operational status; a new local option has been added (cf. View/Options.../Plugins/Paths/LV2 Presets directory; default is ~/.lv2). - Dropped XInitThreads() head call as it was never useful but on those early days of JUCE VST plugins. - Italian (it) translation added (by Massimo Callegari, thanks). - Clip fade-in/out dragging now follows snap-to-beat setting. - Late modern eye-candy indulgence: alternate shaded stripes, on every other bar as in a "zebra" background option for the main tracks and MIDI clip editor views (cf. View/Snap/Zebra). - LV2 Time/position information is now being supported through special designated plugin input ports (after suggestion by Filipe Coelho aka. falktx). Additionally, the time/position information report has been corrected and complemented for VST plugins. - Audio vs. MIDI time drift correction has been slightly improved against rogue tempo changes across looping cycles. - Honor tempo/timing on MIDI instrument plugins. Happy regression fix on getting MIDI note-offs at looping ends back in business; all the necessary bumming for MIDI plugins to play nice in face of tempo changes and whenever playback is started from anywhere but the beginning of the time-line (ie. frame zero); thanks to rvega aka. Rafael Vega, for the heads-up). - Audio clip wave-forms were being displayed in inverted phase (ie. upside-down) all this time ever since day one. What a shame! :) - LV2 Programs interface is getting initial experimental status, to let LV2 instrument plugins get on par with the DSSI and VST crowd for MIDI bank/program instrument inventory and selection support (a sidetrack complot with Filipe Coelho aka. falktx, thanks:). - Dropped the old but entirely useless LV2 URI-unmap feature, now being superseded by official LV2 URID (un)mapper. - Russian (ru) translation added (by Alexandre Prokoudine, thanks). - SLV2 deprecation process started, effective now at configure time. - Added include to shut up gcc 4.7 build failures (patch by Alessio Treglia). - Another approach avoiding recursive observer widget updates. Also applies to mixer, monitor and track state buttons. - Update to latest LV2 state extension (by David Robillard, thanks). - Loop-recording/take number displayed on clip title, respectively. - Make(ing) -jN parallel builds now available for the masses. - A one buffer period slack on audio engine's loop turn-around logic might just have fixed an illusive report on loop-recording/takes going progressively out-of-sync, most notably when recording under large audio buffer period sizes (>= 1024 frames/buffer). - Editing MIDI while playback is rolling, doesn't mute the track any more, adding a point to the live editing experience. - Finer granularity for direct access parameter mouse wheel changes. - Dropped a dumb optimization for short full-cached multiple linked /ref-counted audio clips which were incidentally out-of-sync after rewind/backward playback. Once again and uncertain to be the last take on this, got fixed (probably related to some oddity reported by Louigi Verona, thanks). 0.5.4 2012-03-01 Echo Victor - Direct access plugin/insert parameter changing tool-tip added. - A Transport/Panic action enters the scene, in a nostalgic attempt to emulate the all-MIDI-track-shut-off command of those drop-dead and primordial MIDI sequencers of all time. Now finally a keyboard shortcut and mouse click-away ;) - MIDI editor command redo/undo adjustment now effective on all other channel events besides notes, which overlap at the same event time. - A new File/Unlink menu action is now made available from the MIDI clip editor (aka. piano-roll) for detaching the current linked/ref- counted MIDI clip into a new auto-incremented SMF filename. - Some audio/MIDI content/media-file resource management is entering the scene, taking care of some file-system house-keeping, this gets evident on unsaved/dead recorded files being automatically removed from the file-system, on session close. - Killed the old and entirely deprecated LV2 Save/Restore and Persist feature/extensions support. - Auto-monitored MIDI events are now merged/queued correctly into the instrument plugin playback queue, avoiding sudden crashes, hopefully. - Awesome patch from Albert Graef, thanks, which makes most MIDI SysEx to get through MIDI instrument plugins at last; applies to DSSI and LV2 plugins only. - LV2 URID map/unmap feature support added. - Plugin parameter value redo/undo command aliasing fix. - Double-clicking in plugin list item now show/activates the plugin's editor window (was toggling visibility/activation). - Plugin path settings have been fixed again, with special regards to an effective LV2_PATH environment variable settlement. - Session properties dialog now asks to create a new session directory if the given one does not currently exist. - MIDI note names and their respective octave numbers are now compliant with the ISO standard where middle C (60) is now C4 (was C3). - Fixed audible glitch/pop at the beginning of an audio clip with long quadratic or cubic shaped fade-in (reported by Lougi Verona, thanks). - MIDI Controller Auto-Hook patch by Alessandro Preziosi, thanks. - Make sure all MIDI note-off are always queued after their respective note-on events when buffering for MIDI input of instrument plugins, event though for zero duration MIDI note events (hopefully fixing the hanging notes bug reported by Albert Graef, thanks). - LV2 MIDI-fx plugin support has been repaired. - Single-track clip selection logic corrected again, fixing multi-clip selection drag/move across an odd number of distinct tracks (after a bug report by Louigi Verona, on linux-audio-dev, thanks). - MMC Locate time-code hour bit-field fix; MMC Locate now also on loop turnarounds (patches by Albert Graef, thanks). - Looping across multiple linked/ref-counted audio clips was incredibly broken with complete out-of-sync playback. Hopefully fixed now, with auto-unlinking/cloning all the affected audio clip buffers. - Audio clip overlapping detection off-by-one(-frame) fix. - MIDI Tools/Resize value ramp mode has been added (mocking the 'resize to range' feature request by Daniel MacDonald aka. danboid). 0.5.3 2011-12-28 Delta Whisky - Fixed initial LV2 plugin UI widget/window titles. - Attempt to get any brand new LV2 plugins Qt4 enabled UI's working on either slv2 and lilv build modes ;) (nailed by falkTX, thanks). - Current clip is ultimately inferred from the one under the play-head position and current selected track; the last one clicked over and/or selected still has precedence (following request by Loiugi Verona). - Drag-moving clips horizontally with the keyboard arrow-keys just got a step better with a fixed minimum of one pixel, depending still on the current snap-per-beat setting and horizontal zoom level (as suggested by Louigi Verona, thanks). - Get maximum and minimum peak values back when drawing audio waveforms. - Automation play/feedback has been missed to show on those plugins that provide their own GUI, now on par with all the rest 'homebrew' widgets (eg. generic plugin properties dialog). - All plugin parameters automation and selection were left inaccessible until the generic native plugin dialog is eventually shown, now fixed. 0.5.2 2011-12-16 Charlie X-ray - Fixed a probably old lurking destructive bug, which was incidental to when an invalid or non audio file is imported, intentionally or by mistake, into new or existing audio clips (eg. importing a MIDI file where an audio file is expected). - Force audio file closing to occur on the very same disk-write (sync) thread, possibly mitigating an old random crash issue lurking in the vorbis reference encoder (recording) re. the non thread-safeness of the vorbis_analysis() final call. - Fixed extraordinary shadow-playback of audio clips which extent were longer than the respective audio file length. - Default session file format option now promoted (see View/Options... /General/Session) in the way of whether file suffix gets honored upon session open or save (.qtr, .qts or .qtz). This brand new option also applies for default state file format on a JACK-session salvage context. - The View/Options... (aka. user preferences) dialog sees a new arranjement in layout, with some options moving into this brand new tab page called General. - LV2 State extension support has been added, which shall replace old LV2 Persist extension interface in the near future. It also replaces the LV2 Files extension from now on (thanks to Dave Robillard). - Dirty MIDI clips are now auto-saved when MIDI track properties get changed through its own dialog. - Fixed one probable too old crash when canceling a new/add track dialog. - MIDI SysEx event list editing is now fixed. - Once again, make sure all recorded clips start and end at the very same position when multiple track recording is turned on/off, while playback is still rolling. - Fixed Makefile.in handling installation directories to the configure script eg. --datadir, --localedir. - Possible attempt to improve in-flight recording clip display, especially when looping and MIDI clips are involved. - MIDI clip revision (re)numbering paranoia has been improved as much to avoid MIDI file clashes as much as possible while editing multiple MIDI clips which reference the same SMF and track/channel. - Creating new or updating existing buses with exact same names of any current dedicated inputs/outputs (which are implemented as special covert buses) is now disallowed. - Loop-recording/takes functionality is now ready for the masses and accessible from brand new Clip/Take menu (Select, First, Previous, Next, Last, Reset and Range...; the latter is actually being provided as bonus feature :) as to fold any given clip into so-called takes, simulating loop-recording mode over an arbitrary range). - Loop-recording/takes infrastructure integration is sneaking in: all clips recorded under a loop/cycle range may now get split (folded) into one or more so-called takes. A new option is now provided, as to select which take is about to retain after the recording ends, either the First or the Last one. Just turn this mode off (None) and keep with the old recording behavior, where clips are laid out unfolded through the timeline, as usual (see menu View/Options.../Display/Loop recording mode). - Improved robustness in face of disabled audio buses when global maximum number of JACK (audio) ports is surpassed. - Improved single-track clip selection status logic. - Clip/Unlink action is now available for detaching linked/ref-counted MIDI clips, renaming and saving into auto-incremental SMF filenames. - Temporary session archive directory now (pre)fixed with program name (eg. /tmp/qtractor). - Fixed VST GUI editor window title on first show. - Fixed build for ancient VST-SDK 2.3 (32bit only). 0.5.1 2011-10-05 Bravo Yankee - More LADISH support: SIGTERM signal is nowrapped for graceful application close. - Improved paste-repeat snap precision when dealing with large repeat counts. - Czech (cs) translation added, by Pavel Fric. - Added a few snap-to-beat divisors (unusual Beat/5, Beat/7, Beat/9 and Beat/10) for completeness sake. - Such a simple change of mind with a visual twist: the "A" track automation button/menu has now been moved to the right-most, as it belongs ;) - Auto-connection of dedicated audio outputs is now optional for default audio master bus, metronome, player and MIDI instrument plugin dedicated audio outputs. - Finally, after a pitiful large brain-dead period, generic plugin forms are now showing all possible controllable plugin parameters featuring a paged, tabbed dialog, whenever applicable. - Special hack/fix for JUCE based native VST plugins, which are the most actually found with a GUI editor, skipping explicit shared library unloading and thus avoiding some mysterious crashes on session close and/or application exit. - Support for MIDI-fx plugins (native VST and/or LV2) is now implemented and functional (intra-chain only).Instrument plugin bank/program selection was found broken or dead in the water, specially on VSTi, now finally fixed again. - VST plugin parameter value display on generic form is now back in business (were dead regarding value changes). - All plugin's provided GUI editors are now honored by being popped up first, instead of the usual but good old generic plugin form. - Long overdue implementation of a dedicated MIDI file player is now accessible from the MIDI files widget; one can play the whole SMF or just a single track or channel. - Update to a newer VeSTige header, probably fixing an old 32 vs. 64 bit mismatch. - Avoid JACK session filenames/paths to be ever shown; also avoid all buses ports (re)connections when JACK sessions are (re)loaded, given the fact that session management will take care of just that; more also, untitled/default session names are now also picked up to match current LADISH project name, obviously given by LADISH_PROJECT_NAME environment variable, whenever present (as suggested by Nedko Ardaunov). - Paste-repeat floating selection has been fixed (was showing only the last repeated selection). - Coherence of tempo changes on audio and MIDI clips, whether automatic time-stretching is in effect, has been slightly improved, hopefully fixed. - Bold attempt to get linked (aka. ref-counted) audio and MIDI clips into practice. - Main track view now showing all clips above the grid. - Added support for both new and older (deprecated uri) lv2_external_ui extension (by Nedko Ardaunov). - Following yet another great idea, and implementation, by Holger Dehnhardt, who already brought us the new aux-send insert plugin, thanks again, the so-called direct access control option is now featured for any plugin parameter right from plugin listings eg. mixer strips. - General resource consumption and management has been slightly improved, due to internal buses allocation optimization. - Fixed nasty crash-bug that was severely affecting all sessions that had at least one insert (pseudo-)plugin on any audio input bus. - All automation curve nodes are now relocated whenever disparate session sample-rate is detected on load. - New aux-send/insert pseudo-plugin is now introduced, allowing to route any track audio output to any other audio output bus besides the nominal one -- thanks to an original implementation from Holger Dehnhardt. - New immediate edit/loop-range settings from current clip extents, accessible on the main menu (Clip/Range Set, Loop Set) and from MIDI clip editor menu as well (File/Range Set, Loop Set). - MIDI Names XML files (*.midnam) may now be imported into MIDI instrument definitions. - Avoid cursor reset while editing MIDI events list (fixes bug reported by Frank Neumann). - Just some typos fixing (patch by c-korn). - Track view automation curve editing mode has been slightly fixed, now allowing for other previously existing point-and-click mouse operations. - Default automation curve mode is now preserved (following a suggestion by Alexandre Prokoudine, thanks). 0.5.0 2011-07-22 Alpha Zulu (TYOQA) - MIDI controller learn/catch-up sees the way in: MIDI controller changes are now only effective after catching-up with their respective program parameters, avoiding abrupt jumps and keeping a safe and continuous behavior. - Track/Height menu is now featured, giving access to Increase, Decrease or Reset the current track height. - All changes to audio gain and panning on tracks and buses are now applied following a piece-wise linear ramp, reducing the old nasty clicks, pops or zipper artifacts that might be awfully audible on some situations, most specially on automation. - All zooming in/out is now relative to either the viewport center or current mouse cursor position if found laying inside. - TYOQA! the underground sources have emerged:... after years in the making, track automation, or dynamic curves as some like to call, is finally a reality, tricky but real ;) - Audio clip anti-glitch/ramp-smoothing effect is now slightly independent of current buffer-size period. - Once buried under the Edit menu, Clip menu has been finally promoted to top main menu. - Debugging stacktrace now applies to all working threads. - Fixed muted loop playback on audio clips ending coincidentally with the loop-turn/end point. - Old/deprecated JACK port latency support added to audio recording latency compensation. - Audio clip merge/export lock-ups now untangled. - LV2 extension headers update. - Fixed configure of newer LV2 host implementation stack (LILV) when older (SLV2) is not present. 0.4.9 2011-05-26 The Final Dudette - Session file format saved on JACK session has been reverted to archive/zip bundle one (.qtz) now using temporary extraction directory when loading an existing JACK session. - Main toolbar time and tempo widgets get their visual extents a bit more theme-friendlier ;). - Some current working directory trickery is now in place avoiding JACK session directories to ever be picked as default, as much as possible. - Ghost-playbacks are now avoided on audio clips that are artificially extended beyond their own audio file lengths. - Recording clips now shown in a reddish shade; also, it's all now shown a bit more correctly, regarding the lead and within looping range. - Custom tempo spin-box widgets now honoring the decimal point cursor positioning for integral up/down tempo value stepping. - Audio recording latency is now compensated via automatic clip offsetting. - Audio peak file generation is now pipelined on a single unique thread, instead of old one per audio clip file basis. - MIDI tempo/time-signature map import problem has been hopefully fixed. - Session and track names are now sanitized from slashes (fixes bug# 625798@bugs.debian.org). - Mouse wheel effect to sliders is now reversed. - An appropriate export filename is now suggested as default (Track /Export Tracks...). - Follow-playhead automatism is now temporarily suspended while mouse cursor hovering prompts for any editing action (applies to main track view and MIDI clip editor/piano-roll). - Audio vs. MIDI time drift correction now takes jack_frame_time() as audio time reference. - Audio buffering internal synchronization logic gets it bartered: three bools for a single byte flag. - Connections are now preserved as possible when changing bus properties (View/Buses.../Update). - A rare audio clip looping out-of-sync condition got squared, hopefully the last ;) - Yet again, the audio clip buffer/disk-streaming optimization has been almost completely redrawn: now there's one thread per audio track. - Not replacing a session directory that already exists on loading an archive file (.qtz) is now fixed with an usable brand new untitled session. - The major thread optimization has been slightly improved: the audio clip buffer/disk-streaming thread is now served in a FIFO manner (was LIFO). - Custom time/tempo spin-box widget change fixup. - Audio clip filename change segfault/crash fixed. - Make sure all clips in multiple recording tracks start and end at the very same position whenever recording is already engaged and rolling. - Hopeful fix to a potential audio buffering race condition, which was a probable cause of random muted clips. - Avoid recursive observer widget value updates. - Almost complete rewrite of the main track-view selection and redrawing logic, taking advantage of the fundamentally static graphical backstore. - Autonomic resizing of mixer bus splitter sizes. - Improved timing for monitored MIDI events being buffered though MIDI instrument plugins, while playback/transport is rolling. - Audio peak/waveform is now slightly tweaked from the early optimization days (master C++ guru has always said that was root of all evil anyway :). - MIDI controller mapping now with "Invert" value option. Also, new "Inputs" and "Outputs" buttons have been added as helpers for MIDI control port connections access. - Main left pane vertical splitter resize hack, avoiding some track list update re-entrancy. - Inserting a LV2 instrument/synth plugin on an audio track or bus were causing immediate crash, now fixed (give or take some event buffer stub). - Plugin Activate All/Deactivate All menu fixing. - Make sure given session directory has all the necessary access permission (read/write) while on session properties dialog. - Dedicated audio outputs setting for instrument plugins inserted on the MIDI track properties dialog were not being honored, now fixed. - Force update/close of all MIDI clips and their respective editors (piano-roll) if open, when changing the global session tempo (BPM). - Removed the misleading "(Any)" special channel value while on MIDI controllers/learn dialog. - Floating tool-tips now being shown also while on mouse rubber-banding (drag-select). - Audio clip pitch-shifting change fixing; also, tooltips now showing semitones units instead of a clueless percentage. - Rendering audio wave-forms while recording is now a little bit smoother than before. - New main track-view clip selection tool: invert current selection (Edit/Select/Invert). The MIDI clip editor (piano-roll) also gets proper range selection tool (Edit/Select/Range). - More eye-candy: muted/non-soloed tracks are now slightly shaded on the main track-view. - A major hidden optimization has been implanted: all audio clip buffer/disk-streaming threads are finally merged into a single multiplexing thread (was one thread per audio clip longer than 3 sec. which was quite wasteful and creepy;). - All plugin list view changes are now properly signaled to track properties and bus manager dialogs and enable their respective acceptance. - Two brand new MIDI tools make their appearance: Scale-Quantize and Snap-to-Scale. The later may be readily accessible from the MIDI clip editor toolbar and menu (check View/Toolbars/Scale and View/Scale). - Mixer track strips are now completely redone whenever a track gets moved or re-ordered on main track list-view. - Transport auto-backward option is now honored whenever a new session gets loaded. - LV2 extension headers update. - Got rid of recent QX11EmbedContainer bloating, while introducing gtk_init() as for LV2 GTK UI support stabilization. - Tempo tap helper button was added to View/Tempo Map... dialog. - Executable DSSI plug-in GUI detection fixed. - Backout default session directory after cleaning up extracted archive/zip bundle session (.qtz). - Files widget item selection feedback/focus fix. - MIDI editor anchor event floating tool-tip fix. - Probable fix for GtkStyle usage detection (might be gentoo specific). 0.4.8 2011-01-18 The Fiery Demigoddess - MIDI controller mapping/learn is now possible on all mixer controls (monitor, gain, panning, record, mute, solo). - An internal rewrite (aka. refactoring) have been carried out, making sure that all track state action buttons (R, M, S) are now all under the observer pattern umbrella. - Single track range selection is now available on main menu (Edit/Select/Track Range; default keyboard shortcut: Ctrl+Shift+R); additionally to vertical range and horizontal track actions (Edit/Select/Range, Track) all these operations can now toggle over the previous selection. - Direct clip selection from Files list item has been brute-forcefully implemented (after being challenged by Jiri Prochaszka aka Anchakor:). - Files tree widget sticky "New Group" item fixed. - A new menu option has been added (View/Tool Tips) to show/hide a floating tool-tip while dragging, moving, resizing or pasting selected clips or events over the main tracks view and MIDI clip editor (piano- roll) respectively, displaying current target position and status. - Attempt to reset audio/MIDI time drift compensator on every engine start and loop turnaround. - Moving the punch-out marker over the main track time ruler was failing to shrink the punch-in/out range, now it does (not). - MIDI clip tools (quantize, transpose, normalize, randomize, resize, rescale and the new timeshift) are now all accessible from the main tracks view (Edit/Clip/Tools menu) and apply to all events on current selected clip(s). - LV2 Persist extension support is being introduced. - A new timeshift MIDI tool has been added, after an awesome patch by Jiri Prochaszka aka Anchakor; applies to selected events between edit markers, distorting their time and duration (optionally), either slowing down and accelerating, based on a given parameter P value. - Audio clip/buffer pitch-shifting fixes with regard to latency correction due on the RubberBand library one-pass (real-time) processing mode. - New Send Gain and Dry/Wet control parameters have been implemented for audio Inserts pseudo-plugins. - MIDI channel/master volume enqueued events are now affected by the current track/bus volume (0-100%). - Prevent old rounding error when resetting to plugin parameter default value with the mouse middle-button click while hovering a slider widget (observer). - A quantize percentage has been added to MIDI clip editor quantize tool (Tool/Quantize...) for time and duration quantization (0% for none; 100% for full regular quantization). - Metronome bus/ports are now created at engine start and not when user switches it on anymore. - Make sure all audio clip buffers are in sync upon smooth-ramping going off and playback is rolling. - Copy-pasting across controller event types is now possible on the MIDI clip editor (aka piano-roll). - Finally indulged, the genuine transport stop button makes its appearance on main toolbar and menu. - Main tracks grid visibility option (View/Snap/Grid). - Yet another off-by-one (frame) audio buffer bug got squashed away: rare symptom was that some audio clips were being left dead silent right after playback of their first looping period. - Plugin parameter name/label now a proper attribute of its respective MIDI Controller observer instance, allowing to be shown on dialog title (MIDI learn). - Default session file format saved on JACK session demand is now the bundle archive/zip one (.qtz). - Plugin selection dialog now shows whether a plugin features its own editor (GUI) and/or external state configuration (EXT). - Help/Shortcuts... window positioning and sizing are now preserved. - All plugin chain changes over the track properties dialog now sure counts as a dirty action. - Newly extracted archive/zip session directories are now removed automatically from the file-system on session close, prompting the user for confirmation (cf. View/Options.../Display/Confirm Removals). - Ctrl+mouse-wheel is now set for zooming in/out, on main track-view and all applicable MIDI clip editor views (piano-roll), according to current zoom mode (see View/Zoom Horizontal, Vertical or All for both ways simultaneously). - New MIDI clip editor (piano-roll) mouse hovering effect (eye-candy++). - After too many a user request, a brand new session archive file type is being introduced (.qtz) which tries to bundle in one single zip archive all the media and contents of a session. - Add that to eye-candy: either loop or punch-in/out outer ranges are now shaded on the main track-view, thumb-view and MIDI clip editor (piano-roll) views. - LV2 GTK UI plug-in hosting is now roaring its ugly head. - Ignore all initial and decremental notifications of audio engine's buffer-size changes. - Internal audio buffer loop points were not being set properly for non-zero clip offsets, leaving some as severely out-of-sync while rolling over loop turns. Now fixed again, hopefully. - Avoid audio peak file clashing when deriving from audio sample files with distinct absolute paths but the very same file (base)name. - A new MIDI editor (piano-roll) tool has been added: Rescale event times, durations and/or values by a percentage between 1 and 1000% (adapted thanks to patch by Jesse Lucas). - Attempt to mitigate audio clip sequencing glitches on single-core/single-thread machines. 0.4.7 2010-09-30 The Furious Desertrix - While moving multi-selected MIDI events around the clip editor (aka piano-roll), with help of keyboard arrow keys, that is, was not clear which one was the so-called "anchor" event, the one which positioning gets honored for snap-to-beat business. Not anymore: the anchor event now defaults to the earliest in time or the one the user's last point(-click)ed. - MIDI control observer pattern implementation has sneaked in, making it ready for the so-called and long-awaited "MIDI Learn" feature and arbitrary MIDI controller assignment, for plugin parameters in particular. - MMC DEFERRED PLAY doesn't cause transport state to stop if currently rolling. - Audio clip merge processing might have been skipping a few initial frame blocks, now fixed. - Clip selection and plugin parameter hash optimization. - Anti-glitch audio clip macro fade-in/out fixed again. - New clip fade-in/out slopes (curves) are introduced, partially adapted and refactored from those easing equations of Robert Penner's fame. - Clip fade-in/out non-linear slopes are now shown as actual WYSIWYG curves. - Escape key now closes generic plugin widgets as ever found usual elsewhere. - Picking nits: unselect current track when clicking on any gray empty area, also accessible from a new menu item: Track/Navigate/None. - A nasty and deadly MIDI resolution overflow has been finally fixed, allowing for long MIDI sequences (1h+) to load correctly on 32bit machines from now on (was perfectly fine on 64bit though). - MIDI editor selection hash optimization in face of reasonably huge event sequences. - MIDI controller mapping finally refactored to support some other MIDI event types than just CC (0xBn) ones. - Nitpicking fix: corrected main track-list (left pane) display when no track is currently selected. - libX11 is now being added explicitly to the build link phase, as seen necessary on some bleeding-edge distros eg. Fedora 13, Debian 6. - New audio metronome bar and beat sample gain options. - Progressively, the observer pattern is being finally introduced, targeting all potentially automation controls and widgets as plain ground-zero for the (ultra-)long overdue automation feature. - MIDI controller mapping of still non-existing tracks were being implicitly assigned to the last, highly numbered, existing track. Now fixed. - Moving from old deprecated Qt3'ish custom event post handling into regular asynchronous signal/slot strategy. - Muting/soloing tracks while playback is looping was leaving current audio clip out-of-sync whenever that same track is later un-muted on any other preceding clip. Now hopefully fixed. - MIDI Clock support makes its first appearance. - All tempo (BPM) calculations are now compliant to the MIDI conventional equivalence between beat and quarter note (1/4, crotchet) as common standard time division. - Automatic audio time-stretch option is not enabled by default anymore. - Standard warning Apply button is now only shown when dismissing dialog changes are actually valid. - Make sure non-dedicated metronome and player buses are properly reset and reopen when changing regular audio buses (hopefully fixing bug re. crash after changing audio bus). - Hopefully, an outrageously old bug got squashed away, which was causing random impromptu crashes, most often when importing audio clips while looping and play-head is any near the loop end point. - General standard dialog buttons layout is now in place. - Fixed main track view off-limits play-head positioning. - Main tool-bar Time and Tempo spin-boxes, may now have their colors correct, as for most non-Qt based theme engines (ie. Gnome). Green text on black background has been and still is the the intended aspect design ;) - MIDI file import and internal sequence representation has been changed to be inclusive on all bank-select (CC#0,32) and program-change events which were previously discarded while honoring MIDI track properties. Interleaved SysEx events are now also preserved on their original sequence positions instead of squashing a duplicate into the MIDI bus SysEx setup. - Attempt to include the VeSTige header by default, as for minimal VST plugin support. - JACK transport support has been slightly rewritten, in fact the sync callback is now in effect for repositioning. - The MIDI clip editor (piano roll) widget won't be flagged as a tool window anymore. - A tempo adjustment tool is making inroads from the menu, as Edit/Clip/Tempo... (factory shortcut: F7). - Audio tracks auto-monitoring is now effective on playback. - Make sure to ask whether a dirty MIDI clip should be saved, upon resizing or stretching its edges. - Backward and Forward transport commands are now taking additional stops on loop points. - Attempt to optimize track solo/mute redundant transactions, in special regard to MIDI track events which were being duplicated on soloing and temporarily muted on unsoloing. 0.4.6 2010-05-21 The Funky Deviless - Introducing a non-painting edit sub-mode on the MIDI clip editor's piano-roll (see Edit/Select Mode/Edit Draw menu). - The MIDI clip editor (aka piano-roll) is now a lot more quiet about saving its own dirty content, delegating all salvage questions to main session control. - Don't show session restart message box when changing JACK transport mode option anymore. - Dedicated MIDI control bus switching fixed. Was closing the wrong bus eventually and crashing the whole show with it. - MIDI bank/program backout has been corrected on MIDI track properties dialog rejection (ie. user cancellation). - MIDI bank select method has been corrected for tracks with no instrument defined. - LV2 UI Instance and Data Access extension support added; reduce LV2 external UI parameter value update flickering. - JACK session infrastructure support. - Initial widget geometry and visibility persistence logic has been slightly revised as much to avoid crash failures due to wrong main widget hidden state. - Initial mixer widget extents are now set reasonably larger. - General source tree layout and build configuration change. - Ever since smooth-ramping introduction that having at least one input-only buses were causing immediate playback crashes, now hopefully fixed. - Refactored for common engine client nomenclature, primarily provided by JACK, then secondarily passed to ALSA Sequencer, getting rid of the JackUseExactName requirement and lifting the unique/single instance restriction in the process. - Current JACK Transport, MMC Device, and MIDI Song Position pointer (SPP) control modes are now saved/loaded as part of session option properties. - MIDI clip editor's context menu crash on Qt >= 4.6 has been fixed. - An ancient double-free corruption has been finally fixed at the audio/MIDI bus connection persistence logic. - Improved visibility of track state buttons text (R, M, S) when turned on dark colored themes. - LV2 Save/Restore extension support kicks off. - MIDI engine read-ahead period has been shortened to half than it was since inception--now it's a 500msec cycle. - MIDI clip editor event list gets its due inline editing, for time, note, value/velocity and duration columns, just one double-click away over the target cell ;) - Add-plugin selection dialog position and extent are now remembered across invocations and application sessions (tipping by Frank Neumann). - MIDI clip time-stretching is now made available through the same gestures as audio ones, by just shift+dragging either of the clip edges. - Drag-and-copying plug-in instances (cloning) is now fixed with regard to parameter value replication. - MIDI clip editor snap-per-beat setting is now independent from main multi-track view; File/Save As... dialog fixed; the current event selection is now kept floating as long as it's possible after editing command actions; finally, edit mode has been extended to free-hand event drawing, chalking off (piano roll) draw mode from the TODO list. - Swing-quantize has finally made its overdue debut as an additional MIDI clip editor tool (see Tools/Quantize...). - Almost since its inception, audio inserts were injecting garbage random noise when not being activated, now fixed. - Dedicated audio output ports for MIDI track plugins, now have their connection persistence back in business due on session load. 0.4.5 2010-01-23 A Friskier Demivierge ;) - Changing loop points while playback is rolling, with the play-head any near, was leaving audio clips out-of-sync. - MIDI event list view was missing some selected items with the very same onset time, now fixed. - When failing to detect a SSE enabled build, the CFLAGS variables are now properly restored to their previous sane state, preventing all subsequent dependency tests from false positives (bug# 565860 @bugs.debian.org). - MIDI clip editor (aka piano-roll) multiple selection has been fixed (again) re. move/paste-snapping consistency. 0.4.4 2010-01-16 The Frisky Demivierge - For all the DSSI plugins that have output control ports, a host feedback/update process cycle is now being finally provided: all output control ports are now marshaled to their respective GUI process, rather often and when found open/visible. - MIDI clip editor (aka piano-roll) snap-to-beat behavior on edit mode is now kind of more like 'filling-in-the-blanks' (as Frank Neumann et al. wishes ;) - Fixed MIDI clip editor mistake when reverting to initial clip length, before closing and discard changes (thanks to Frank Neumann, for spotting this one). - LADISH Level 1 support has been added: SIGUSR1 signal trap just makes it a shortcut to File/Save. - Avoid parameter value flickering, due to duplicate command invocation, most evident when changing values massively on native Linux VSTi plugin editor GUIs (thanks to a detailed report on this odd behavior, from Mike of linuxDSP.co.uk). - Another TODO item bites the dust: MIDI event list editor, now acessible from the MIDI clip editor menu (View/Events) - Last used session directory is now made current on startup only when no filename is given on the command line. - Current snap-to-beat setting (time quantization) now affects the anchor event only, while dragging, moving and/or pasting multiple events over the MIDI clip editor (aka piano-roll). - Make anti-glitch audio clip micro fade-in/outs independent from current buffer size as much as possible. - Audio/MIDI engine drift correction gets really sophisticated, with the help of (now old) ALSA MIDI tempo skew facility. - Edit/Clip/Import... menu option is now available for expedite clip insertion from audio and MIDI file requesters. - Set default session directory effective to file's location. - Audio track/clip recording process has been target to special refactorization across the internal audio engine process cycle, in a late attempt to get self-bounce/recording effective and working consistently for all track layouts. - All session related dialogs are now set to window modality, (were set to default application modality before) allowing for continued input focus and interaction on all plugin/tool windows. - An off-by-one nasty old bug fixed in audio clip drawing, was causing instant crashes on certain zoom levels of the main track view. - Graphical MIDI clip representation regarding note/pitch range is now kept as much as possible across clip edits (cut, copy, paste, drag, move, delete, etc.) - LV2 plug-in hosting has finally come into actual implementation; only some and the most basic LV2 plug-in features are supported at the moment; probably there's no big advantage against the old LADSPA ones; there's some support for external UIs though; also, LV2 MIDI/Event bare-bones support is included but chances are it won't build nor work right on most of the setups out there. It's a WIP host implementation anyways, as is the whole LV2 spec. for that matter ;) - Connections filter is now reset when widget is shown through the View/Connections main menu or toolbar button. - Audio bus auto-connection option is now applied when creating or updating, newer or existing buses, respectively. - Global configuration state is now explicitly saved/committed to disk whenever View/Options... dialog changes are applied or when a session is loaded or saved. - Audio ramping spin-locking makes its smooth stuff, in an attempt to reduce glitching and crackling when editing (due to its own pseudo spin-locking) and toggling playback states. - JACK Transport, MMC Device, and MIDI Song Position pointer (SPP) control modes are now made optional (View/Options...), allowing for discretionary configuration: None/Disabled, Slave/Input, Master/Output or Full/Duplex (default). - Session files may now be dragged and dropped over the main track view and get loaded for business as usual (once quietly ignored). - In an attempt to mitigate potential stack corruption and sudden crashes, old commented out session pseudo-locks are now back in business while executing clip editing commands (cut, paste, drag, move, insert, delete) and playback is currently rolling. - Adjusted first-time application window size to fit into 800x600 screen size and with reasonable initial dockables layout. - Avoid duplicate snap-to-grid effect when changing the length of MIDI clip editor events across non-zero clip offsets (after a glitch reported by Ralf Mardorf). - Late audio track processing optimization, suppressing all plugin, mixer and monitor pass-through activity when given track is muted, either explicitly or implicitly (ie. other track is in solo state). - Entering System Exclusive events (SysEx) on the MIDI clip editor (aka matrix/piano-roll widget), yet something not fully supported anyway, even though allowed in edit mode, doesn't crash the whole damn thing anymore, while saving the clip to a file. - Strict aliasing avoidance, with plain and demanded use of 'union', as much as to stop nagging warnings from gcc >= 4.4.1 (last seen on src/qtractorMidiEvent.h hackery). - Visual correct play-head position while changing zoom levels, applicable to both main track and MIDI clip editor views. 0.4.3 2009-10-05 The Fussy Doula - External preset files are not removed nor deleted from the file-system anymore. - Connections support for UTF-8 encoded client/port names. - Force track and clip properties dialog widget to be modal as it should from their beginning dawn. - Audio effect send/return aux. inserts are implemented as special pseudo-plugins (Plugins/Inserts). - Reset play-head position on auto-backward and keep playback rolling when continue past end transport option is not set. - MIDI clip editor (aka piano-roll/matrix aditor) gets better on the virtual piano keyboard eye-candy side of things ;). - Plugins are now also referenced by label, avoiding plugin index clash/misses eg. when plugin object file/path changes or is moved externally. - Keyboard focus is now cleared/reset from the main toolbar time and tempo spin-boxes when editing gets finished (eg. Enter key is pressed). - First audio metronome beat/bar now played back correctly. - Client to/from port (dis)connections now found consistent as good ol'QjackCtl behavior. - All dirty open MIDI clip editors are now prompted to save before the main application closes. - Mixer level meters get their long deserved gradient look. - Fixed any ghost clip selections that were haunting the main track view, specially after undo/redo. - Increased tolerance on reading corrupt MIDI files (SMF). - A MIDI SysEx manager is being finally introduced, in some primordial rather basic form though. MIDI System Exclusive (SysEx) event strings may now be freely assigned to MIDI output buses only, allowing for proper setup of external outboard MIDI equipment. Each bus may have an unlimited SysEx queue that gets sent out on every connection change (see View/Buses.../MIDI/SysEx...). - A default MIDI instrument name may now be assigned to any MIDI output bus (see View/Buses.../MIDI). - More legacy headers, stdio.h and stdlib.h, are yet again necessary to build with gcc/g++ >= 4.4 (as patch noted by Alexis Ballier on Gentoo bug report #274168, thanks). - Bus manager dialog (View/Buses...) gets new columns on the left pane buses list as for displaying number of channels and bus mode. - Crash when updating bus probably fixed. - Fixed glitch displaying beat snap/grid lines on MIDI clip editor, incidental to clips located at absolute zero time. - Overlapped MIDI clips were rendering garbled note events to DSSI/VSTi plugins, now fixed. - New MIDI Playback/Queue timer (resolution) option is now available (see View/Options.../MIDI). - MIDI instrument definitions may now be imported from plain SoundFont files. 0.4.2 2009-06-04 The Flaunty Demoness - The MIDI clip editor (piano-roll/matrix editor), the main track view as well, have been subject to usability fixing, the most notable avoids clearing current selection as much as possible when updating view contents (eg. changing zoom levels does not reset current selection anymore). - MIDI tracks channel bank/program and controller stuff are now only issued when the respective bus connections have changed, seldom on every playback start. - MIDI controller mapping infrastructure, with file based configuration management (see View/Controllers...), is now in place, following an original contribution from gizzmo aka Mathias Krause. - Plugin chain buffer reset on playback start/stop is not guarded by a momentary plugin de/activation anymore. - Clip export may now be applied to multiple clips, sharing common refactored code and same semantics as merging of current selected clips. - Improved, may be just fixed yet again, audio track export synchronization and reliability. - Clip merge is now featured both for audio and MIDI tracks (see Edit/Clip/Merge...). - Improved, or better said, fixed (again) the precision of multi-clip final positioning as result of drag/move and paste operations in main track view. - MIDI track program number is now listed in 1-128 range, in an attempt to be consistent with corresponding MIDI track dialog drop-down list. - MIDI editor snap grid lines get slight different color then regular beat divisions. - Reset local tempo map cursor on newer MIDI file imports in a tentative to fix incidental but random crashes. 0.4.1 2009-04-04 The Funky Dominatrix - MIDI editor command item execution order has been fixed, correcting the redo/undo adjustment of overlapping note events. - MIDI clip editor (aka. piano-roll/matrix editor) sees one of its most wanted features introduced: visual snap grid, now accessible through View/Snap/Grid option toggle. - Actual non-zero session length gets back to status bar of main application window. - One potential buffer-overflow/memory-corruption crash bug has been fixed, long due on most audio (down) sample-rate conversions and affecting audio export in particular. - MIDI track/channel patch information, ie. bank-select and program-change events, are now being properly set on MIDI track/clip export. - SSE optimization is back in town after being mysteriously disabled since its dawn :/ - Looping and punch-recording now actively mutual exclusive states: setting either one unsets the other off and vice- versa. Also, punch-in/out is now made an undoable command. - Moving tracks, any track, up or down, were leaving MIDI playback and meter monitoring completely out-of-sync, now fixed. - Automatic crash-dump reports, debugger stack-traces (gdb), back-traces, whatever, are being introduced as a brand new configure option (--enable-stacktrace) and default enabled on debug build targets (--enable-debug). - Audio/MIDI drift correction is now progressive, taking a least significant differential approach, on every read- ahead cycle and swallowed on loop turn-arounds, as before. - Improved Edit/Clip/Normalize and Quantize commands, now affecting the whole extended multi-clip selection. - Playback is now being temporarily suspended while either transport rewind or fast-forward rolling is engaged. - A bad and shame-on-me bug was fixed: this was hideously affecting any track/clip playback synchronization, most noticeable after toggling solo/mute track states while playback is rolling and skipping the play-head backward over more than one clip under the same track. - A floating-rectangle flip that showed while dragging new files beyond the left of main track view is now gone. - MIDI note event truncation on both track and clip export has been fixed. 0.4.0 2009-03-13 The Foxy Dryad - MIDI (re)connections fix; now caring for the ALSA client and port textual names only, avoiding as much as possible, any reliance on those volatile client and port numbers. - Transport/Backward and Forward commands may now reset to immediate full start or end of session locations, by just pressing the Shift or Ctrl keyboard modifiers and clicking their respective toolbar buttons. - Default session/MIDI resolution has been set to 960 ticks per beat (960 TPQN, where a beat equals a quarter-note); it is worth of note that the previous default resolution was set to one order of magnitude lower, ie. 96 TPQN ;). - Making (dis)connections now also flags session as dirty. - Internal Audio/MIDI engines queue/time drift correction takes a brand new approach, specially adapted to rolling tempo/time-signature changes. - MIDI monitor refresh-cycle slight internal optimization. - Converted obsolete QMessageBox forms to standard buttons. - Transport/Rewind and Fast-forward commands may now be set to double speed, by pressing the Shift or Ctrl modifiers while clicking their respective tool buttons. - MIDI clip editor zoom ratios are now saved and preserved across sessions. - Time-signature is now directly accessible from the main tempo spin-box which also reflects current tempo status. - Time/frame spin-boxes now allow to step change each field individually, depending on the cursor beam position. - Make sure that Transport/Follow playhead option is only effective when playback is actually rolling. - Primordial attempt to include MIDI Song Position Pointer (SPP), Song Start, Stop and Continue sequencing support. - A completely new time-scale infrastructure is now in place, with full support for session tempo and time-signature map; this long due feature is primarily accessible through the main menu, View/Tempo Map...; also by double-clicking on the the main window and MIDI clip editor time rulers and left-clicking on the main toolbar tempo/signature spin-box. - Moving and resizing individual clips now cares for track proper ordering and overlapping changes, avoiding nasty out of sequence clips and other unpredictable effects. - An expedite MIDI clip quantize command is now available from the main track view menu (Edit/Clip/Quantize), which simply applies the current snap-to-beat setting to a MIDI clip range selection. - Fixed that hideous bug affecting overlapped audio clips when playhead gets moved backward, causing the playback of those audio clips in particular, go out of sync. - Tracks are now limited to their minimum height, specially effective in face of vertical zooming. - Zoom mode option introduced (on menu View/Zoom/Horizontal, Vertical, All). - Tempo beat type is a new session property; however it is not yet user modifiable and currently disabled to default MIDI quarter note (1/4). - All open MIDI clip editor time-scales are now updated and corrected when the main session time base changes (tempo, time-signature, resolution, etc.) - MIDI metronome fixed, preventing duplicate click events. - MP3 audio file decoding was broken for way too long and falling short for every file with custom frames, ID3 tags and comments. Got shamefully fixed. - Time signature denominator (ie. beat divisor) is now an accessible and effective session property. - Attempt to retain original size (clip length) of all audio clips when changing the global session tempo and automatic time-stretching is not an option. 0.3.0 2008-12-25 The Fluffy Doll - Almost complete rewrite of the plugin configuration and parameter initialization logic. - MIDI bank/program selection is now taken into account on plugins initialization and replication. - Fixed initial parameter values for native VST plugins. - Track form plugin lists are now properly (re)initialized when track type changes. - Generic plugin forms now have the option to show/hide the parameter widgets through the new "Params" button. - New auto-monitor toggle option (menu Track/Auto Monitor): the current selected track is now set on monitor and MIDI channel omni-mode automagically, as a convenient workflow feature (kindly suggested by Holborn). - MIDI clip editor Tools menu is not disabled anymore when there's no selection, drop-down menu items are instead. - Make all recorded clips to honor either the punch-out or play-head accumulated position; resolve all pending MIDI sequence note events on record stop/close. - Major silent move: audio plugins chain are now applied in a pre-fader/meter basis as is usually implied from the mixer strip layout ie. signal flows from the top to the bottom. - All MIDI buses may now have plugins inserted so that multi-timbral synth/sampler plugins get driven to their fullness. - MIDI track plugin's dedicated audio output bus may now be effective, as it seems, good old master audio output bus was being used, no matter what. - Paste-repeat command has been introduced, now allowing to replicate and concatenate the clipboard contents over the time-line, with a given repeat-count and optional period (see menu Edit/Paste Repeat... on the main and MIDI clip editor windows). - Normalize tool on MIDI clip editor got rewritten from its previous brain-dead, useless and utterly wrong operation. - All time offsets and lengths are now zero-bar/beat based when displayed in the BBT (bar.beat.ticks) format. - MMC STEP gets adjusted to current snap-per-beat setting. - Fixed broken initial buffering that was randomly crippling those audio clips that fit integrally in cached and while on playback. - Fix initialization of multiple instances of DSSI plugins which implement run_multiple_synths (eg. fluidsynth-dssi), preventing an instant crash on activation. - Exclude deprecated VST elements from compilation. - Export tracks dialog has new punch in/out range option. - Somehow realized that looping and punch-recording are two mutually exclusive states, at least until loop recording (ie. takes) gets real. - Fixed crash on tempo change; affecting the WSOLA based time-stretching on all non-stereo audio clips. - Incomplete audio peaks were being cached prematurely, fixed. - Make way for paste/dropping items from the system clipboard over the main track view. Cut/Copy/Paste/Delete of file items have now this workaround fixed, wrt. Files widget keyboard shortcuts, respectively. - Clip gain/volume propriety is now in place and reflected in audio clip waveform drawing in particular. - A new hideous progress bar is now lurking in the main status line, as found convenient to display progress of the also new clip tools (normalize, export, etc.). - Clip normalize tool is now available (Edit/Clip/Normalize). - Audio and MIDI clip file export is now available as a tool (see Edit/Clip/Export...). - Punch in/out (range) recording is now in experimental shape, with minimal settings and functionality, already accessible through the main menus, transport toolbars and visible on main tracks view and MIDI editors as magenta colored line markers. - External MIDI control events for channel volume (7) and channel panning (10) are now handled properly through session tracks. - Session file templates make its debut with new usability option, on whether new sessions are created based on existing template file (see View/Options.../Display/Session/New session template; nb. session templates are just regular session files but loaded and saved with no media content (no clips nor files). - Grayed/disabled palette color group fix for dark color themes. - Implicit attempt to flush all pending notes for some, if not most plugin instruments (eg. VSTi), on playback stop. - Fait-divers: desktop menu file touched to openSUSE conventions. - Internal refactoring alert: Session and Options instances are now being redesigned as singletons, preparing to get out of the way from the master GUI/MainForm instance. - Clip drawing methods refactored so let the fade-in/out handles get seen with transparency over the clip graphics content. - Reset and continue looping even still when continue past end transport option is not set and playback is rolling. 0.2.2 2008-10-05 The Flirty Ditz - Slight optimization in audio and MIDI meters refresh rate. - Another ancient bug has been squashed: MIDI events were being recorded even though recording wasn't rolling; spurious event times were being recorded due to an absent started queue. - Major fix applied to audio track monitor metering, and most importantly to plugin processing, correcting tentatively all audio buffer offsetting and slicing due on loop turnarounds. - Fixed a potential crash and/or simple record dismissal when changing properties of a track already armed for recording; prevent record engaged tracks from editing or removal. - Lighten up the connections line and highlight colors, as seen to fit best on some darker background themes. - Several icons refined with slight transparent shadowing. - Send/reset all MIDI buses and track controllers (ie. volume and panning) only when main transport playback is started, avoiding the pouring on eg. loop, playhead or tempo changes. - Pressing the Escape key also clears current selection in the main track-view and MIDI clip editor; resizing multiple events at once doesn't need help from Shift or Ctrl modifiers anymore. - DSSI and VSTi plugins get all their default parameters values reset on MIDI program change. - Several major fixes have been applied to the MIDI clip editor, regarding snap precision and correctness, most specially due on clips which weren't located on exact bar boundaries. - Brand new usability feature introduced: mute, solo and monitor toggling may now be applied to all tracks in session at once, when issued with either the Shift or Ctrl keyboard modifiers, which will set or reverse respectively all other tracks state. - Audio buses plugin chain may be also accessed and edited from the extended bus management dialog (View/Buses...). - MIDI meter level default color is now set distinct from the old lime-green one as in audio level meters. - MIDI clip editor is now a genuine top-level window, fixing all keyboard shortcut ambiguities with main application window. - Mixer splitter panes are now collapsible and optionally hidden. - Make MIDI instrument patch management a little more sane, as for preventing the accidental insertion of blank instrument names and automatic default bank/program selection in track properties. - All connections are now based exclusively on the textual client and port names, effective in particular to match MIDI bus ports with disregard to their volatile numerical identification. - MIDI file (SMF) header endianess fix (PPC users rejoyce:)) - Record armed tracks aren't muted for playback anymore, as this was a severe crippling nuisance regarding input monitoring and all mighty user experience after recording a simple take; for instance, as the bottom line goes, there's no need to un-arm a track from its record enabled state anymore, for just recorded material get heard on immediate playback; kick on the jam! - Playhead position overflow fixed on negative MMC STEP commands. - Thumb-view width proportions now based on minimal slack session length instead of the auto-extending track-view contents width. - Optimize audio clip drawing, most specially on zoomed-out levels. - Bring the audio peak frames into some sort of cache, preventing recurrent peak frame buffer reallocation and trashing. 0.2.1 2008-08-30 The Fainty Diva - Gradient eye-candy now featured for clips, tracks and mixer strips widget backgrounds, disabled on configure time (--disable-gradient). - MIDI pitch-bend/wheel events are now captured/recorded properly; in fact, there was this ancient bug, now squashed, as all MIDI clips weren't being stored at all if there weren't a single note event captured. - MIDI channel translation is finally in effect on monitored tracks, specially the ones set in omni-channel input mode. - MIDI open files dialog gets a few more file filter types now. - Playhead position is now shown, updated and can be also set on the main toolbar session thumb-view (Shift-click to set the playhead). - The floating selection, as shown for a clipboard pasting operation, has been corrected regarding time scale (horizontal zoom) changes, while in the main track-view. Also fixed final position snapping precision. - Current tempo and snap-per-beat setting now survive session cycling. - DSSI plugins implementing run_multiple_synths (eg. fluidsynth-dssi) are now formally supported according to the DSSI spec (hopefully); note that previously one could only have one DSSI plugin instance loaded in session in any given time, otherwise a sudden crash was in sure demand when either plugin got activated. - Audio plugin outputs now overrides each other when the number of output ports does not match the number of mix-down audio channels and thus fixing a nasty crash bug in the process. - All custom font sizes are now set relative to default global font. - Changing loop points by dragging on the time rulers is now mapped as undoable/redoable commands as it ought to be ever since. - Drop-span option (View/Options.../Drop multiple audio files into the same track) now takes into effect the current session snap-per-beat setting when concatenating multiple audio clips. - All plugins get their default bank/program selected on insertion. - Make record armed tracks muted for playback, a needed regression to let both audio and MIDI tracks behave consistently regarding input monitor switching through output. - Fixed a pretty old and shameless bug regarding MIDI clip recording, in which cases the queue start time offset was not taken into account, with captured events being recorded with erratic long delays. - Almost complete refactoring of the clumsy audio peak file classes, possibly making it even clumsier but straight nevertheless, with the noble and final aim to build it in-flight and draw the waveforms while recording is rolling. - Recording clips get their initial name labels drawn while fade-in/out handles are dropped as found pretty useless while recording. - Escape key now closes connections and mixer widgets as found usual. 0.2.0 2008-07-18 The Frolic Demoiselle - MIDI clip filename revision logic is now introduced, thus avoiding the proliferation of several numbered SMF's on each edit/save; some lurking bugs were exposed in the MIDI clip externalization method but promptly squashed. - Fixed a mouse release event glitch while in drag-and-drop items in the Files tree list widget. - A dummy plugin type option has been devised, just to bear with some troubled behavior of the lovely JUCETICE plugins (View/Options.../ Plugins/Experimental/Dummy VST plugin scan). Bad news are that all, yes all as in every native VST plugins, are indistinguishable from being just pure audio effects, either mono or stereo, whatever, and thus all being considered full-blown stereo VSTi instruments (which are the vast mainstream and rather interesting majority, nevertheless:). - The plugin selection dialog (Add Plugin...) now features the option whether the selected plugins should be activated on insert. - Mixer strip titles now have distinguishable type icons, either for audio or MIDI, and shown on all buses and track strips. - Major optimization breakthrough: muted audio tracks aren't streamed any longer, saving precious CPU cycles from decoding, resampling, pitch-shifting, time-stretching, plugin effects, whatever. - Incredible as it might be, audio/MIDI track record monitoring is now mixed (or merged) with rolling playback content, thus not as mutually exclusive between record and playback states anymore; also, track mute/solo states doesn't apply to recording material anymore. - Main form timer slot gets corrected and now independent of current process buffer-size in regard to JACK transport synchronization. - All file references in session state file are now stored as relative paths to main session directory. - DSSI/VSTi plugin presets can now be explicitly recalled from file (ie. Open/load preset) through this new tool button whether visible while in the generic plugin form. - Due to some trouble with newer Qt >= 4.4 applications regarding font size configuration, a new global user option is now available to the rescue: View/Options... /Display/Base font size (default is no-op). - Logarithmic scale is now taken into effect by control parameter sliders, in the generic plugin editor dialog as provided by LADSPA and DSSI plugin types. - MIDI track bank/program does not default to zero (PC#0) anymore. - Second attempt for Qt4.4 build support, regarding the bundled atomic primitives, now corrected and way more seriously :). - Long due DSSI/VSTi plugin host implementation has taken shape for MIDI instruments (eg. soft-synth plugins); DSSI reached its full host implementation and VSTi is already kicking as well. - DSSI/VSTi plugin presets may now be stored to external XML files, which should include all parameter values and configuration data chunks, taken as proper state snapshot and subject for recall. - Dedicated audio output bus option is now also accessible for all MIDI instrument plugins, either set globally as a default mode in View/Options.../Plugins/Instruments or in the plugin context menu for MIDI tracks, as an undoable command. - Fixed a potential crash-exception due to freeing a null-pointer, raised on some ever stringent platforms and while adding tracks to empty sessions, which is the same to say this was crashing more than always:). - Loop turn-around is now taken care of, as this has been found missing and causing noticeable gapping when un-muting or changing MIDI track events while in playback. - An off-by-one bug was fixed while inside MIDI cursor backward seek method, which was missing all other events that have the same exact onset timing. - Attempt to load Qt's own translation support and get rid of the ever warning startup message, unless built in debug mode. (transaction by Guido Scholz, while on qsynth-devel, thanks). - Only one application instance is now allowed to be up and running, with immediate but graceful termination upon startup iif an already running instance is detected, which will see its main widget shown up automatically (Qt/X11 platform only). - Clip fades have now a slight transparency. - Avoid loop read-ahead on initial audio clip loading. - Messages file logging makes its first long overdue appearance, with user configurable settings in View/Options.../Logging; options dialog was slightly rearranged and moved the Plugins section into a new tab page. - Audio/MIDI drifting correction was missing its own correct and due (re)initialization whenever playback is (re)started; also, MIDI metering synchronization has been fixed once again. - Fixed session cursor backward seeking, specially applicable when playback passes the end of overlapped clips. - Fixed potential crash when opening bogus audio files. - Time-stretch FIFO buffer implementation is now made generic, as template, fixing a destructor memory leak in the process. - Include legacy headers, stdlib.h and string.h, where necessary to build with stricter gcc/g++ >= 4.3. 0.1.3 2008-05-02 The Frugal Damsel - As one may find convenient sometimes, the global time display format (frames, time or BBT) may now be changed on the main transport time spin-box context menu. - Left-clicking on the track list number column now toggles all track content clip selection. - Prevent audio-buffer initialization mashups when editing short audio clips while playback is rolling and within clip region. - Audio peak files gets a bit simplified, dropping the peak frame count from its header; peak waveform graphics are now rendered as straight lines when over the end of audio file. - The drop-span option (View/Options.../Drop multiple audio files into the same track) now also applies when importing tracks (as in Track/Import Tracks/Audio...) to concatenate multiple audio clips into one and the same new track. - Audio and MIDI meter level colors are now user configurable (as global configuration options, View/Options.../Display/Meters) - First attempt for Qt4.4 build support, regarding the bundled atomic primitives, which have changed upstream as advertised (thanks to Paul Thomas, for spotting this one first time). - Record monitor switch is now an accessible button option on all track mixer strips; for visual consistency, the old bus "thru" switch button has been renamed to "monitor". - Force track-view position reset to origin on session close. - Fixed segfault on inserting an external file into files widget. - Mixer splitter sizes are now better saved/restored when closed. - Track record monitoring is now a state option, being toggled from the Track/State/Monitor menu; applies both to audio end MIDI tracks: when set all input will be pass-through to the current assigned output bus, including track plug-ins chain. - Session dialog gets split in its own tab components, between descriptive, time and view configuration ones. - Drifting correction among audio and MIDI engines is now back, but avoided while recording or should it be while looping? - Time-stretching percent value gets its semantics inverted, as thought consistent with ones general sense for relative stretching ie., lower to shrink and higher to make longer. this is a major up-side-down change and should affect all sessions saved with time-stretched audio clips. - Slack space in main tracks and MIDI clip editor views are now proportional to viewport width, leaving enough room for drag and moving content past the current session length, specially at the lower zoom levels. - Clip end time is now also shown on tool-tip. - When armed for recording, MIDI tracks are now monitored and filtered through their own output bus, thus having the same behavior as audio tracks; this also implies that all record armed tracks won't playback their current content material when recording is engaged and rolling; track mute and solo states are now honored on record monitoring. - Audio clip pitch-shifting makes its first appearance, with the optional help from Chris Cannam's RubberBand library. - A new MIDI editor tool is available: note/pitch randomize. - Avoid (re)setting the default session directory if a session cannot be open or loaded for some reason. - Another nastiness bites the dust: a subtle but progressive drifting has been swept away from the audio buffer looping; zero buffer flushing is now also taken into account, which was the cause for serious drifting on time-stretched clips. - A major digital audio processing bug was tamed: audio clip fade-in/outs are now linearly piece-wise applied, even at the clip edges, giving a much smoother rendering and thus mitigating the nasty click-and-pop artifacts that were in fact due to some early design optimization with a poor and sloppy implementation. 0.1.2 2008-03-23 The Frantic Dame - Session length fixed (yet again) while extend recording; also improved follow-playhead switching while playback/recording. - Whitespace sanitization gets leaner for all recorded filenames. - Run-time SSE optimization detection has been improved while on configure; additionally, IEEE 32bit float specific optimizations have also sneaked in. - SSE optimization is now featured over all audio monitoring, and most specially on audio bus buffering, lowering the CPU burden a bit while doing track and bus gain, pan, metering and mix-down. - Fixed MIDI clip move into new track, preserving the original channel, bank and program whenever possible. - Fixed session cursor seeking, specially regarding overlapped clips, once gain. - The MIDI editor gets new menu access to current MIDI clip track (see File/Track/Inputs, Outputs, Properties); selection of MIDI events has also been improved, specially regarding overlapped note events. - Clip split command enters the stage (see Edit/Clip/Split) about splitting the current (selected) clip at the current playhead position (red cursor line). - Creating new clips from scratch is now finally permitted (see Edit/Clip/New...); additionally, the clip properties dialog is now also allowing for changing the filename (and track/channel as special to MIDI clips). - Record armed tracks are now properly monitored and fed through their own output audio bus on mix-down, which includes plug-in effects processing. - The files widget get alternating coloured rows. - VST plug-in preset values are now being restored properly; individual parameter changes are now being queued for the also convenient undo/redo command pattern. - Some audio clip buffer-sync tweaks have sneaked in, improving and fixing the rendering of full-overlapped, integrally cached and/or offset clips altogether. - Stuffed one primordial shot on XInitThreads() at the main head, and let native VST plug-ins start behaving as they should, or not; this might be in fact problematic and dangerous for people who won't ever try the JUCE based plugins as from JUCETICE (http://www.anticore.org/jucetice), due to some broken locking mechanism in xcb; thanks anyway to mighty kRAkEn/gORe@JUCETICE for this precious hint and from who knows best. - True deterministic session length update has due fixed. - Track menu has new accessible actions: Track/Inputs - show current track input bus connections; Track/Outputs - show current track output bus connections; Track/State/Record - arm current track for recording; Track/State/Mute - mute current track; Track/State/Solo - solo current track; Track/Navigate/First - make current the first track; Track/Navigate/Previous - make current the previous track; Track/Navigate/Next - make current the next track; Track/Navigate/Last - make current the last track; Track/Move/Top - move current track to top; Track/Move/Up - move current track up; Track/Move/Down - move current track down; Track/Move/Bottom - move current track to bottom; - View menus have new accessible actions: View/Zoom/In - horizontal and vertical zoom-in (Ctrl +); View/Zoom/Out - horizontal and vertical zoom-out (Ctrl -); View/Zoom/Reset - reset both zoom levels to default; View/Snap - select current snap-per-beat setting; - Plug-in forms don't auto-open on session reload anymore. - Keyboard shortcuts icon item (Help/Shortcuts...) sneaks in. 0.1.1 2008-02-16 The Futile Duchess - After some great user demand, keyboard shortcuts are finally configurable, as found provisionally under Help/Shortcuts..., for the main application menu and for the MIDI editor as well. - Debian package gets SSE optimization disabled as default. - At least some transport actions get to be non auto-repeatable when pressed for much too long, as Play and Record, avoiding the tumbling imposed from the keyboard. - For the first time ever, jackd auto-start is now allowed (!). - OSC service support through liblo gets optional at configure time, now leading the way to proper DSSI plug-in hosting. - All plug-in widget controls count are now capped to one hundred. - Plugin paths setup is now made available on the options dialog, overriding each of respective default settings, as implicit from the LADSPA_PATH, DSSI_PATH and VST_PATH environment variables (see View/Options.../Display/Plugin Paths). - Clip fade-in/out lengths are now kept relative to tempo changes and also to clip offset and length changes (clip resizes). - Automatic time-stretching for all audio clips when session tempo changes, may now be disabled/enabled as a global session option (see View/Options.../Audio/Playback/Automatic time-stretching). - Double-clicking on an empty area (de)selects all clips on track. - MIDI capture (record) quantization is now an option, possibly handy for some jerky performance musicians, as the one found in myself ;) (see View/Options.../MIDI/Capture/Quantize). - The global options dialog (View/Options...) has seen its Display tab page being moved back and to the right. - Major rewrite of the plug-in infrastructure, adding primordial support for DSSI and native VST plug-in flavors. - Drag-and-drop of plug-in instances are now allowed intra- and inter-mixer strip chains, either on tracks or buses. - Turning track record off while recording is rolling was leaving the session in a inconsistent recording status, now fixed. - A random but instant crash upon audition/pre-listening player onset was hopefully fixed. 0.1.0 2008-01-05 The Frivolous Debutante. - Audio clip time-stretching makes its debut, with code adapted and refactored from the SoundTouch library, under the (L)GPL umbrella. - New "Options.../Audio/Playback/Quick seek time-stretching" global option, providing a quick seek mode (hierarchical search) while doing all audio buffer time-stretching. - Changing session tempo will automatically apply the corresponding time-stretch percent factor to all in-place audio clips. Audio clip dialog also includes a new time-stretch property setting. - Tempo changes are now affecting clip offsets correctly, keeping the clip offset constant in time units (ticks), as are clip start and length properties already. - Mixer splitter sizes are now properly saved/restored when hidden. - Extended multi-selection is now featured on the files widget; all drag and drop functionality has been almost completely rewritten. - SSE optimization is now enabled where available (via configure). - Options for having separate dedicated ports for the audition/pre- listening player output, audio metronome output, MIDI control input/output and MIDI metronome output, are now in place. - A brand new subtle option sneaks in, affecting the drag-and-drop of the main track-view: View/Options.../Drop multiple audio files into the same track, whether to drop multiple external files into new or existing track as concatenated audio clips. - The audio metronome makes its debut as an alternative to the MIDI existing one; parameters include bar and beat audio sample files, accessible from the View/Options.../Audio/Metronome dialog. - Files widget action refactorization; the files context menu gets its due item icons and a new menu item for direct audio player accessibility. - MIDI time resolution changes (ppqn, ticks per beat) now tries to keep all session MIDI clip times unchanged as far as possible. - MIDI channel volume and panning control change events, CC#7 and CC#10 respectively, are now rendered unfiltered on playback. - First rendition of the long due implementation of an audition or pre-listening audio player is now in place; the files window got this new play/stop control button on its lower-right corner. - Actual instrument definition note (keys) and controller names are now in effect on the MIDI editor, whenever applicable. - Fixed instrument bank selection method, "Bank MSB" (2), which was broken enough to never send the correct bank number. - Mouse-wheel horizontal scrolling is now accessible on every view, while pressing a modifier key (Shift or Ctrl). - New auto-backward transport option: when enabled the playhead will be reset backward automatically whenever transport stops playing. - A suicide-crash has been fixed while invoking the bus dialog from the respective mixer bus strip context menu. - Master (default) buses are always set to Duplex mode, being now an enforced update policy while on the Buses dialog. - A stupid lockup bug (infinite loop) was spotted on the track bus assignment method and squashed (thanks, lexridge). - New keyboard shortcuts for toggling the Connections tool (F8) and the mighty Mixer tool (F9) windows. - Avoid showing a context menu while right-clicking on time rulers. - Audio clip waveform drawing gets additional closing points. - It is now possible to change the length/size of a clip by dragging its left or right edges, while in the main track-view. Shift/Ctrl+drag will also time-stretch to the resulting audio clip length. - Another off-by-one mistake was corrected, which was causing audio clips to go out-of-sync on loop turnover boundary; also changed the loop turnaround strategy, now honoring already cached periods. - A race-condition has been mitigated in the audio-buffer thread, that was exposed and lead to sudden application freezing upon changing some composite audio clip commands. - Take absolute audio peak values only, making peak files a little bit shorter and hopefully faster to load and draw as waveforms. 0.0.9 2007-11-30 The Adolescence Prime - Drag-and-dropping of MIDI files without specific track or channel, into existing tracks, is now rejected. The drop operation is now allowed on the track-view blank area only, meaning the same as the complete MIDI file import into session. - Record actual MIDI clip length to last play-head position, instead of time of last event in the recorded sequence. - Connections item lists gets properly sorted, as intended. - Clear connection persistance once an explicit discconnection is issued on any of the (intrinsic) bus ports. - MIDI output buses now get the panning slider to spit out some GM system master balance (sysex) messages, being now enabled. - Mouse hovering on the clip fade-in/out handles, while in the main track-view, gets its long due cursor pointer feedback. - Fixed a off-by-one boundary issue on MIDI clip event playback, which were enqueuing duplicated MIDI events on every read-ahead output thread processing cycle (1 sec). - Transport menu and toolbar are now featured on the MIDI Editor. - Use actual session name when asking to save changed session. - Transport loop setting keyboard shortcuts swapped: Ctrl+L will set the loop immediately, and Shift+Ctrl+L will toggle on/off. - Changing MIDI event duration may now affect MIDI clip duration. - Alternate sharp-note color lines have returned to MIDI Editor canvas, as was the shadow color marking the end of MIDI clip. 0.0.8 2007-11-12 The Twelfth Tight - Bus context menu is now accessible from respective mixer strip. - Fixed a subtle crash-suicide issue when invoking the bus dialog with a double-click over the corresponding mixer bus strip; also fixed the sloppy ganguing mistake when changing mixer bus gain (volume) and panning values. - MIDI Omni mode (sort of) makes its entrance as a new MIDI track property, meaning that is now finally possible for the capture of any unfiltered MIDI channel event, without regard to the current channel assignment, which still applies for playback purposes. - Audio (pass-)through has been also implemented, now being a common and consistent property of both audio and MIDI buses, provided those are set in duplex mode (input and output). - Re-touched follow-playhead and continue-past-end tool icons, again to be a bit softer and not so bright as to hurt someones eyes. 0.0.7 2007-10-31 The Eleventh Tower - Suspend auto-follow-playhead while dragging or moving content over the main track and midi editor views; re-touched follow-playhead and new continue-past-end tool icons. - Playback is now forced complete full-stop whenever play-head goes behind the current session length and/or loop-end and the newer "continue past end" option is enabled from transport menu. - Tool/child windows position and size preservation fixed. - MIDI (pass-)through has been finally implemented, after several kind requests, it applies as a property of duplex-mode MIDI buses; this new setting is configurable from the View/Buses... dialog; also from respective new input bus mixer-strip button; when enabled, implies all incoming MIDI events at the input bus will pass-through unchanged to the corresponding output bus, as found useful just for direct monitoring one's performance without the help of any extra circumvent or kludgy connections. - Mouse cursor shape changes accordingly while hovering in header time-scale rulers, both in main track-view and MIDI editor. - Track-view clipboard paste action has been refactored, with the user interface consistent with the same functionality featured on the MIDI editor: the clipboard selection being pasted now floats at the mouse pointer and can be moved around before placed into its final position, either with the mouse or keyboard arrow keys. - A unitialized member variable bug affecting all MIDI clips has been discovered and squashed; this one has been lurking for quite some time and was causing wrong clip editing results, specially when tempo or meter differs between session and the MIDI file. - Keyboard step-moving is now allowed while pasting in MIDI editor. - Track-view clip selection can now be drag-moved into the void (bottom) view area creating brand new tracks automagically. - Losing focus resets all current keyboard step-moves in progress, affecting the main track-view and all MIDI editor as well. - As done before on the MIDI editor, the main track-view current selection may be step-wise moved using the keyboard arrow keys and the enter/insert keys for final placement; horizontal step movement is quantized according to current snap setting; vertical key-step movement is only allowed to selected clips belonging to one single and the same track. - Immediate session loop setting, accessible from the main menu. - Track properties dialog gets fixed again in its auto-size treat. - Another audio-buffer thread bug was scrubbed off, which was causing spurious and audible garbage on certain loop workloads. 0.0.6 2007-10-09 The Tenth Commencement - A bit more of precision is achieved over the metronome regular ticking and both the audio and MIDI monitor meters. - A rudimentary MIDI metronome is now in place; parameters, like MIDI channel, bar and beat accent note, velocity and duration, are readily configurable from the main menu, View/Options... /MIDI/Metronome dialog. - Track properties dialog now gets tightly auto-sized, depending whether its an audio or a MIDI track. - MIDI clips are now auto-extendable when adding or moving events beyond the clip length and while in the MIDI Editor, of course. - MIDI editor current selection can now be step-wise moved using the keyboard arrow keys and the enter/insert keys as for final placement; horizontal step movement is subordinated to current snap per beat setting, no less than unity; vertical step moves are obviously quantized to the next semitone. - Get configure to try and detect the correct qmake location and insert it the search order, so let the qt4 tools take precedence when --with-qt option is given and older qt3 ones coexist and are found located ahead in the PATH. - Drifting correction on audio and MIDI engines was seriously infected in some kind of snafu conception, as evidence lead to even worse drifting being detected to much of great despair, specially after recording and/or bouncing audio tracks from MIDI sequenced material. Credit must certainly go to Christian Schoenebeck on splatting this sloppy one on the face. - MIDI editing actions while playing now get immediate feedback; this was possible to some internal interface redesign of all MIDI editor accessory classes, making the MIDI clip now being the main editing target object instead of just the MIDI event sequence as it was previously. - Simple as it could ever be, the build executive summary report is now given on configure. - The internal decoded frame list for MPEG 1Audio Layer III (mp3) audio files (ie. via libmad) has been made one-time cached as global shared objects, benefitting from the fact that the list is always completely (re)built during the peak file computation, and thus speeding up all frame accurate access operations (seek) over this specific audio file format. - More eye-candy is sneaking in the MIDI editor: there's new view options on the View menu: Note Color and Value Color, affecting note event colors according to pitch and/or velocity. - New view option on the MIDI editor: on menu View/Note Duration switches whether events are shown proportional to their durations or as simple vertical candlesticks. - New snap-per-beat divisors are now available (Beat/3, 6, 12, 24 and 48), giving support for triplets for the very first time (after a heads-up suggested patch from Marko, thanks). 0.0.5 2007-09-08 The Ninth Hitch Nail - MIDI edit tools (quantize, transpose, normalize, randomize, resize) are all functional and ready for experimentalism; gets in its own top-level menu and form with named preset store and recall functionality. - Main form backward and rewind transport actions are now being immediately enabled when playing from the session (zero-time) start position. - Audio and MIDI export sneaks in and in form and accessible from the main Track menu. - MIDI track channel is now properly set on session (re)load; track background color changes was missing the alpha setting. - The mix-down buffering was fixed again, now taking multi-track overlapping clips into consideration (was a lot more broken since the recent glitch-looping fix). - Dirty MIDI clip editing control has been fixed but still somewhat hacky nevertheless. - First attempt on solving a nasty MIDI editor bug, which was quietly and severely crippling MIDI files while saving offset edited clips. - Session loop (re)setting is finally now an undoable command. - Yet another insidious bug has been swept away from under the carpet: once again on audio looping, some astonishing old and crappy session cursor seek-backward statement was lurking to be laid off. Gone now, simply as it is, growing old on this :) - A tremendous bug has been fixed: audio looping is now a little more glitch-free as the mix-down buffering was badly broken even since its primordial implementation. Rejoice. - Minor improvements on track-view cursor updates and visual tracking while recording. - MIDI editor windows get their keyboard accelerator/shortcuts back in business, whether opted as tools always on top or not. - The infamous "Keep tool windows always on top" global option is now infecting the Connections, Mixer, Plugin and MIDI Editor window instances with no probable regrets. To be used with discretion, of course. - Session update/initialization gets it clean on startup. - Range selection action (Ctrl+R) is now back in business with the added bonus of being accessible from all MIDI editor instances too. - Common edit-head and tail cursor positions are now under common control and display from all MIDI editor instances; the session loop-start/end points are now also shown on every time-line and share the same control behavior across all MIDI editor instances. 0.0.4 2007-07-19 The Eighth Wanderer - Main toolbar tempo spin-box gets loose from keyboard change tracking (Qt >= 4.3); custom spin-box compilation fix for Qt 4.1. - Illusive but nasty Connections/Patchbay item tooltip crash bug has been hopefully fixed (Qt >= 4.3); QComboBox::editTextChanged() signal replaces old QComboBox::textChanged(). - Combo-box setup history has been corrected on restore, which was discarding the very initial default (factory) contents. - Make debian package build depend on libqt4-dev; win32 console flag is back to qmake project file. - MIDI instrument selection (e.g. on track form) gets fixed and improved. - Sorting method for the connections port list has been refactored; potential crash bug fix on connections sorting method. - Messages class accessor methods constness fix. - Got rid of some autoconf redundand thingies on configure; late debian changelog update. - Desktop categories update: AudioVideo. - README correction. 0.0.3 2007-06-23 The Seventh Draught - Crash fix on the connections widget, was a matter of refactoring the refresh/clear slots. - Help menu added to MIDI editor form (redirected and same to main form anyway). - Clip properties form gets its proper sanity check, querying any existing clip editor whether its safe to apply the new settings. - Mixer sliders get their long due valued correction, hopefully. - Transport backward and play/stop are now made accessible from the MIDI editor widget, through their keyboard accelerator shortcuts (backspace and space, respectively). - Transport actions (play, rec, rew and ffwd) are now kept stable on a single point, instead of being scattered all over the main form code; transport visual feedback might get affected, specially regarding the MMC processing. - Application icon is now officially installed into ${prefix}/share/pixmaps. - Spec file is now a bit more openSUSE compliant; just made RPM requirements as exigent as the new debian ones. - Paste cursor is now properly preserved after leaving MIDI editor views. - Amazingly why this was not spotted before, the main application logo-icon has been downscaled to the 32x32 pixel standard icon size. - MIDI clip editor clipboard gets singleton status and is now shared on all MIDI editor instances. Similarly to the main track-view, Shift/Ctrl-left-clicking on the MIDI editor views sets the current session play-head position. - A desktop entry file has been included on install, at last. - Clips in main track-view get more info in the form of tooltips. - Major rounding fix to time-scaling and most specially on all those internal MIDI I/O methods. - Extended range selection from the time-ruler and key-list headers is now possible by click-and-drag the mouse pointer. - Play-head cursor is now also displayed and/or set position on all open MIDI clip editors time line view. As in the main application form, a new local follow play-head option is also featured on the MIDI editor view menu and toolbar. - All MIDI file save operations are now logged to main messages and files are added to the main files list view. - Initial debianization. - MIDI capture/record file format (SMF Format 0 or 1) is now an user option, introducing the new MIDI tab on the global View/Options dialog. - A bad old-time session cursor glitch has been apparently fixed. - Make sure the generic clip properties form is modal. - Major rewrite and adaptation to the session time-scale properties, making its way for a brand new command instance: the session-tempo command. - Changing the snap-per-beat combobox value on the main window toolbar does not make it as an undoable command anymore. - Long due MIDI clip editor integration has come to reality. - Major rewrite on the MIDI sequence file read/write methods, in preparation to the coming MIDI clip editor. - Status-bar session length label now gets rightly updated, while extended recording, of course. - Clip properties fade-in/out lengths, gets their old due constraints. - Main transport time display is now an editable custom spin-box; the Tempo (BPM) spin-box has seen new colors (green on black). - Transport time display format option adds the new choice of absolute frame number, alternative to previous time and BBT. - The frame-time based spin-boxes, on the clip properties form, were replaced with a new custom one, allowing for alternate frame, time and BBT input/display formats. - Time-scale helper class has been introduced. 0.0.2 2007-05-26 The Sixth Lord - Audio/MIDI connections gets slightly refactored, contributing for whole robustness, specially in case of incidental engine shutdown. - Mixer window gets a minimum default height bump. - Clip fade-in/out type changes have been properly fixed. - Complete refactoring of the command class pattern, making it now derived from QObject and not having a reference to the main form anymore. - Inoperative context menu event handler has been removed from the track-view. - MIDI sequence note-on event tracking now uses faster QHash class, instead of original QMap. - Off-by-one bug fix on MIDI track write method, while parsing co-incident note-on/off events in the wrong order and thus leaving note events with an invalid (zero) duration; obviously affecting MIDI recording in very special circumstances. - Minor and rather innocuous drop at this time in the MIDI event class structure: the flags member field. - Port connections now get their lines correctly drawn; strangely enough, the connection lines were being painted only for the parent client items, probably since the Qt4 migration (aka. Halloween files). - Early clip editing is in place (clip name, start, offset and length parameters, fade in/out length and type). - Some menu item text capitalization. 0.0.1 2007-05-07 The Filthy Fifth - Newer JACK 0.105.0 seems to bitch, probably correctly, about the return value of the process callback. Make it to bitch no more by ensuring the JACK client is always issuing the innocuous 0 (zero) return value. - Important fixes have been issued, affecting MIDI recording: MIDI sequence zero-time event insertion; MIDI file pending note-off processing on write-track method. - Qt4 migration complete. 0.0.0 2006-10-31 The Halloween Files qtractor-1.5.11/PaxHeaders/LICENSE0000644000000000000000000000013215124701674013530 xustar0030 mtime=1767080892.772263542 30 atime=1767080892.772263542 30 ctime=1767080892.772263542 qtractor-1.5.11/LICENSE0000644000175000001440000004310315124701674013521 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. qtractor-1.5.11/PaxHeaders/README0000644000000000000000000000013215124701674013403 xustar0030 mtime=1767080892.772263542 30 atime=1767080892.772263542 30 ctime=1767080892.772263542 qtractor-1.5.11/README0000644000175000001440000002320115124701674013371 0ustar00rncbcusersQtractor - An Audio/MIDI multi-track sequencer ---------------------------------------------- Qtractor is an audio/MIDI multi-track sequencer application written in C++ with the Qt framework [1]. Target platform is Linux, where the Jack Audio Connection Kit (JACK) for audio [2] and the Advanced Linux Sound Architecture (ALSA) for MIDI [3] are the main infrastructures to evolve as a fairly-featured Linux desktop audio workstation GUI, specially dedicated to the personal home-studio. Website: https://qtractor.org Project page: https://sourceforge.net/projects/qtractor Git repos: https://git.code.sf.net/p/qtractor/code https://github.com/rncbc/qtractor.git https://gitlab.com/rncbc/qtractor.git https://bitbucket.org/rncbc/qtractor.git Wiki: https://sourceforge.net/p/qtractor/wiki/ - static rendering: https://qtractor.org/doc - user manual & how-to's: https://download.sf.net/qtractor/qtractor-manual-and-howtos.epub https://download.sf.net/qtractor/qtractor-manual-and-howtos.pdf Weblog: https://www.rncbc.org Qtractor is free, open-source software, distributed under the terms of the GNU General Public License (GPL) [18] version 2 or later. Features -------- - Multi-track audio and MIDI sequencing and recording. - Developed on the Qt C++ application and UI framework [1]. - Uses JACK [2] for audio and ALSA [3] sequencer for MIDI as multimedia infrastructures. - Traditional multi-track tape recorder control paradigm. - Audio file formats support: OGG (via libvorbis [6]), MP3 (via libmad [7], playback only), WAV, FLAC, AIFF and many, many more (via libsndfile [4]). - Standard MIDI files support (format 0 and 1). - Non-destructive, non-linear editing. - Unlimited number of tracks per session/project. - Unlimited number of overlapping clips per track. - XML encoded session/project description files (SDI). - Point-and-click, multi-select, drag-and-drop interaction (drag, move, drop, cut, copy, paste, paste-repeat, delete, split, merge). - Unlimited undo/redo. - Built-in mixer and monitor controls. - Built-in connection patchbay control and persistence (a-la QjackCtl [19]). - LADSPA [5], DSSI [11], native VST(2), VST3 [12], LV2 [13] and CLAP [21] plug-ins support. - Unlimited number of plug-ins per track or bus. - Plug-in presets, programs and chunk/configurations support, including native VST FXB/FXP file support. - Unlimited audio/MIDI effect send/return inserts per track or bus. - Loop-recording/takes. - Audio/MIDI clip fade-in/out, cross-fade (linear, quadratic, cubic). - Audio/MIDI clip gain/volume, normalize, export. - Audio/MIDI track and plugin parameter automation (dynamic curves, sample&hold, linear and spline modes). - Audio clip time-stretching (WSOLA-like or via librubberband [9]), pitch-shifting (via librubberband [9]) and seamless sample-rate conversion (via libsamplerate [8]). - Audio/MIDI track export (mix-down, render, merge, freeze). - Audio/MIDI metronome bar/beat clicks. - Unlimited tempo/time-signature map. - Unlimited location/bar markers. - MIDI clip editor (matrix/piano roll). - MIDI instrument definitions (a-la Cakewalk(tm) (*.ins) [21]); SoundFont (*.sf2) and MIDI Names XML (*.midnam) files also supported. - MIDI controller mapping/learn/assignment (mixer and plug-in parameters). - MIDI system exclusive (SysEx) setups. - JACK transport sync master/slave. - JACK session support. - NSM (Non/New Session Management) support [15]. - MMC control surface enabled. - MIDI Clock, Song Position Pointer (SPP) support. - Configurable PC-keyboard and MIDI controller shortcuts. Requirements ------------ The software requirements for build and runtime are listed as follows: Mandatory: - Qt framework [1], C++ class library and tools for cross-platform application and UI development https://qt.io/ - JACK [2] Audio Connection Kit https://jackaudio.org/ - ALSA [3], Advanced Linux Sound Architecture https://www.alsa-project.org/ - libsndfile [4], C library for reading and writing files containing sampled sound http://www.mega-nerd.com/libsndfile/ - LADSPA [5], Linux Audio Developer's Simple Plugin API http://www.ladspa.org/ Optional (opted-in at build time): - libvorbis [6] (enc, file), Ogg Vorbis audio compression https://xiph.org/vorbis/ - libmad [7], High-quality MPEG audio decoder https://www.underbit.com/products/mad/ - libsamplerate [8], The secret rabbit code, C library for audio sample rate conversion http://www.mega-nerd.com/SRC/ - librubberband [9], Rubber Band Audio Time Stretcher, an audio time-stretching and pitch-shifting library https://breakfastquay.com/rubberband/ - liblo [10], Lightweight OSC implementation (needed for DSSI GUI [11] and/or NSM support [15]) http://liblo.sourceforge.net/ - DSSI [11], An API for soft synth plugins with custom user interfaces http://dssi.sourceforge.net/ - VST SDK [12], Steinberg's Virtual Studio Technology (see README.VST(3)) https://www.steinberg.net/ - LV2 [13], Audio Plugin Standard, the extensible successor of LADSPA http://lv2plug.in/ - liblilv [14], Lightweight LV2 host implementation stack https://drobilla.net/software/lilv https://drobilla.net/software/sratom https://drobilla.net/software/sord https://drobilla.net/software/serd - libaubio [16], a library for real time audio labelling https://aubio.org - CLAP [21], CLever Audio Plugin https://github.com/free-audio/clap Installation ------------ Unpack the tarball and in the extracted source directory: cmake [-DCMAKE_INSTALL_PREFIX=] -B build cmake --build build [--parallel ] and optionally, as root: [sudo] cmake --install build Note that the default installation path () is /usr/local . Configuration ------------- Qtractor holds its settings and configuration state per user, in a file located as $HOME/.config/rncbc.org/Qtractor.conf . Normally, there's no need to edit this file, as it is recreated and rewritten everytime qtractor is run. Bugs ---- Probably plenty still, Qtractor maybe considered on beta stage already. Support ------- Qtractor is free, Linux Audio [17] open source free software. For bug reports, feature requests, discussion forums, mailing lists, or any other matter related to the development of this piece of software, please use the Sourceforge project page (https://sourceforge.net/projects/qtractor). You can also find timely and closer contact information on my personal web site (https://www.rncbc.org). Acknowledgments --------------- The (out)dated Qtractor quick start guide and user manual for version 0.5.x has been authored by Seth Kenlon & Klaatu. (https://downloads.sourceforge.net/qtractor/qtractor-0.5.x-user-manual.pdf) The older Qtractor user manual for version 0.3.0 and before, have been co-authored by James Laco Hines and Stephen Doonan. (https://downloads.sourceforge.net/qtractor/qtractor-0.3.0-user-manual.pdf) Qtractor logo/icon is an original work of Andy Fitzsimon, borrowed from the public domain openclipart.org gallery. A special mention should also go to the translators of Qtractor (see TRANSLATORS) and of course, last but not least, to all the past, present and future contributors of the Qtractor Wiki. (https://sourceforge.net/p/qtractor/wiki/) Thank you all. References ---------- [1] Qt framework, C++ class library and tools for cross-platform application and UI development https://qt.io/ [2] JACK Audio Connection Kit https://jackaudio.org/ [3] ALSA, Advanced Linux Sound Architecture https://www.alsa-project.org/ [4] libsndfile, C library for reading and writing files containing sampled sound http://www.mega-nerd.com/libsndfile/ [5] LADSPA, Linux Audio Developer's Simple Plugin API http://www.ladspa.org/ [6] libvorbis (enc, file), Ogg Vorbis audio compression https://xiph.org/vorbis/ [7] libmad, High-quality MPEG audio decoder https://www.underbit.com/products/mad/ [8] libsamplerate, The secret rabbit code, C library for audio sample rate conversion http://www.mega-nerd.com/SRC/ [9] librubberband, Rubber Band Audio Time Stretcher, an audio time-stretching and pitch-shifting library https://breakfastquay.com/rubberband/ [10] liblo, Lightweight OSC implementation (needed for DSSI GUI support) http://liblo.sourceforge.net/ [11] DSSI, an API for soft synth plugins with custom user interfaces http://dssi.sourceforge.net/ [12] VST SDK, Steinberg's Virtual Studio Technology (see README.VST) https://www.steinberg.net/ [13] LV2, Audio Plugin Standard, the extensible successor of LADSPA https://lv2plug.in/ [14] liblilv, Lightweight LV2 host implementation stack (needed for LV2 support) https://drobilla.net/software/lilv/ https://drobilla.net/software/sratom/ https://drobilla.net/software/sord/ https://drobilla.net/software/serd/ [15] Non Session Management (NSM) (legacy) http://non.tuxfamily.org/nsm/ New Session Manager (NSM) https://new-session-manager.jackaudio.org/ [16] libaubio, a library for real time audio labelling https://aubio.org [17] Linux Audio consortium of libre software for audio-related work https://linuxaudio.org [18] GNU General Public License https://www.gnu.org/copyleft/gpl.html [19] QjackCtl - JACK Qt GUI Interface https://qjackctl.sourceforge.io http://qjackctl.sourceforge.net [20] Cakewalk, powerful and easy-to-use tools for Windows-based music and sound production http://www.cakewalk.com/ [21] CLAP, CLever Audio Plugin https://github.com/free-audio/clap Enjoy && have (lots of) fun. -- rncbc aka Rui Nuno Capela rncbc at rncbc dot org https://www.rncbc.org qtractor-1.5.11/PaxHeaders/README.VST20000644000000000000000000000013215124701674014140 xustar0030 mtime=1767080892.772263542 30 atime=1767080892.772263542 30 ctime=1767080892.772263542 qtractor-1.5.11/README.VST20000644000175000001440000000363715124701674014141 0ustar00rncbcusersNative VST2 plug-in support -------------------------- Building for native VST support is not that easy. To say the least, it might not work out of the box. First, due to licensing issues, you'll have to go through the nuisance and download yourself the VST SDK, from its site: Steinberg Media Technologies GmbH https://www.steinberg.net Steinberg 3rd Party Developers https://www.steinberg.net/en/company/developers.html It doesn't really matter much whether you pick an older VST 2.x or the latest VST 3.x version of the VST SDK, you need to pick one and just one only. You may need to download the later anyway, as the former might be included and distributed in that way only. But again, you're on your own. Once downloaded the VST SDK zip-archive, you'll have to unpack the pertinent header files, which are found under the respective folder: - VST SDK 2.4: vstsdk2.4/pluginterfaces/vst2.x/ aeffectx.h aeffect.h - VST SDK 3.x: VST_SDK/VST2_SDK/pluginterfaces/vst2.x/ aeffectx.h aeffect.h Just copy those couple of files to somewhere on your system. You may choose to copy those files into some of the system standard include directories (eg. /usr/local/include or /usr/include). That way, all will be handled automagically by the usual build steps. Otherwise, you'll need do supply the include path yourself, as in the following: cmake -DCONFIG_VST2SDK=/path/to/pluginterfaces/vst2.x ... Next step, once properly built, you'll need to tell where the actual native VST plug-ins can be found in your file system. The directories where plug-ins can be picked up by qtractor at run-time are specified respectively in the View/Options.../Display/Plugin Paths dialog. Last warning notice: leave that VST_PATH environment variable alone. That variable is most precious for DSSI-VST though, in finding those Windows(tm) VSTi plug-ins (.dll's) on its own. Have a glass of WINE and relax ;) Enjoy. qtractor-1.5.11/PaxHeaders/TRANSLATORS0000644000000000000000000000013215124701674014262 xustar0030 mtime=1767080892.772263542 30 atime=1767080892.772263542 30 ctime=1767080892.772263542 qtractor-1.5.11/TRANSLATORS0000644000175000001440000000105515124701674014253 0ustar00rncbcusersCzech (cs) Pavel Fric German (de) Guido Scholz Spanish (es) David Reyes Pucheta French (fr) Yann Collette Olivier Humbert Italian (it) Massimo Callegari Japanese (ja) Takashi Sakamoto Portuguese (pt_BR) Esteban Viveros Russian (ru) Alexandre Prokoudine Ukranian (uk) Yuri Chornoivan qtractor-1.5.11/PaxHeaders/src0000644000000000000000000000013215124701674013235 xustar0030 mtime=1767080892.816263357 30 atime=1767080892.773458803 30 ctime=1767080892.816263357 qtractor-1.5.11/src/0000755000175000001440000000000015124701674013302 5ustar00rncbcusersqtractor-1.5.11/src/PaxHeaders/qtractorActionControl.h0000644000000000000000000000013215124701674020021 xustar0030 mtime=1767080892.777862586 30 atime=1767080892.777862586 30 ctime=1767080892.777862586 qtractor-1.5.11/src/qtractorActionControl.h0000644000175000001440000000501215124701674020007 0ustar00rncbcusers// qtractorActionControl.h // /**************************************************************************** Copyright (C) 2005-2022, 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 __qtractorActionControl_h #define __qtractorActionControl_h #include "qtractorMidiControlObserver.h" #include //---------------------------------------------------------------------- // class qtractorActionControl -- (QAction) MIDI observers map. // class qtractorActionControl : public QObject { Q_OBJECT public: // ctor. qtractorActionControl(QObject *pParent = nullptr); // dtor. ~qtractorActionControl(); // MIDI observers interface. class MidiObserver : public qtractorMidiControlObserver { public: MidiObserver(QAction *pAction); protected: void update(bool bUpdate); private: QAction *m_pAction; qtractorSubject m_subject; }; // MIDI observers map accessor. typedef QHash MidiObservers; const MidiObservers& midiObservers() const { return m_midiObservers; } // MIDI observers map methods. MidiObserver *getMidiObserver(QAction *pAction); MidiObserver *addMidiObserver(QAction *pAction); void removeMidiObserver(QAction *pAction); // MIDI observers map cleaner. void clear(); // Pseudo-singleton instance accessor. static qtractorActionControl *getInstance(); // Complete action text, from associated menus. static QString menuActionText(QAction *pAction, const QString& sText); protected slots: // MIDI observer trigger slot. void triggeredSlot(bool bOn); private: // MIDI observers map. MidiObservers m_midiObservers; // Pseudo-singleton instance. static qtractorActionControl *g_pActionControl; }; #endif // __qtractorActionControl_h // end of qtractorActionControl.h qtractor-1.5.11/src/PaxHeaders/qtractorPluginSelectForm.h0000644000000000000000000000013215124701674020465 xustar0030 mtime=1767080892.799263428 30 atime=1767080892.799263428 30 ctime=1767080892.799263428 qtractor-1.5.11/src/qtractorPluginSelectForm.h0000644000175000001440000000412615124701674020460 0ustar00rncbcusers// qtractorPluginSelectForm.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorPluginSelectForm_h #define __qtractorPluginSelectForm_h #include "ui_qtractorPluginSelectForm.h" #include "qtractorPlugin.h" //---------------------------------------------------------------------------- // qtractorPluginSelectForm -- UI wrapper form. class qtractorPluginSelectForm : public QDialog { Q_OBJECT public: // Constructor. qtractorPluginSelectForm(QWidget *pParent = nullptr); // Destructor. ~qtractorPluginSelectForm(); void setPluginList(qtractorPluginList *pPluginList); qtractorPluginList *pluginList() const; int pluginCount() const; QString pluginFilename(int iPlugin) const; unsigned long pluginIndex(int iPlugin) const; qtractorPluginType::Hint pluginTypeHint(int iPlugin) const; protected slots: void typeHintChanged(int iTypeHint); void reset(); void rescan(); void refresh(); void scanned(int iPercent); void stabilize(); void accept(); private: // The Qt-designer UI struct... Ui::qtractorPluginSelectForm m_ui; qtractorPluginList *m_pPluginList; QList m_selectedItems; }; #endif // __qtractorPluginSelectForm_h // end of qtractorPluginSelectForm.h qtractor-1.5.11/src/PaxHeaders/qtractorOptionsForm.cpp0000644000000000000000000000013215124701674020055 xustar0030 mtime=1767080892.797263437 30 atime=1767080892.796263441 30 ctime=1767080892.797263437 qtractor-1.5.11/src/qtractorOptionsForm.cpp0000644000175000001440000023524115124701674020054 0ustar00rncbcusers// qtractorOptionsForm.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorOptionsForm.h" #include "qtractorAbout.h" #include "qtractorOptions.h" #include "qtractorAudioFile.h" #include "qtractorMidiTimer.h" #include "qtractorMidiEditor.h" #include "qtractorTimeScale.h" #include "qtractorSession.h" #include "qtractorClip.h" #include "qtractorPluginFactory.h" #include "qtractorAudioMeter.h" #include "qtractorMidiMeter.h" #include "qtractorPluginSelectForm.h" #include "qtractorPaletteForm.h" #include #include #include #include #include #include #include #include // Needed for logf() and powf() #include static inline float log10f2 ( float x ) { return (x > 0.0f ? 20.0f * ::log10f(x) : -60.0f); } static inline float pow10f2 ( float x ) { return ::powf(10.0f, 0.05f * x); } //---------------------------------------------------------------------------- // Available session formats/ext-suffixes. static QHash g_sessionFormats; void qtractorOptionsForm::initSessionFormats (void) { static struct SessionFormat { const char *name; const char *ext; } s_aSessionFormats[] = { { QT_TR_NOOP("XML Default (*.%1)"), "qtr" }, { QT_TR_NOOP("XML Regular (*.%1)"), "qts" }, { QT_TR_NOOP("ZIP Archive (*.%1)"), "qtz" }, { nullptr, nullptr } }; if (g_sessionFormats.isEmpty()) { for (int i = 0; s_aSessionFormats[i].name; ++i) { SessionFormat& sf = s_aSessionFormats[i]; g_sessionFormats.insert(sf.ext, tr(sf.name)); } } } // Default (empty/blank) name. static const char *g_pszDefName = QT_TRANSLATE_NOOP("qtractorOptionsForm", "(default)"); //---------------------------------------------------------------------------- // qtractorOptionsForm -- UI wrapper form. // Constructor. qtractorOptionsForm::qtractorOptionsForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); #if QT_VERSION < QT_VERSION_CHECK(6, 1, 0) QDialog::setWindowIcon(QIcon(":/images/qtractor.png")); #endif // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::ApplicationModal); // No settings descriptor initially (the caller will set it). m_pOptions = nullptr; // Have some deafult time-scale for instance... m_pTimeScale = nullptr; // Meter colors array setup. m_paAudioMeterColors = new QColor [qtractorAudioMeter::ColorCount - 1]; m_paMidiMeterColors = new QColor [qtractorMidiMeter::ColorCount - 1]; int iColor; for (iColor = 0; iColor < qtractorAudioMeter::ColorCount - 1; ++iColor) m_paAudioMeterColors[iColor] = qtractorAudioMeter::defaultColor(iColor); for (iColor = 0; iColor < qtractorMidiMeter::ColorCount - 1; ++iColor) m_paMidiMeterColors[iColor] = qtractorMidiMeter::defaultColor(iColor); m_iDirtyMeterColors = 0; // Time-scale and audio metronome setup. qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { m_pTimeScale = new qtractorTimeScale(*pSession->timeScale()); m_ui.AudioMetroOffsetSpinBox->setTimeScale(m_pTimeScale); m_ui.AudioMetroOffsetSpinBox->setMaximum(m_pTimeScale->sampleRate()); m_ui.AudioMetroOffsetSpinBox->setDeltaValue(true); } #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) // Some conveniency cleaner helpers... m_ui.SessionTemplatePathComboBox->lineEdit()->setClearButtonEnabled(true); m_ui.MetroBarFilenameComboBox->lineEdit()->setClearButtonEnabled(true); m_ui.MetroBeatFilenameComboBox->lineEdit()->setClearButtonEnabled(true); m_ui.MessagesLogPathComboBox->lineEdit()->setClearButtonEnabled(true); m_ui.PluginPathComboBox->lineEdit()->setClearButtonEnabled(true); m_ui.PluginBlacklistComboBox->lineEdit()->setClearButtonEnabled(true); m_ui.Lv2PresetDirComboBox->lineEdit()->setClearButtonEnabled(true); #endif // Populate the session format combo-box. initSessionFormats(); m_ui.SessionFormatComboBox->clear(); QHash::ConstIterator sf_iter = g_sessionFormats.constBegin(); const QHash::ConstIterator& sf_end = g_sessionFormats.constEnd(); int iSessionFormat = 0; for ( ; sf_iter != sf_end; ++sf_iter) { const QString& sSessionExt = sf_iter.key(); const QString& sSessionFormat = sf_iter.value(); m_ui.SessionFormatComboBox->insertItem( iSessionFormat++, sSessionFormat.arg(sSessionExt), sSessionExt); } // Populate the fade-in/out types combo-boxes. const qtractorClip::FadeTypes& fadeTypes = qtractorClip::fadeTypes(); for (int i = 0; i < fadeTypes.count(); ++i) { const qtractorClip::FadeTypeInfo& info = fadeTypes.value(i); m_ui.ClipFadeInTypeComboBox->addItem(info.iconFadeIn, info.name); m_ui.ClipFadeOutTypeComboBox->addItem(info.iconFadeOut, info.name); } // Populate the MIDI capture quantize combo-box. const QIcon& snapIcon = QIcon::fromTheme("itemBeat"); const QStringList& snapItems = qtractorTimeScale::snapItems(); QStringListIterator snapIter(snapItems); m_ui.MidiCaptureQuantizeComboBox->clear(); m_ui.MidiCaptureQuantizeComboBox->setIconSize(QSize(8, 16)); // snapIter.toFront(); if (snapIter.hasNext()) m_ui.MidiCaptureQuantizeComboBox->addItem( QIcon::fromTheme("itemNone"), snapIter.next()); while (snapIter.hasNext()) m_ui.MidiCaptureQuantizeComboBox->addItem(snapIcon, snapIter.next()); // m_ui.MidiCaptureQuantizeComboBox->insertItems(0, items); // Populate the MMC device combo-box. m_ui.MidiMmcDeviceComboBox->clear(); for (unsigned char mmcDevice = 0; mmcDevice < 0x7f; ++mmcDevice) m_ui.MidiMmcDeviceComboBox->addItem(QString::number(int(mmcDevice))); m_ui.MidiMmcDeviceComboBox->addItem(tr("(Any)")); // updateMetroNoteNames(); // Plugin path types... m_ui.PluginTypeComboBox->clear(); #ifdef CONFIG_LADSPA m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Ladspa)); #endif #ifdef CONFIG_DSSI m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Dssi)); #endif #ifdef CONFIG_VST2 m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Vst2)); #endif #ifdef CONFIG_VST3 m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Vst3)); #endif #ifdef CONFIG_CLAP m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Clap)); #endif #ifdef CONFIG_LV2 m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Lv2)); #endif #ifndef CONFIG_LV2_PRESETS m_ui.Lv2PresetDirLabel->hide(); m_ui.Lv2PresetDirComboBox->hide(); m_ui.Lv2PresetDirToolButton->hide(); #endif // Initialize dirty control state. m_iDirtyCount = 0; m_iDirtyCustomColorThemes = 0; m_iDirtyLadspaPaths = 0; m_iDirtyDssiPaths = 0; m_iDirtyVst2Paths = 0; m_iDirtyVst3Paths = 0; m_iDirtyClapPaths = 0; m_iDirtyLv2Paths = 0; m_iDirtyBlacklist = 0; // Try to restore old window positioning. adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.AudioCaptureTypeComboBox, SIGNAL(activated(int)), SLOT(audioCaptureTypeChanged(int))); QObject::connect(m_ui.AudioCaptureFormatComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.AudioCaptureQualitySpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioResampleTypeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.TransportModeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.TimebaseCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioAutoTimeStretchCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioWsolaTimeStretchCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioWsolaQuickSeekCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); #ifdef CONFIG_LIBRUBBERBAND QObject::connect(m_ui.AudioRubberBandFormantCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); #ifdef CONFIG_LIBRUBBERBAND_R3 QObject::connect(m_ui.AudioRubberBandFinerR3CheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); #endif #endif QObject::connect(m_ui.AudioPlayerBusCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioPlayerAutoConnectCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioSelfConnectedCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioMetronomeCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioCountInModeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.AudioCountInBeatsSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.MetroBarFilenameComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.MetroBarFilenameToolButton, SIGNAL(clicked()), SLOT(chooseMetroBarFilename())); QObject::connect(m_ui.MetroBarGainSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.MetroBeatFilenameComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.MetroBeatFilenameToolButton, SIGNAL(clicked()), SLOT(chooseMetroBeatFilename())); QObject::connect(m_ui.MetroBeatGainSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.AudioMetroBusCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioMetroAutoConnectCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioMetroOffsetSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(changed())); QObject::connect(m_ui.MidiCaptureFormatComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MidiCaptureQuantizeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MidiQueueTimerComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MidiDriftCorrectCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.MidiPlayerBusCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.MidiResetAllControllersCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.MidiMmcModeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MidiMmcDeviceComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MidiSppModeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MidiClockModeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MidiControlBusCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.MidiMetronomeCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.MidiCountInModeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MidiCountInBeatsSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.MetroChannelSpinBox, SIGNAL(valueChanged(int)), SLOT(updateMetroNoteNames())); QObject::connect(m_ui.MetroBarNoteComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MetroBarVelocitySpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.MetroBarDurationSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.MetroBeatNoteComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MetroBeatVelocitySpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.MetroBeatDurationSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.MidiMetroBusCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.MidiMetroOffsetSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.ConfirmRemoveCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.ConfirmArchiveCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.StdoutCaptureCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.CompletePathCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.PeakAutoRemoveCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.KeepToolsOnTopCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.KeepEditorsOnTopCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.TrackViewDropSpanCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.ShiftKeyModifierCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.MidButtonModifierCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.LoopRecordingModeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.DisplayFormatComboBox, SIGNAL(activated(int)), SLOT(displayFormatChanged(int))); QObject::connect(m_ui.ClipFadeInTypeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.ClipFadeOutTypeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MaxRecentFilesSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.BaseFontSizeComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.SessionFormatComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.SessionTemplateCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.SessionTemplatePathComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.SessionTemplatePathToolButton, SIGNAL(clicked()), SLOT(chooseSessionTemplatePath())); QObject::connect(m_ui.SessionBackupCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.SessionBackupModeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.SessionAutoSaveCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.SessionAutoSaveSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.CustomColorThemeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.CustomColorThemeToolButton, SIGNAL(clicked()), SLOT(editCustomColorThemes())); QObject::connect(m_ui.CustomStyleThemeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.CustomStyleSheetComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.CustomStyleSheetToolButton, SIGNAL(clicked()), SLOT(chooseCustomStyleSheet())); QObject::connect(m_ui.CustomIconsThemeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.CustomIconsThemeToolButton, SIGNAL(clicked()), SLOT(chooseCustomIconsTheme())); QObject::connect(m_ui.AudioMeterLevelComboBox, SIGNAL(activated(int)), SLOT(changeAudioMeterLevel(int))); QObject::connect(m_ui.MidiMeterLevelComboBox, SIGNAL(activated(int)), SLOT(changeMidiMeterLevel(int))); QObject::connect(m_ui.AudioMeterColorLineEdit, SIGNAL(textChanged(const QString&)), SLOT(changeAudioMeterColor(const QString&))); QObject::connect(m_ui.MidiMeterColorLineEdit, SIGNAL(textChanged(const QString&)), SLOT(changeMidiMeterColor(const QString&))); QObject::connect(m_ui.AudioMeterColorToolButton, SIGNAL(clicked()), SLOT(chooseAudioMeterColor())); QObject::connect(m_ui.MidiMeterColorToolButton, SIGNAL(clicked()), SLOT(chooseMidiMeterColor())); QObject::connect(m_ui.ResetMeterColorsPushButton, SIGNAL(clicked()), SLOT(resetMeterColors())); QObject::connect(m_ui.PluginTypeComboBox, SIGNAL(activated(int)), SLOT(choosePluginType(int))); QObject::connect(m_ui.PluginPathComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changePluginPath(const QString&))); QObject::connect(m_ui.PluginPathToolButton, SIGNAL(clicked()), SLOT(choosePluginPath())); QObject::connect(m_ui.PluginPathAddToolButton, SIGNAL(clicked()), SLOT(addPluginPath())); QObject::connect(m_ui.PluginPathListWidget, SIGNAL(itemSelectionChanged()), SLOT(selectPluginPath())); QObject::connect(m_ui.PluginPathRemoveToolButton, SIGNAL(clicked()), SLOT(removePluginPath())); QObject::connect(m_ui.PluginPathUpToolButton, SIGNAL(clicked()), SLOT(moveUpPluginPath())); QObject::connect(m_ui.PluginPathDownToolButton, SIGNAL(clicked()), SLOT(moveDownPluginPath())); #ifdef CONFIG_LV2_PRESETS QObject::connect(m_ui.Lv2PresetDirComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.Lv2PresetDirToolButton, SIGNAL(clicked()), SLOT(chooseLv2PresetDir())); #endif QObject::connect(m_ui.AudioOutputBusCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioOutputAutoConnectCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.OpenEditorCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.QueryEditorTypeCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.PluginBlacklistComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changePluginBlacklist(const QString&))); QObject::connect(m_ui.PluginBlacklistToolButton, SIGNAL(clicked()), SLOT(choosePluginBlacklist())); QObject::connect(m_ui.PluginBlacklistAddToolButton, SIGNAL(clicked()), SLOT(addPluginBlacklist())); QObject::connect(m_ui.PluginBlacklistWidget, SIGNAL(itemSelectionChanged()), SLOT(selectPluginBlacklist())); QObject::connect(m_ui.PluginBlacklistRemoveToolButton, SIGNAL(clicked()), SLOT(removePluginBlacklist())); QObject::connect(m_ui.PluginBlacklistClearToolButton, SIGNAL(clicked()), SLOT(clearPluginBlacklist())); QObject::connect(m_ui.MessagesFontPushButton, SIGNAL(clicked()), SLOT(chooseMessagesFont())); QObject::connect(m_ui.MessagesLimitCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.MessagesLimitLinesSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.MessagesLogCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.MessagesLogPathComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.MessagesLogPathToolButton, SIGNAL(clicked()), SLOT(chooseMessagesLogPath())); QObject::connect(m_ui.SyncViewHoldCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.UseNativeDialogsCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.TrackColorSaturationSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); } // Destructor. qtractorOptionsForm::~qtractorOptionsForm (void) { delete [] m_paMidiMeterColors; delete [] m_paAudioMeterColors; if (m_pTimeScale) delete m_pTimeScale; } // Populate (setup) dialog controls from settings descriptors. void qtractorOptionsForm::setOptions ( qtractorOptions *pOptions ) { // Set reference descriptor. m_pOptions = pOptions; // Initialize conveniency options... m_pOptions->loadComboBoxHistory(m_ui.MetroBarFilenameComboBox); m_pOptions->loadComboBoxHistory(m_ui.MetroBeatFilenameComboBox); m_pOptions->loadComboBoxFileHistory(m_ui.CustomStyleSheetComboBox); m_pOptions->loadComboBoxFileHistory(m_ui.CustomIconsThemeComboBox); m_pOptions->loadComboBoxHistory(m_ui.PluginPathComboBox); m_pOptions->loadComboBoxHistory(m_ui.MessagesLogPathComboBox); m_pOptions->loadComboBoxHistory(m_ui.SessionTemplatePathComboBox); m_pOptions->loadComboBoxHistory(m_ui.Lv2PresetDirComboBox); m_pOptions->loadComboBoxHistory(m_ui.PluginBlacklistComboBox); // Time-scale related options... const qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(m_pOptions->iDisplayFormat); if (m_pTimeScale) m_pTimeScale->setDisplayFormat(displayFormat); // Audio options. m_ui.AudioCaptureTypeComboBox->setCurrentType( m_pOptions->sAudioCaptureExt, m_pOptions->iAudioCaptureType); m_ui.AudioCaptureFormatComboBox->setCurrentIndex(m_pOptions->iAudioCaptureFormat); m_ui.AudioCaptureQualitySpinBox->setValue(m_pOptions->iAudioCaptureQuality); m_ui.AudioResampleTypeComboBox->setCurrentIndex(m_pOptions->iAudioResampleType); m_ui.TransportModeComboBox->setCurrentIndex(m_pOptions->iTransportMode); m_ui.TimebaseCheckBox->setChecked(m_pOptions->bTimebase); m_ui.AudioAutoTimeStretchCheckBox->setChecked(m_pOptions->bAudioAutoTimeStretch); #ifdef CONFIG_LIBRUBBERBAND m_ui.AudioWsolaTimeStretchCheckBox->setChecked(m_pOptions->bAudioWsolaTimeStretch); m_ui.AudioRubberBandFormantCheckBox->setChecked(m_pOptions->bAudioRubberBandFormant); #ifdef CONFIG_LIBRUBBERBAND_R3 m_ui.AudioRubberBandFinerR3CheckBox->setChecked(m_pOptions->bAudioRubberBandFinerR3); #else m_ui.AudioRubberBandFinerR3CheckBox->hide(); #endif #else m_ui.AudioWsolaTimeStretchCheckBox->setChecked(true); m_ui.AudioWsolaTimeStretchCheckBox->setEnabled(false); m_ui.AudioRubberBandFormantCheckBox->hide(); m_ui.AudioRubberBandFinerR3CheckBox->hide(); #endif m_ui.AudioWsolaQuickSeekCheckBox->setChecked(m_pOptions->bAudioWsolaQuickSeek); m_ui.AudioPlayerBusCheckBox->setChecked(m_pOptions->bAudioPlayerBus); m_ui.AudioPlayerAutoConnectCheckBox->setChecked(m_pOptions->bAudioPlayerAutoConnect); m_ui.AudioSelfConnectedCheckBox->setChecked(m_pOptions->bAudioSelfConnected); #ifndef CONFIG_LIBSAMPLERATE m_ui.AudioResampleTypeTextLabel->setEnabled(false); m_ui.AudioResampleTypeComboBox->setEnabled(false); #endif // Have some audio metronome default sample files... // const QChar sep = QDir::separator(); QString sAudioPath = QApplication::applicationDirPath(); sAudioPath.remove(CONFIG_BINDIR); sAudioPath.append(CONFIG_DATADIR); sAudioPath.append(sep); sAudioPath.append(PROJECT_NAME); sAudioPath.append(sep); sAudioPath.append("audio"); const QFileInfo metro_bar(sAudioPath, "metro_bar.wav"); if (metro_bar.isFile() && metro_bar.isReadable()) { const QString& sMetroBarFilename = metro_bar.absoluteFilePath(); if (m_pOptions->sMetroBarFilename.isEmpty()) m_pOptions->sMetroBarFilename = sMetroBarFilename; if (m_ui.MetroBarFilenameComboBox->findText(sMetroBarFilename) < 0) m_ui.MetroBarFilenameComboBox->addItem(sMetroBarFilename); } const QFileInfo metro_beat(sAudioPath, "metro_beat.wav"); if (metro_beat.isFile() && metro_beat.isReadable()) { const QString& sMetroBeatFilename = metro_beat.absoluteFilePath(); if (m_pOptions->sMetroBeatFilename.isEmpty()) m_pOptions->sMetroBeatFilename = sMetroBeatFilename; if (m_ui.MetroBeatFilenameComboBox->findText(sMetroBeatFilename) < 0) m_ui.MetroBeatFilenameComboBox->addItem(sMetroBeatFilename); } // Audio metronome options. m_ui.AudioMetronomeCheckBox->setChecked(m_pOptions->bAudioMetronome); m_ui.AudioCountInModeComboBox->setCurrentIndex(m_pOptions->iAudioCountInMode); m_ui.AudioCountInBeatsSpinBox->setValue(m_pOptions->iAudioCountInBeats); m_ui.MetroBarFilenameComboBox->setEditText(m_pOptions->sMetroBarFilename); m_ui.MetroBarGainSpinBox->setValue(log10f2(m_pOptions->fMetroBarGain)); m_ui.MetroBeatFilenameComboBox->setEditText(m_pOptions->sMetroBeatFilename); m_ui.MetroBeatGainSpinBox->setValue(log10f2(m_pOptions->fMetroBeatGain)); m_ui.AudioMetroBusCheckBox->setChecked(m_pOptions->bAudioMetroBus); m_ui.AudioMetroAutoConnectCheckBox->setChecked(m_pOptions->bAudioMetroAutoConnect); m_ui.AudioMetroOffsetSpinBox->setDisplayFormat(displayFormat); m_ui.AudioMetroOffsetSpinBox->setValue(m_pOptions->iAudioMetroOffset); // MIDI capture/export options. m_ui.MidiCaptureFormatComboBox->setCurrentIndex(m_pOptions->iMidiCaptureFormat); m_ui.MidiCaptureQuantizeComboBox->setCurrentIndex(m_pOptions->iMidiCaptureQuantize); // MIDI playback options. qtractorMidiTimer timer; m_ui.MidiQueueTimerComboBox->clear(); for (int i = 0; i < timer.count(); ++i) m_ui.MidiQueueTimerComboBox->addItem(timer.name(i), timer.key(i)); m_ui.MidiQueueTimerComboBox->setCurrentIndex( timer.indexOf(m_pOptions->iMidiQueueTimer)); m_ui.MidiDriftCorrectCheckBox->setChecked(m_pOptions->bMidiDriftCorrect); m_ui.MidiPlayerBusCheckBox->setChecked(m_pOptions->bMidiPlayerBus); m_ui.MidiResetAllControllersCheckBox->setChecked(m_pOptions->bMidiResetAllControllers); // MIDI control options. m_ui.MidiMmcModeComboBox->setCurrentIndex(m_pOptions->iMidiMmcMode); m_ui.MidiMmcDeviceComboBox->setCurrentIndex(m_pOptions->iMidiMmcDevice); m_ui.MidiSppModeComboBox->setCurrentIndex(m_pOptions->iMidiSppMode); m_ui.MidiClockModeComboBox->setCurrentIndex(m_pOptions->iMidiClockMode); m_ui.MidiControlBusCheckBox->setChecked(m_pOptions->bMidiControlBus); // MIDI metronome options. m_ui.MidiMetronomeCheckBox->setChecked(m_pOptions->bMidiMetronome); m_ui.MidiCountInModeComboBox->setCurrentIndex(m_pOptions->iMidiCountInMode); m_ui.MidiCountInBeatsSpinBox->setValue(m_pOptions->iMidiCountInBeats); m_ui.MetroChannelSpinBox->setValue(m_pOptions->iMetroChannel + 1); updateMetroNoteNames(); m_ui.MetroBarNoteComboBox->setCurrentIndex(m_pOptions->iMetroBarNote); m_ui.MetroBarVelocitySpinBox->setValue(m_pOptions->iMetroBarVelocity); m_ui.MetroBarDurationSpinBox->setValue(m_pOptions->iMetroBarDuration); m_ui.MetroBeatNoteComboBox->setCurrentIndex(m_pOptions->iMetroBeatNote); m_ui.MetroBeatVelocitySpinBox->setValue(m_pOptions->iMetroBeatVelocity); m_ui.MetroBeatDurationSpinBox->setValue(m_pOptions->iMetroBeatDuration); m_ui.MidiMetroBusCheckBox->setChecked(m_pOptions->bMidiMetroBus); m_ui.MidiMetroOffsetSpinBox->setValue(m_pOptions->iMidiMetroOffset); // Meter colors array setup. int iColor; for (iColor = 0; iColor < qtractorAudioMeter::ColorCount - 1; ++iColor) m_paAudioMeterColors[iColor] = qtractorAudioMeter::color(iColor); for (iColor = 0; iColor < qtractorMidiMeter::ColorCount - 1; ++iColor) m_paMidiMeterColors[iColor] = qtractorMidiMeter::color(iColor); // Default meter color levels shown... m_ui.AudioMeterLevelComboBox->setCurrentIndex(qtractorAudioMeter::Color10dB); m_ui.MidiMeterLevelComboBox->setCurrentIndex(qtractorMidiMeter::ColorOver); changeAudioMeterLevel(m_ui.AudioMeterLevelComboBox->currentIndex()); changeMidiMeterLevel(m_ui.MidiMeterLevelComboBox->currentIndex()); // Custom display options... resetCustomColorThemes(m_pOptions->sCustomColorTheme); resetCustomStyleThemes(m_pOptions->sCustomStyleTheme); m_pOptions->setComboBoxCurrentFile( m_ui.CustomStyleSheetComboBox, m_pOptions->sCustomStyleSheet); m_pOptions->setComboBoxCurrentFile( m_ui.CustomIconsThemeComboBox, m_pOptions->sCustomIconsTheme); // Load Display options... QFont font; // Messages font. if (m_pOptions->sMessagesFont.isEmpty() || !font.fromString(m_pOptions->sMessagesFont)) font = QFont("Monospace", 8); QPalette pal(m_ui.MessagesFontTextLabel->palette()); pal.setColor(QPalette::Window, pal.base().color()); m_ui.MessagesFontTextLabel->setPalette(pal); m_ui.MessagesFontTextLabel->setFont(font); m_ui.MessagesFontTextLabel->setText( font.family() + " " + QString::number(font.pointSize())); // Messages limit option. m_ui.MessagesLimitCheckBox->setChecked(m_pOptions->bMessagesLimit); m_ui.MessagesLimitLinesSpinBox->setValue(m_pOptions->iMessagesLimitLines); // Transport display preferences... m_ui.SyncViewHoldCheckBox->setChecked(m_pOptions->bSyncViewHold); // Dialogs preferences... m_ui.UseNativeDialogsCheckBox->setChecked(m_pOptions->bUseNativeDialogs); // Default track color saturation issue.. m_ui.TrackColorSaturationSpinBox->setValue(m_pOptions->iTrackColorSaturation); // Logging options... m_ui.MessagesLogCheckBox->setChecked(m_pOptions->bMessagesLog); m_ui.MessagesLogPathComboBox->setEditText(m_pOptions->sMessagesLogPath); // Other options finally. m_ui.ConfirmRemoveCheckBox->setChecked(m_pOptions->bConfirmRemove); m_ui.ConfirmArchiveCheckBox->setChecked(m_pOptions->bConfirmArchive); m_ui.StdoutCaptureCheckBox->setChecked(m_pOptions->bStdoutCapture); m_ui.CompletePathCheckBox->setChecked(m_pOptions->bCompletePath); m_ui.PeakAutoRemoveCheckBox->setChecked(m_pOptions->bPeakAutoRemove); m_ui.KeepToolsOnTopCheckBox->setChecked(m_pOptions->bKeepToolsOnTop); m_ui.KeepEditorsOnTopCheckBox->setChecked(m_pOptions->bKeepEditorsOnTop); m_ui.TrackViewDropSpanCheckBox->setChecked(m_pOptions->bTrackViewDropSpan); m_ui.ShiftKeyModifierCheckBox->setChecked(m_pOptions->bShiftKeyModifier); m_ui.MidButtonModifierCheckBox->setChecked(m_pOptions->bMidButtonModifier); m_ui.ClipFadeInTypeComboBox->setCurrentIndex(m_pOptions->iClipFadeInType); m_ui.ClipFadeOutTypeComboBox->setCurrentIndex(m_pOptions->iClipFadeOutType); m_ui.MaxRecentFilesSpinBox->setValue(m_pOptions->iMaxRecentFiles); m_ui.LoopRecordingModeComboBox->setCurrentIndex(m_pOptions->iLoopRecordingMode); m_ui.DisplayFormatComboBox->setCurrentIndex(m_pOptions->iDisplayFormat); if (m_pOptions->iBaseFontSize > 0) m_ui.BaseFontSizeComboBox->setEditText(QString::number(m_pOptions->iBaseFontSize)); else m_ui.BaseFontSizeComboBox->setCurrentIndex(0); // Session options... int iSessionFormat = m_ui.SessionFormatComboBox->findData(m_pOptions->sSessionExt); if (iSessionFormat < 0) iSessionFormat = 0; m_ui.SessionFormatComboBox->setCurrentIndex(iSessionFormat); m_ui.SessionTemplateCheckBox->setChecked(m_pOptions->bSessionTemplate); m_ui.SessionTemplatePathComboBox->setEditText(m_pOptions->sSessionTemplatePath); m_ui.SessionBackupCheckBox->setChecked(m_pOptions->bSessionBackup); m_ui.SessionBackupModeComboBox->setCurrentIndex(m_pOptions->iSessionBackupMode); m_ui.SessionAutoSaveCheckBox->setChecked(m_pOptions->bAutoSaveEnabled); m_ui.SessionAutoSaveSpinBox->setValue(m_pOptions->iAutoSavePeriod); // Plugin path initialization... m_ladspaPaths = m_pOptions->ladspaPaths; m_dssiPaths = m_pOptions->dssiPaths; m_vst2Paths = m_pOptions->vst2Paths; m_vst3Paths = m_pOptions->vst3Paths; m_lv2Paths = m_pOptions->lv2Paths; m_ui.Lv2PresetDirComboBox->setEditText(m_pOptions->sLv2PresetDir); qtractorPluginFactory *pPluginFactory = qtractorPluginFactory::getInstance(); if (pPluginFactory) { #ifdef CONFIG_LADSPA if (m_ladspaPaths.isEmpty()) m_ladspaPaths = pPluginFactory->pluginPaths(qtractorPluginType::Ladspa); #endif #ifdef CONFIG_DSSI if (m_dssiPaths.isEmpty()) m_dssiPaths = pPluginFactory->pluginPaths(qtractorPluginType::Dssi); #endif #ifdef CONFIG_VST2 if (m_vst2Paths.isEmpty()) m_vst2Paths = pPluginFactory->pluginPaths(qtractorPluginType::Vst2); #endif #ifdef CONFIG_VST3 if (m_vst3Paths.isEmpty()) m_vst3Paths = pPluginFactory->pluginPaths(qtractorPluginType::Vst3); #endif #ifdef CONFIG_CLAP if (m_clapPaths.isEmpty()) m_clapPaths = pPluginFactory->pluginPaths(qtractorPluginType::Clap); #endif #ifdef CONFIG_LV2 if (m_lv2Paths.isEmpty()) m_lv2Paths = pPluginFactory->pluginPaths(qtractorPluginType::Lv2); #endif } // Plugin instruments options. m_ui.AudioOutputBusCheckBox->setChecked(m_pOptions->bAudioOutputBus); m_ui.AudioOutputAutoConnectCheckBox->setChecked(m_pOptions->bAudioOutputAutoConnect); m_ui.OpenEditorCheckBox->setChecked(m_pOptions->bOpenEditor); m_ui.QueryEditorTypeCheckBox->setChecked(m_pOptions->bQueryEditorType); int iPluginType = m_pOptions->iPluginType - 1; if (iPluginType < 0) iPluginType = 0; m_ui.PluginTypeComboBox->setCurrentIndex(iPluginType); m_ui.PluginPathComboBox->setEditText(QString()); m_ui.PluginBlacklistWidget->clear(); m_ui.PluginBlacklistWidget->addItems(pPluginFactory->blacklist()); m_ui.PluginBlacklistComboBox->setEditText(QString()); choosePluginType(iPluginType); selectPluginBlacklist(); #ifdef CONFIG_DEBUG m_ui.StdoutCaptureCheckBox->setEnabled(false); #endif // Done. Restart clean. m_iDirtyCount = 0; m_iDirtyLadspaPaths = 0; m_iDirtyDssiPaths = 0; m_iDirtyVst2Paths = 0; m_iDirtyVst3Paths = 0; m_iDirtyClapPaths = 0; m_iDirtyLv2Paths = 0; m_iDirtyBlacklist = 0; stabilizeForm(); } // Retrieve the editing options, if the case arises. qtractorOptions *qtractorOptionsForm::options (void) const { return m_pOptions; } // Spacial meter colors dirty flag. bool qtractorOptionsForm::isDirtyMeterColors (void) const { return (m_iDirtyMeterColors > 0); } // Special custom color themes dirty flag. bool qtractorOptionsForm::isDirtyCustomColorThemes (void) const { return (m_iDirtyCustomColorThemes > 0); } // Accept settings (OK button slot). void qtractorOptionsForm::accept (void) { // Save options... if (m_iDirtyCount > 0) { // Audio options... const void *handle = m_ui.AudioCaptureTypeComboBox->currentHandle(); m_pOptions->sAudioCaptureExt = m_ui.AudioCaptureTypeComboBox->currentExt(handle); m_pOptions->iAudioCaptureType = m_ui.AudioCaptureTypeComboBox->currentType(handle); m_pOptions->iAudioCaptureFormat = m_ui.AudioCaptureFormatComboBox->currentIndex(); m_pOptions->iAudioCaptureQuality = m_ui.AudioCaptureQualitySpinBox->value(); m_pOptions->iAudioResampleType = m_ui.AudioResampleTypeComboBox->currentIndex(); m_pOptions->iTransportMode = m_ui.TransportModeComboBox->currentIndex(); m_pOptions->bTimebase = m_ui.TimebaseCheckBox->isChecked(); m_pOptions->bAudioAutoTimeStretch = m_ui.AudioAutoTimeStretchCheckBox->isChecked(); m_pOptions->bAudioWsolaTimeStretch = m_ui.AudioWsolaTimeStretchCheckBox->isChecked(); m_pOptions->bAudioWsolaQuickSeek = m_ui.AudioWsolaQuickSeekCheckBox->isChecked(); m_pOptions->bAudioRubberBandFormant = m_ui.AudioRubberBandFormantCheckBox->isChecked(); m_pOptions->bAudioRubberBandFinerR3 = m_ui.AudioRubberBandFinerR3CheckBox->isChecked(); m_pOptions->bAudioPlayerBus = m_ui.AudioPlayerBusCheckBox->isChecked(); m_pOptions->bAudioPlayerAutoConnect = m_ui.AudioPlayerAutoConnectCheckBox->isChecked(); m_pOptions->bAudioSelfConnected = m_ui.AudioSelfConnectedCheckBox->isChecked(); // Audio metronome options. m_pOptions->bAudioMetronome = m_ui.AudioMetronomeCheckBox->isChecked(); m_pOptions->iAudioCountInMode = m_ui.AudioCountInModeComboBox->currentIndex(); m_pOptions->iAudioCountInBeats = m_ui.AudioCountInBeatsSpinBox->value(); m_pOptions->sMetroBarFilename = m_ui.MetroBarFilenameComboBox->currentText(); m_pOptions->fMetroBarGain = pow10f2(m_ui.MetroBarGainSpinBox->value()); m_pOptions->sMetroBeatFilename = m_ui.MetroBeatFilenameComboBox->currentText(); m_pOptions->fMetroBeatGain = pow10f2(m_ui.MetroBeatGainSpinBox->value()); m_pOptions->bAudioMetroBus = m_ui.AudioMetroBusCheckBox->isChecked(); m_pOptions->bAudioMetroAutoConnect = m_ui.AudioMetroAutoConnectCheckBox->isChecked(); m_pOptions->iAudioMetroOffset = m_ui.AudioMetroOffsetSpinBox->value(); // MIDI options... m_pOptions->iMidiCaptureFormat = m_ui.MidiCaptureFormatComboBox->currentIndex(); m_pOptions->iMidiCaptureQuantize = m_ui.MidiCaptureQuantizeComboBox->currentIndex(); m_pOptions->iMidiQueueTimer = m_ui.MidiQueueTimerComboBox->itemData( m_ui.MidiQueueTimerComboBox->currentIndex()).toInt(); m_pOptions->bMidiDriftCorrect = m_ui.MidiDriftCorrectCheckBox->isChecked(); m_pOptions->bMidiPlayerBus = m_ui.MidiPlayerBusCheckBox->isChecked(); m_pOptions->bMidiResetAllControllers = m_ui.MidiResetAllControllersCheckBox->isChecked(); m_pOptions->iMidiMmcMode = m_ui.MidiMmcModeComboBox->currentIndex(); m_pOptions->iMidiMmcDevice = m_ui.MidiMmcDeviceComboBox->currentIndex(); m_pOptions->iMidiSppMode = m_ui.MidiSppModeComboBox->currentIndex(); m_pOptions->iMidiClockMode = m_ui.MidiClockModeComboBox->currentIndex(); m_pOptions->bMidiControlBus = m_ui.MidiControlBusCheckBox->isChecked(); // MIDI metronome options. m_pOptions->bMidiMetronome = m_ui.MidiMetronomeCheckBox->isChecked(); m_pOptions->iMidiCountInMode = m_ui.MidiCountInModeComboBox->currentIndex(); m_pOptions->iMidiCountInBeats = m_ui.MidiCountInBeatsSpinBox->value(); m_pOptions->iMetroChannel = m_ui.MetroChannelSpinBox->value() - 1; m_pOptions->iMetroBarNote = m_ui.MetroBarNoteComboBox->currentIndex(); m_pOptions->iMetroBarVelocity = m_ui.MetroBarVelocitySpinBox->value(); m_pOptions->iMetroBarDuration = m_ui.MetroBarDurationSpinBox->value(); m_pOptions->iMetroBeatNote = m_ui.MetroBeatNoteComboBox->currentIndex(); m_pOptions->iMetroBeatVelocity = m_ui.MetroBeatVelocitySpinBox->value(); m_pOptions->iMetroBeatDuration = m_ui.MetroBeatDurationSpinBox->value(); m_pOptions->bMidiMetroBus = m_ui.MidiMetroBusCheckBox->isChecked(); m_pOptions->iMidiMetroOffset = m_ui.MidiMetroOffsetSpinBox->value(); // Display options... m_pOptions->bConfirmRemove = m_ui.ConfirmRemoveCheckBox->isChecked(); m_pOptions->bConfirmArchive = m_ui.ConfirmArchiveCheckBox->isChecked(); m_pOptions->bStdoutCapture = m_ui.StdoutCaptureCheckBox->isChecked(); m_pOptions->bCompletePath = m_ui.CompletePathCheckBox->isChecked(); m_pOptions->bPeakAutoRemove = m_ui.PeakAutoRemoveCheckBox->isChecked(); m_pOptions->bKeepToolsOnTop = m_ui.KeepToolsOnTopCheckBox->isChecked(); m_pOptions->bKeepEditorsOnTop = m_ui.KeepEditorsOnTopCheckBox->isChecked(); m_pOptions->bTrackViewDropSpan = m_ui.TrackViewDropSpanCheckBox->isChecked(); m_pOptions->bShiftKeyModifier = m_ui.ShiftKeyModifierCheckBox->isChecked(); m_pOptions->bMidButtonModifier = m_ui.MidButtonModifierCheckBox->isChecked(); m_pOptions->iClipFadeInType = m_ui.ClipFadeInTypeComboBox->currentIndex(); m_pOptions->iClipFadeOutType = m_ui.ClipFadeOutTypeComboBox->currentIndex(); m_pOptions->iMaxRecentFiles = m_ui.MaxRecentFilesSpinBox->value(); m_pOptions->iLoopRecordingMode = m_ui.LoopRecordingModeComboBox->currentIndex(); m_pOptions->iDisplayFormat = m_ui.DisplayFormatComboBox->currentIndex(); m_pOptions->iBaseFontSize = m_ui.BaseFontSizeComboBox->currentText().toInt(); // Plugin paths... m_pOptions->iPluginType = m_ui.PluginTypeComboBox->currentIndex() + 1; if (m_iDirtyLadspaPaths > 0) m_pOptions->ladspaPaths = m_ladspaPaths; if (m_iDirtyDssiPaths > 0) m_pOptions->dssiPaths = m_dssiPaths; if (m_iDirtyVst2Paths > 0) m_pOptions->vst2Paths = m_vst2Paths; if (m_iDirtyVst3Paths > 0) m_pOptions->vst3Paths = m_vst3Paths; if (m_iDirtyClapPaths > 0) m_pOptions->clapPaths = m_clapPaths; if (m_iDirtyLv2Paths > 0) { m_pOptions->lv2Paths = m_lv2Paths; m_pOptions->sLv2PresetDir = m_ui.Lv2PresetDirComboBox->currentText(); } // Plugin instruments options. m_pOptions->bAudioOutputBus = m_ui.AudioOutputBusCheckBox->isChecked(); m_pOptions->bAudioOutputAutoConnect = m_ui.AudioOutputAutoConnectCheckBox->isChecked(); m_pOptions->bOpenEditor = m_ui.OpenEditorCheckBox->isChecked(); m_pOptions->bQueryEditorType = m_ui.QueryEditorTypeCheckBox->isChecked(); // Messages options... m_pOptions->sMessagesFont = m_ui.MessagesFontTextLabel->font().toString(); m_pOptions->bMessagesLimit = m_ui.MessagesLimitCheckBox->isChecked(); m_pOptions->iMessagesLimitLines = m_ui.MessagesLimitLinesSpinBox->value(); // Logging options... m_pOptions->bMessagesLog = m_ui.MessagesLogCheckBox->isChecked(); m_pOptions->sMessagesLogPath = m_ui.MessagesLogPathComboBox->currentText(); // Session options... m_pOptions->bSessionTemplate = m_ui.SessionTemplateCheckBox->isChecked(); m_pOptions->sSessionTemplatePath = m_ui.SessionTemplatePathComboBox->currentText(); const int iSessionFormat = m_ui.SessionFormatComboBox->currentIndex(); m_pOptions->sSessionExt = m_ui.SessionFormatComboBox->itemData(iSessionFormat).toString(); m_pOptions->bSessionBackup = m_ui.SessionBackupCheckBox->isChecked(); m_pOptions->iSessionBackupMode = m_ui.SessionBackupModeComboBox->currentIndex(); m_pOptions->bAutoSaveEnabled = m_ui.SessionAutoSaveCheckBox->isChecked(); m_pOptions->iAutoSavePeriod = m_ui.SessionAutoSaveSpinBox->value(); // Custom colors. int iColor; for (iColor = 0; iColor < qtractorAudioMeter::ColorCount - 1; ++iColor) qtractorAudioMeter::setColor(iColor, m_paAudioMeterColors[iColor]); for (iColor = 0; iColor < qtractorMidiMeter::ColorCount - 1; ++iColor) qtractorMidiMeter::setColor(iColor, m_paMidiMeterColors[iColor]); // Transport display preferences... m_pOptions->bSyncViewHold = m_ui.SyncViewHoldCheckBox->isChecked(); // Dialogs preferences... m_pOptions->bUseNativeDialogs = m_ui.UseNativeDialogsCheckBox->isChecked(); m_pOptions->bDontUseNativeDialogs = !m_pOptions->bUseNativeDialogs; // Default track color saturation issue.. m_pOptions->iTrackColorSaturation = m_ui.TrackColorSaturationSpinBox->value(); // Custom options.. if (m_ui.CustomColorThemeComboBox->currentIndex() > 0) m_pOptions->sCustomColorTheme = m_ui.CustomColorThemeComboBox->currentText(); else m_pOptions->sCustomColorTheme.clear(); if (m_ui.CustomStyleThemeComboBox->currentIndex() > 0) m_pOptions->sCustomStyleTheme = m_ui.CustomStyleThemeComboBox->currentText(); else m_pOptions->sCustomStyleTheme.clear(); m_pOptions->sCustomStyleSheet = m_pOptions->comboBoxCurrentFile(m_ui.CustomStyleSheetComboBox); m_pOptions->sCustomIconsTheme = m_pOptions->comboBoxCurrentFile(m_ui.CustomIconsThemeComboBox); // Reset dirty flags. qtractorPluginFactory *pPluginFactory = qtractorPluginFactory::getInstance(); if (pPluginFactory) { if (m_iDirtyLadspaPaths > 0) { pPluginFactory->updatePluginPaths(qtractorPluginType::Ladspa); pPluginFactory->clearAll(qtractorPluginType::Ladspa); } if (m_iDirtyDssiPaths > 0){ pPluginFactory->updatePluginPaths(qtractorPluginType::Dssi); pPluginFactory->clearAll(qtractorPluginType::Dssi); } if (m_iDirtyVst2Paths > 0){ pPluginFactory->updatePluginPaths(qtractorPluginType::Vst2); pPluginFactory->clearAll(qtractorPluginType::Vst2); } if (m_iDirtyVst3Paths > 0){ pPluginFactory->updatePluginPaths(qtractorPluginType::Vst3); pPluginFactory->clearAll(qtractorPluginType::Vst3); } if (m_iDirtyClapPaths > 0){ pPluginFactory->updatePluginPaths(qtractorPluginType::Clap); pPluginFactory->clearAll(qtractorPluginType::Clap); } if (m_iDirtyLv2Paths > 0){ pPluginFactory->updatePluginPaths(qtractorPluginType::Lv2); pPluginFactory->clearAll(qtractorPluginType::Lv2); } if (m_iDirtyBlacklist > 0) { QStringList blacklist; const int iBlacklistCount = m_ui.PluginBlacklistWidget->count(); for (int iBlacklist = 0; iBlacklist < iBlacklistCount; ++iBlacklist) { const QListWidgetItem *pItem = m_ui.PluginBlacklistWidget->item(iBlacklist); if (pItem) blacklist.append(pItem->text()); } pPluginFactory->setBlacklist(blacklist); } } m_iDirtyCount = 0; } // Save other conveniency options... m_pOptions->saveComboBoxHistory(m_ui.MetroBarFilenameComboBox); m_pOptions->saveComboBoxHistory(m_ui.MetroBeatFilenameComboBox); m_pOptions->saveComboBoxFileHistory(m_ui.CustomStyleSheetComboBox); m_pOptions->saveComboBoxFileHistory(m_ui.CustomIconsThemeComboBox); m_pOptions->saveComboBoxHistory(m_ui.PluginPathComboBox); m_pOptions->saveComboBoxHistory(m_ui.MessagesLogPathComboBox); m_pOptions->saveComboBoxHistory(m_ui.SessionTemplatePathComboBox); m_pOptions->saveComboBoxHistory(m_ui.Lv2PresetDirComboBox); m_pOptions->saveComboBoxHistory(m_ui.PluginBlacklistComboBox); // Save/commit to disk. m_pOptions->saveOptions(); // Just go with dialog acceptance QDialog::accept(); } // Reject settings (Cancel button slot). void qtractorOptionsForm::reject (void) { bool bReject = true; // Check if there's any pending changes... if (m_iDirtyCount > 0) { QMessageBox::StandardButtons buttons = QMessageBox::Discard | QMessageBox::Cancel; if (m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->isEnabled()) buttons |= QMessageBox::Apply; switch (QMessageBox::warning(this, tr("Warning"), tr("Some settings have been changed.\n\n" "Do you want to apply the changes?"), buttons)) { case QMessageBox::Apply: accept(); return; case QMessageBox::Discard: break; default: // Cancel. bReject = false; } } if (bReject) QDialog::reject(); } // Dirty up settings. void qtractorOptionsForm::changed (void) { ++m_iDirtyCount; stabilizeForm(); } // Audio file type changed. void qtractorOptionsForm::audioCaptureTypeChanged ( int iIndex ) { const void *handle = m_ui.AudioCaptureTypeComboBox->handleOf(iIndex); const qtractorAudioFileFactory::FileFormat *pFormat = static_cast (handle); if (handle && pFormat) { const bool bBlockSignals = m_ui.AudioCaptureFormatComboBox->blockSignals(true); int iFormat = m_ui.AudioCaptureFormatComboBox->currentIndex(); while (iFormat > 0 && // Retry down to PCM Signed 16-Bit... !qtractorAudioFileFactory::isValidFormat(pFormat, iFormat)) --iFormat; m_ui.AudioCaptureFormatComboBox->setCurrentIndex(iFormat); m_ui.AudioCaptureFormatComboBox->blockSignals(bBlockSignals); } changed(); } // Choose audio metronome filenames. void qtractorOptionsForm::chooseMetroBarFilename (void) { QString sFilename = getOpenAudioFileName( tr("Metronome Bar Audio File"), m_ui.MetroBarFilenameComboBox->currentText()); if (sFilename.isEmpty()) return; m_ui.MetroBarFilenameComboBox->setEditText(sFilename); changed(); } void qtractorOptionsForm::chooseMetroBeatFilename (void) { QString sFilename = getOpenAudioFileName( tr("Metronome Beat Audio File"), m_ui.MetroBeatFilenameComboBox->currentText()); if (sFilename.isEmpty()) return; m_ui.MetroBeatFilenameComboBox->setEditText(sFilename); changed(); } // The metronome note names changer. void qtractorOptionsForm::updateMetroNoteNames (void) { // Save current selection... const int iOldBarNote = m_ui.MetroBarNoteComboBox->currentIndex(); const int iOldBeatNote = m_ui.MetroBeatNoteComboBox->currentIndex(); // Populate the Metronome notes. m_ui.MetroBarNoteComboBox->clear(); m_ui.MetroBeatNoteComboBox->clear(); const bool bDrums = (m_ui.MetroChannelSpinBox->value() == 10); QStringList items; const QString sItem("%1 (%2)"); for (int i = 0; i < 128; ++i) { items.append(sItem .arg(qtractorMidiEditor::defaultNoteName(i, bDrums)).arg(i)); } m_ui.MetroBarNoteComboBox->insertItems(0, items); m_ui.MetroBeatNoteComboBox->insertItems(0, items); // Restore old selection... m_ui.MetroBarNoteComboBox->setCurrentIndex(iOldBarNote); m_ui.MetroBeatNoteComboBox->setCurrentIndex(iOldBeatNote); changed(); } // Display format has changed. void qtractorOptionsForm::displayFormatChanged ( int iDisplayFormat ) { const qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(iDisplayFormat); m_ui.AudioMetroOffsetSpinBox->setDisplayFormat(displayFormat); if (m_pTimeScale) m_pTimeScale->setDisplayFormat(displayFormat); changed(); } // Custom color palette theme manager. void qtractorOptionsForm::editCustomColorThemes (void) { qtractorPaletteForm form(this); form.setSettings(&m_pOptions->settings()); QString sCustomColorTheme; int iDirtyCustomColorTheme = 0; const int iCustomColorTheme = m_ui.CustomColorThemeComboBox->currentIndex(); if (iCustomColorTheme > 0) { sCustomColorTheme = m_ui.CustomColorThemeComboBox->itemText(iCustomColorTheme); form.setPaletteName(sCustomColorTheme); } if (form.exec() == QDialog::Accepted) { sCustomColorTheme = form.paletteName(); ++iDirtyCustomColorTheme; } if (iDirtyCustomColorTheme > 0 || form.isDirty()) { ++m_iDirtyCustomColorThemes; resetCustomColorThemes(sCustomColorTheme); changed(); } } // Custom color palette themes settler. void qtractorOptionsForm::resetCustomColorThemes ( const QString& sCustomColorTheme ) { m_ui.CustomColorThemeComboBox->clear(); m_ui.CustomColorThemeComboBox->addItem( tr(g_pszDefName)); m_ui.CustomColorThemeComboBox->addItems( qtractorPaletteForm::namedPaletteList(&m_pOptions->settings())); int iCustomColorTheme = 0; if (!sCustomColorTheme.isEmpty()) { iCustomColorTheme = m_ui.CustomColorThemeComboBox->findText( sCustomColorTheme); if (iCustomColorTheme < 0) iCustomColorTheme = 0; } m_ui.CustomColorThemeComboBox->setCurrentIndex(iCustomColorTheme); } // Custom widget style themes settler. void qtractorOptionsForm::resetCustomStyleThemes ( const QString& sCustomStyleTheme ) { m_ui.CustomStyleThemeComboBox->clear(); m_ui.CustomStyleThemeComboBox->addItem( tr(g_pszDefName)); m_ui.CustomStyleThemeComboBox->addItems( QStyleFactory::keys()); int iCustomStyleTheme = 0; if (!sCustomStyleTheme.isEmpty()) { iCustomStyleTheme = m_ui.CustomStyleThemeComboBox->findText( sCustomStyleTheme); if (iCustomStyleTheme < 0) iCustomStyleTheme = 0; } m_ui.CustomStyleThemeComboBox->setCurrentIndex(iCustomStyleTheme); } void qtractorOptionsForm::chooseCustomStyleSheet (void) { if (m_pOptions == nullptr) return; QString sFilename = m_ui.CustomStyleSheetComboBox->currentText(); const QString sExt("qss"); const QString& sTitle = tr("Open Style Sheet"); QStringList filters; filters.append(tr("Style Sheet files (*.%1)").arg(sExt)); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (m_pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) sFilename = QFileDialog::getOpenFileName(pParentWidget, sTitle, sFilename, sFilter, nullptr, options); #else QFileDialog fileDialog(pParentWidget, sTitle, sFilename, sFilter); fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFiles); fileDialog.setDefaultSuffix(sExt); QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(m_pOptions->sSessionDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); #endif if (sFilename.isEmpty()) return; if (m_pOptions->setComboBoxCurrentFile( m_ui.CustomStyleSheetComboBox, sFilename)) { changed(); } } // Custom icons theme (directory) chooser. void qtractorOptionsForm::chooseCustomIconsTheme (void) { if (m_pOptions == nullptr) return; QString sIconsDir = m_ui.CustomIconsThemeComboBox->currentText(); const QString& sTitle = tr("Icons Theme Directory"); QWidget *pParentWidget = nullptr; QFileDialog::Options options = QFileDialog::ShowDirsOnly; if (m_pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1// QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the directory... sIconsDir = QFileDialog::getExistingDirectory(pParentWidget, sTitle, sIconsDir, options); #else // Construct open-directory dialog... QFileDialog fileDialog(pParentWidget, sTitle, sIconsDir); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::DirectoryOnly); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(m_pOptions->sIconsDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sLv2PresetDir = fileDialog.selectedFiles().first(); #endif if (sIconsDir.isEmpty()) return; // TODO: Make sure the chosen directory has some image files... // if (m_pOptions->setComboBoxCurrentFile( m_ui.CustomIconsThemeComboBox, sIconsDir)) { changed(); } } // Audio meter level index change. void qtractorOptionsForm::changeAudioMeterLevel ( int iColor ) { const QColor& color = m_paAudioMeterColors[iColor]; m_ui.AudioMeterColorLineEdit->setText(color.name()); } // MIDI meter level index change. void qtractorOptionsForm::changeMidiMeterLevel ( int iColor ) { const QColor& color = m_paMidiMeterColors[iColor]; m_ui.MidiMeterColorLineEdit->setText(color.name()); } // Audio meter color change. void qtractorOptionsForm::changeAudioMeterColor ( const QString& sColor ) { const QColor& color = QColor(sColor); if (color.isValid()) { updateColorText(m_ui.AudioMeterColorLineEdit, color); m_paAudioMeterColors[m_ui.AudioMeterLevelComboBox->currentIndex()] = color; ++m_iDirtyMeterColors; changed(); } } // MIDI meter color change. void qtractorOptionsForm::changeMidiMeterColor ( const QString& sColor ) { const QColor& color = QColor(sColor); if (color.isValid()) { updateColorText(m_ui.MidiMeterColorLineEdit, color); m_paMidiMeterColors[m_ui.MidiMeterLevelComboBox->currentIndex()] = color; ++m_iDirtyMeterColors; changed(); } } // Audio meter color selection. void qtractorOptionsForm::chooseAudioMeterColor (void) { const QString& sTitle = tr("Audio Meter Color"); QWidget *pParentWidget = nullptr; QColorDialog::ColorDialogOptions options; if (m_pOptions && m_pOptions->bDontUseNativeDialogs) { options |= QColorDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } const QColor& color = QColorDialog::getColor( QColor(m_ui.AudioMeterColorLineEdit->text()), pParentWidget, sTitle, options); if (color.isValid()) m_ui.AudioMeterColorLineEdit->setText(color.name()); } // Midi meter color selection. void qtractorOptionsForm::chooseMidiMeterColor (void) { const QString& sTitle = tr("MIDI Meter Color"); QWidget *pParentWidget = nullptr; QColorDialog::ColorDialogOptions options; if (m_pOptions && m_pOptions->bDontUseNativeDialogs) { options |= QColorDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } const QColor& color = QColorDialog::getColor( QColor(m_ui.MidiMeterColorLineEdit->text()), pParentWidget, sTitle, options); if (color.isValid()) m_ui.MidiMeterColorLineEdit->setText(color.name()); } // Reset all meter colors from default. void qtractorOptionsForm::resetMeterColors (void) { // Reset colors. int iColor; for (iColor = 0; iColor < qtractorAudioMeter::ColorCount - 1; ++iColor) m_paAudioMeterColors[iColor] = qtractorAudioMeter::defaultColor(iColor); for (iColor = 0; iColor < qtractorMidiMeter::ColorCount - 1; ++iColor) m_paMidiMeterColors[iColor] = qtractorMidiMeter::defaultColor(iColor); // Update current display... changeAudioMeterLevel(m_ui.AudioMeterLevelComboBox->currentIndex()); changeMidiMeterLevel(m_ui.MidiMeterLevelComboBox->currentIndex()); ++m_iDirtyMeterColors; changed(); } // Update color item visual text. void qtractorOptionsForm::updateColorText ( QLineEdit *pLineEdit, const QColor& color ) { QPalette pal(color); pal.setColor(QPalette::Base, color); pLineEdit->setPalette(pal); } // Change plugin type. void qtractorOptionsForm::choosePluginType ( int iPluginType ) { if (m_pOptions == nullptr) return; QStringList paths; qtractorPluginType::Hint typeHint = qtractorPluginType::hintFromText( m_ui.PluginTypeComboBox->itemText(iPluginType)); switch (typeHint) { case qtractorPluginType::Ladspa: paths = m_ladspaPaths; break; case qtractorPluginType::Dssi: paths = m_dssiPaths; break; case qtractorPluginType::Vst2: paths = m_vst2Paths; break; case qtractorPluginType::Vst3: paths = m_vst3Paths; break; case qtractorPluginType::Clap: paths = m_clapPaths; break; case qtractorPluginType::Lv2: paths = m_lv2Paths; // Fall thru... default: break; } m_ui.PluginPathListWidget->clear(); m_ui.PluginPathListWidget->addItems(paths); selectPluginPath(); stabilizeForm(); } // Change plugin path. void qtractorOptionsForm::changePluginPath ( const QString& /*sPluginPath*/ ) { selectPluginPath(); stabilizeForm(); } // Browse for plugin path. void qtractorOptionsForm::choosePluginPath (void) { QString sPluginPath; const QString& sTitle = tr("Plug-in Directory"); QWidget *pParentWidget = nullptr; QFileDialog::Options options = QFileDialog::ShowDirsOnly; if (m_pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the directory... sPluginPath = QFileDialog::getExistingDirectory(pParentWidget, sTitle, m_ui.PluginPathComboBox->currentText(), options); #else // Construct open-directory dialog... QFileDialog fileDialog(pParentWidget, sTitle, m_ui.PluginPathComboBox->currentText()); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::DirectoryOnly); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(m_pOptions->sSessionDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sPluginPath = fileDialog.selectedFiles().first(); #endif if (!sPluginPath.isEmpty()) { m_ui.PluginPathComboBox->setEditText(sPluginPath); m_ui.PluginPathComboBox->setFocus(); } selectPluginPath(); stabilizeForm(); } // Add chosen plugin path. void qtractorOptionsForm::addPluginPath (void) { const QString& sPluginPath = m_ui.PluginPathComboBox->currentText(); if (sPluginPath.isEmpty()) return; if (!QDir(sPluginPath).exists()) return; qtractorPluginType::Hint typeHint = qtractorPluginType::hintFromText( m_ui.PluginTypeComboBox->currentText()); switch (typeHint) { case qtractorPluginType::Ladspa: m_ladspaPaths.append(sPluginPath); ++m_iDirtyLadspaPaths; break; case qtractorPluginType::Dssi: m_dssiPaths.append(sPluginPath); ++m_iDirtyDssiPaths; break; case qtractorPluginType::Vst2: m_vst2Paths.append(sPluginPath); ++m_iDirtyVst2Paths; break; case qtractorPluginType::Vst3: m_vst3Paths.append(sPluginPath); ++m_iDirtyVst3Paths; break; case qtractorPluginType::Clap: m_clapPaths.append(sPluginPath); ++m_iDirtyClapPaths; break; case qtractorPluginType::Lv2: m_lv2Paths.append(sPluginPath); ++m_iDirtyLv2Paths; break; default: return; } m_ui.PluginPathListWidget->addItem(sPluginPath); m_ui.PluginPathListWidget->setCurrentRow( m_ui.PluginPathListWidget->count() - 1); const int i = m_ui.PluginPathComboBox->findText(sPluginPath); if (i >= 0) m_ui.PluginPathComboBox->removeItem(i); m_ui.PluginPathComboBox->insertItem(0, sPluginPath); m_ui.PluginPathComboBox->setEditText(QString()); m_ui.PluginPathListWidget->setFocus(); selectPluginPath(); changed(); } // Select current plugin path. void qtractorOptionsForm::selectPluginPath (void) { const int iPluginPath = m_ui.PluginPathListWidget->currentRow(); m_ui.PluginPathRemoveToolButton->setEnabled(iPluginPath >= 0); m_ui.PluginPathUpToolButton->setEnabled(iPluginPath > 0); m_ui.PluginPathDownToolButton->setEnabled(iPluginPath >= 0 && iPluginPath < m_ui.PluginPathListWidget->count() - 1); } // Remove current plugin path. void qtractorOptionsForm::removePluginPath (void) { const int iPluginPath = m_ui.PluginPathListWidget->currentRow(); if (iPluginPath < 0) return; qtractorPluginType::Hint typeHint = qtractorPluginType::hintFromText( m_ui.PluginTypeComboBox->currentText()); switch (typeHint) { case qtractorPluginType::Ladspa: m_ladspaPaths.removeAt(iPluginPath); ++m_iDirtyLadspaPaths; break; case qtractorPluginType::Dssi: m_dssiPaths.removeAt(iPluginPath); ++m_iDirtyDssiPaths; break; case qtractorPluginType::Vst2: m_vst2Paths.removeAt(iPluginPath); ++m_iDirtyVst2Paths; break; case qtractorPluginType::Vst3: m_vst3Paths.removeAt(iPluginPath); ++m_iDirtyVst3Paths; break; case qtractorPluginType::Clap: m_clapPaths.removeAt(iPluginPath); ++m_iDirtyClapPaths; break; case qtractorPluginType::Lv2: m_lv2Paths.removeAt(iPluginPath); ++m_iDirtyLv2Paths; break; default: return; } QListWidgetItem *pItem = m_ui.PluginPathListWidget->takeItem(iPluginPath); if (pItem) delete pItem; selectPluginPath(); changed(); } // Move up plugin path on search order. void qtractorOptionsForm::moveUpPluginPath (void) { const int iPluginPath = m_ui.PluginPathListWidget->currentRow(); if (iPluginPath < 1) return; QString sPluginPath; qtractorPluginType::Hint typeHint = qtractorPluginType::hintFromText( m_ui.PluginTypeComboBox->currentText()); switch (typeHint) { case qtractorPluginType::Ladspa: sPluginPath = m_ladspaPaths.takeAt(iPluginPath); m_ladspaPaths.insert(iPluginPath - 1, sPluginPath); ++m_iDirtyLadspaPaths; break; case qtractorPluginType::Dssi: sPluginPath = m_dssiPaths.takeAt(iPluginPath); m_dssiPaths.insert(iPluginPath - 1, sPluginPath); ++m_iDirtyDssiPaths; break; case qtractorPluginType::Vst2: sPluginPath = m_vst2Paths.takeAt(iPluginPath); m_vst2Paths.insert(iPluginPath - 1, sPluginPath); ++m_iDirtyVst2Paths; break; case qtractorPluginType::Vst3: sPluginPath = m_vst3Paths.takeAt(iPluginPath); m_vst3Paths.insert(iPluginPath - 1, sPluginPath); ++m_iDirtyVst3Paths; break; case qtractorPluginType::Clap: sPluginPath = m_clapPaths.takeAt(iPluginPath); m_clapPaths.insert(iPluginPath - 1, sPluginPath); ++m_iDirtyClapPaths; break; case qtractorPluginType::Lv2: sPluginPath = m_lv2Paths.takeAt(iPluginPath); m_lv2Paths.insert(iPluginPath - 1, sPluginPath); ++m_iDirtyLv2Paths; break; default: return; } QListWidgetItem *pItem = m_ui.PluginPathListWidget->takeItem(iPluginPath); if (pItem) { pItem->setSelected(false); m_ui.PluginPathListWidget->insertItem(iPluginPath - 1, pItem); pItem->setSelected(true); m_ui.PluginPathListWidget->setCurrentItem(pItem); } selectPluginPath(); changed(); } // Move down plugin path on search order. void qtractorOptionsForm::moveDownPluginPath (void) { const int iPluginPath = m_ui.PluginPathListWidget->currentRow(); if (iPluginPath >= m_ui.PluginPathListWidget->count() - 1) return; QString sPluginPath; qtractorPluginType::Hint typeHint = qtractorPluginType::hintFromText( m_ui.PluginTypeComboBox->currentText()); switch (typeHint) { case qtractorPluginType::Ladspa: sPluginPath = m_ladspaPaths.takeAt(iPluginPath); m_ladspaPaths.insert(iPluginPath + 1, sPluginPath); ++m_iDirtyLadspaPaths; break; case qtractorPluginType::Dssi: sPluginPath = m_dssiPaths.takeAt(iPluginPath); m_dssiPaths.insert(iPluginPath + 1, sPluginPath); ++m_iDirtyDssiPaths; break; case qtractorPluginType::Vst2: sPluginPath = m_vst2Paths.takeAt(iPluginPath); m_vst2Paths.insert(iPluginPath + 1, sPluginPath); ++m_iDirtyVst2Paths; break; case qtractorPluginType::Vst3: sPluginPath = m_vst3Paths.takeAt(iPluginPath); m_vst3Paths.insert(iPluginPath + 1, sPluginPath); ++m_iDirtyVst3Paths; break; case qtractorPluginType::Clap: sPluginPath = m_clapPaths.takeAt(iPluginPath); m_clapPaths.insert(iPluginPath + 1, sPluginPath); ++m_iDirtyClapPaths; break; case qtractorPluginType::Lv2: sPluginPath = m_lv2Paths.takeAt(iPluginPath); m_lv2Paths.insert(iPluginPath + 1, sPluginPath); ++m_iDirtyLv2Paths; break; default: return; } QListWidgetItem *pItem = m_ui.PluginPathListWidget->takeItem(iPluginPath); if (pItem) { pItem->setSelected(false); m_ui.PluginPathListWidget->insertItem(iPluginPath + 1, pItem); pItem->setSelected(true); m_ui.PluginPathListWidget->setCurrentItem(pItem); } selectPluginPath(); changed(); } // Browse for LV2 Presets directory. void qtractorOptionsForm::chooseLv2PresetDir (void) { QString sLv2PresetDir = m_ui.Lv2PresetDirComboBox->currentText(); if (sLv2PresetDir.isEmpty()) sLv2PresetDir = QDir::homePath() + QDir::separator() + ".lv2"; const QString& sTitle = tr("LV2 Presets Directory"); QWidget *pParentWidget = nullptr; QFileDialog::Options options = QFileDialog::ShowDirsOnly; if (m_pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1// QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the directory... sLv2PresetDir = QFileDialog::getExistingDirectory(pParentWidget, sTitle, sLv2PresetDir, options); #else // Construct open-directory dialog... QFileDialog fileDialog(pParentWidget, sTitle, sLv2PresetDir); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::DirectoryOnly); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(m_pOptions->sLv2PresetDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sLv2PresetDir = fileDialog.selectedFiles().first(); #endif if (!sLv2PresetDir.isEmpty()) { m_ui.Lv2PresetDirComboBox->setEditText(sLv2PresetDir); m_ui.Lv2PresetDirComboBox->setFocus(); } ++m_iDirtyLv2Paths; changed(); } // Change plugin path. void qtractorOptionsForm::changePluginBlacklist ( const QString& /*sBlacklist*/ ) { selectPluginBlacklist(); stabilizeForm(); } // Browse for plugin blacklist path. void qtractorOptionsForm::choosePluginBlacklist (void) { QString sFilename; const QString& sTitle = tr("Plug-in Blacklist"); QStringList exts; exts.append("so"); #ifdef CONFIG_CLAP exts.append("clap"); #endif QStringList filters; filters.append(tr("Plug-in files (*.%1)").arg(exts.join(" *."))); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (m_pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to open... sFilename = QFileDialog::getOpenFileName(pParentWidget, sTitle, m_ui.PluginBlacklistComboBox->currentText(), sFilter, nullptr, options); #else // Construct open-files dialog... QFileDialog fileDialog(pParentWidget, sTitle, m_ui.PluginBlacklistComboBox->currentText(), sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFile); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(m_pOptions->sSessionDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); #endif if (!sFilename.isEmpty()) { m_ui.PluginBlacklistComboBox->setEditText(sFilename); m_ui.PluginBlacklistComboBox->setFocus(); } selectPluginBlacklist(); stabilizeForm(); } // Add chosen plugin blacklist path. void qtractorOptionsForm::addPluginBlacklist (void) { const QString& sBlacklist = m_ui.PluginBlacklistComboBox->currentText(); if (sBlacklist.isEmpty()) return; m_ui.PluginBlacklistWidget->addItem(sBlacklist); m_ui.PluginBlacklistWidget->setCurrentRow( m_ui.PluginBlacklistWidget->count() - 1); const int i = m_ui.PluginBlacklistComboBox->findText(sBlacklist); if (i >= 0) m_ui.PluginBlacklistComboBox->removeItem(i); m_ui.PluginBlacklistComboBox->insertItem(0, sBlacklist); m_ui.PluginBlacklistComboBox->setEditText(QString()); m_ui.PluginBlacklistWidget->setFocus(); ++m_iDirtyBlacklist; selectPluginBlacklist(); changed(); } // Select current plugin path. void qtractorOptionsForm::selectPluginBlacklist (void) { const int iBlacklist = m_ui.PluginBlacklistWidget->currentRow(); m_ui.PluginBlacklistRemoveToolButton->setEnabled(iBlacklist >= 0); m_ui.PluginBlacklistClearToolButton->setEnabled( m_ui.PluginBlacklistWidget->count() > 0); } // Remove current plugin path. void qtractorOptionsForm::removePluginBlacklist (void) { const int iBlacklist = m_ui.PluginBlacklistWidget->currentRow(); if (iBlacklist < 0) return; QListWidgetItem *pItem = m_ui.PluginBlacklistWidget->takeItem(iBlacklist); if (pItem) delete pItem; ++m_iDirtyBlacklist; selectPluginBlacklist(); changed(); } // Clear blacklist paths. void qtractorOptionsForm::clearPluginBlacklist (void) { m_ui.PluginBlacklistWidget->clear(); ++m_iDirtyBlacklist; selectPluginBlacklist(); changed(); } // The messages font selection dialog. void qtractorOptionsForm::chooseMessagesFont (void) { const QString& sTitle = tr("Messages Font"); QWidget *pParentWidget = nullptr; QFontDialog::FontDialogOptions options; if (m_pOptions->bDontUseNativeDialogs) { options |= QFontDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } bool bOk = false; const QFont font = QFontDialog::getFont(&bOk, m_ui.MessagesFontTextLabel->font(), pParentWidget, sTitle, options); if (bOk) { m_ui.MessagesFontTextLabel->setFont(font); m_ui.MessagesFontTextLabel->setText( font.family() + ' ' + QString::number(font.pointSize())); changed(); } } // Messages log path browse slot. void qtractorOptionsForm::chooseMessagesLogPath (void) { QString sFilename; const QString sExt("log"); const QString& sTitle = tr("Messages Log"); QStringList filters; filters.append(tr("Log files (*.%1)").arg(sExt)); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (m_pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to open... sFilename = QFileDialog::getSaveFileName(pParentWidget, sTitle, m_ui.MessagesLogPathComboBox->currentText(), sFilter, nullptr, options); #else // Construct open-file dialog... QFileDialog fileDialog(pParentWidget, sTitle, m_ui.MessagesLogPathComboBox->currentText(), sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setDefaultSuffix(sExt); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); #endif if (!sFilename.isEmpty() && sFilename.at(0) != '.') { m_ui.MessagesLogPathComboBox->setEditText(sFilename); m_ui.MessagesLogPathComboBox->setFocus(); changed(); } } // Session template path browse slot. void qtractorOptionsForm::chooseSessionTemplatePath (void) { QString sFilename; const QString sExt("qtt"); const QString& sTitle = tr("Session Template"); QStringList filters; filters.append(tr("Session template files (*.qtr *.qts *.%1)").arg(sExt)); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (m_pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to open... sFilename = QFileDialog::getOpenFileName(pParentWidget, sTitle, m_ui.SessionTemplatePathComboBox->currentText(), sFilter, nullptr, options); #else // Construct open-files dialog... QFileDialog fileDialog(pParentWidget, sTitle, m_ui.SessionTemplatePathComboBox->currentText(), sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFile); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(m_pOptions->sSessionDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); #endif if (sFilename.isEmpty()) return; m_ui.SessionTemplatePathComboBox->setEditText(sFilename); m_ui.SessionTemplatePathComboBox->setFocus(); changed(); } // Stabilize current form state. void qtractorOptionsForm::stabilizeForm (void) { m_ui.MessagesLimitLinesSpinBox->setEnabled( m_ui.MessagesLimitCheckBox->isChecked()); // Audio options validy check... const qtractorAudioFileFactory::FileFormat *pFormat = static_cast ( m_ui.AudioCaptureTypeComboBox->currentHandle()); const bool bSndFile = (pFormat && pFormat->type == qtractorAudioFileFactory::SndFile); m_ui.AudioCaptureFormatTextLabel->setEnabled(bSndFile); m_ui.AudioCaptureFormatComboBox->setEnabled(bSndFile); const bool bVorbisFile = (pFormat && pFormat->type == qtractorAudioFileFactory::VorbisFile); m_ui.AudioCaptureQualityTextLabel->setEnabled(bVorbisFile); m_ui.AudioCaptureQualitySpinBox->setEnabled(bVorbisFile); bool bValid = (m_iDirtyCount > 0); if (bValid) { const int iFormat = m_ui.AudioCaptureFormatComboBox->currentIndex(); bValid = qtractorAudioFileFactory::isValidFormat(pFormat, iFormat); } m_ui.AudioWsolaQuickSeekCheckBox->setEnabled( m_ui.AudioWsolaTimeStretchCheckBox->isChecked()); m_ui.AudioPlayerAutoConnectCheckBox->setEnabled( m_ui.AudioPlayerBusCheckBox->isChecked()); const bool bAudioMetronome = m_ui.AudioMetronomeCheckBox->isChecked(); m_ui.AudioCountInModeLabel->setEnabled(bAudioMetronome); m_ui.AudioCountInModeComboBox->setEnabled(bAudioMetronome); m_ui.AudioCountInBeatsSpinBox->setEnabled( bAudioMetronome && m_ui.AudioCountInModeComboBox->currentIndex() > 0); m_ui.MetroBarFilenameTextLabel->setEnabled(bAudioMetronome); m_ui.MetroBarFilenameComboBox->setEnabled(bAudioMetronome); m_ui.MetroBarFilenameToolButton->setEnabled(bAudioMetronome); bool bAudioMetroBar = false; if (bAudioMetronome) { const QFileInfo fi(m_ui.MetroBarFilenameComboBox->currentText()); bAudioMetroBar = fi.isFile() && fi.isReadable(); bValid = bAudioMetroBar; } m_ui.MetroBarGainTextLabel->setEnabled(bAudioMetroBar); m_ui.MetroBarGainSpinBox->setEnabled(bAudioMetroBar); m_ui.MetroBeatFilenameTextLabel->setEnabled(bAudioMetronome); m_ui.MetroBeatFilenameComboBox->setEnabled(bAudioMetronome); m_ui.MetroBeatFilenameToolButton->setEnabled(bAudioMetronome); bool bAudioMetroBeat = false; if (bAudioMetronome) { const QFileInfo fi(m_ui.MetroBeatFilenameComboBox->currentText()); bAudioMetroBeat = fi.isFile() && fi.isReadable(); bValid = bAudioMetroBeat; } m_ui.MetroBeatGainTextLabel->setEnabled(bAudioMetroBeat); m_ui.MetroBeatGainSpinBox->setEnabled(bAudioMetroBeat); m_ui.AudioMetroBusCheckBox->setEnabled(bAudioMetronome); m_ui.AudioMetroOffsetTextLabel->setEnabled(bAudioMetronome); m_ui.AudioMetroOffsetSpinBox->setEnabled(bAudioMetronome); m_ui.AudioMetroAutoConnectCheckBox->setEnabled( bAudioMetronome && m_ui.AudioMetroBusCheckBox->isChecked()); const bool bMmcMode =(m_ui.MidiMmcModeComboBox->currentIndex() > 0); m_ui.MidiMmcDeviceTextLabel->setEnabled(bMmcMode); m_ui.MidiMmcDeviceComboBox->setEnabled(bMmcMode); const bool bMidiMetronome = m_ui.MidiMetronomeCheckBox->isChecked(); m_ui.MidiCountInModeLabel->setEnabled(bMidiMetronome); m_ui.MidiCountInModeComboBox->setEnabled(bMidiMetronome); m_ui.MidiCountInBeatsSpinBox->setEnabled( bMidiMetronome && m_ui.MidiCountInModeComboBox->currentIndex() > 0); m_ui.MetroChannelTextLabel->setEnabled(bMidiMetronome); m_ui.MetroChannelSpinBox->setEnabled(bMidiMetronome); m_ui.MetroBarNoteTextLabel->setEnabled(bMidiMetronome); m_ui.MetroBarNoteComboBox->setEnabled(bMidiMetronome); m_ui.MetroBarVelocityTextLabel->setEnabled(bMidiMetronome); m_ui.MetroBarVelocitySpinBox->setEnabled(bMidiMetronome); m_ui.MetroBarDurationTextLabel->setEnabled(bMidiMetronome); m_ui.MetroBarDurationSpinBox->setEnabled(bMidiMetronome); m_ui.MetroBeatNoteTextLabel->setEnabled(bMidiMetronome); m_ui.MetroBeatNoteComboBox->setEnabled(bMidiMetronome); m_ui.MetroBeatVelocityTextLabel->setEnabled(bMidiMetronome); m_ui.MetroBeatVelocitySpinBox->setEnabled(bMidiMetronome); m_ui.MetroBeatDurationTextLabel->setEnabled(bMidiMetronome); m_ui.MetroBeatDurationSpinBox->setEnabled(bMidiMetronome); m_ui.MidiMetroBusCheckBox->setEnabled(bMidiMetronome); m_ui.MidiMetroOffsetTextLabel->setEnabled(bMidiMetronome); m_ui.MidiMetroOffsetSpinBox->setEnabled(bMidiMetronome); const bool bMessagesLog = m_ui.MessagesLogCheckBox->isChecked(); m_ui.MessagesLogPathComboBox->setEnabled(bMessagesLog); m_ui.MessagesLogPathToolButton->setEnabled(bMessagesLog); if (bMessagesLog && bValid) { const QString& sPath = m_ui.MessagesLogPathComboBox->currentText(); bValid = !sPath.isEmpty(); } const bool bSessionTemplate = m_ui.SessionTemplateCheckBox->isChecked(); m_ui.SessionTemplatePathComboBox->setEnabled(bSessionTemplate); m_ui.SessionTemplatePathToolButton->setEnabled(bSessionTemplate); if (bSessionTemplate && bValid) { const QString& sPath = m_ui.SessionTemplatePathComboBox->currentText(); bValid = !sPath.isEmpty() && QFileInfo::exists(sPath); } m_ui.SessionBackupModeComboBox->setEnabled( m_ui.SessionBackupCheckBox->isChecked()); m_ui.SessionAutoSaveSpinBox->setEnabled( m_ui.SessionAutoSaveCheckBox->isChecked()); const QString& sPluginPath = m_ui.PluginPathComboBox->currentText(); m_ui.PluginPathAddToolButton->setEnabled( !sPluginPath.isEmpty() && QDir(sPluginPath).exists() && m_ui.PluginPathListWidget->findItems( sPluginPath, Qt::MatchExactly).isEmpty()); const QString& sBlacklist = m_ui.PluginBlacklistComboBox->currentText(); m_ui.PluginBlacklistAddToolButton->setEnabled( !sBlacklist.isEmpty() && m_ui.PluginBlacklistWidget->findItems( sBlacklist, Qt::MatchExactly).isEmpty()); if (bValid) { const QString& sLv2PresetDir = m_ui.Lv2PresetDirComboBox->currentText(); bValid = sLv2PresetDir.isEmpty() || QDir(sLv2PresetDir).exists(); } m_ui.AudioOutputAutoConnectCheckBox->setEnabled( m_ui.AudioOutputBusCheckBox->isChecked()); m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(bValid); } // Browse for an existing audio filename. QString qtractorOptionsForm::getOpenAudioFileName ( const QString& sTitle, const QString& sFilename ) { const QString& sFilter = qtractorAudioFileFactory::filters().join(";;"); QString sAudioFile; QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (m_pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to open... sAudioFile = QFileDialog::getOpenFileName(pParentWidget, sTitle, sFilename, sFilter, nullptr, options); #else // Construct open-file dialog... QFileDialog fileDialog(pParentWidget, sTitle, sFilename, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFile); fileDialog.setDefaultSuffix(qtractorAudioFileFactory::defaultExt()); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(m_pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(m_pOptions->sAudioDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sAudioFile = fileDialog.selectedFiles().first(); #endif return sAudioFile; } // end of qtractorOptionsForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorSessionCursor.cpp0000644000000000000000000000013215124701674020417 xustar0030 mtime=1767080892.800263424 30 atime=1767080892.800263424 30 ctime=1767080892.800263424 qtractor-1.5.11/src/qtractorSessionCursor.cpp0000644000175000001440000001764715124701674020426 0ustar00rncbcusers// qtractorSessionCursor.cpp // /**************************************************************************** Copyright (C) 2005-2019, 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 "qtractorAbout.h" #include "qtractorSessionCursor.h" #include "qtractorSession.h" #include "qtractorClip.h" //---------------------------------------------------------------------- // class qtractorSessionCursor - implementation. // // Constructor. qtractorSessionCursor::qtractorSessionCursor ( qtractorSession *pSession, unsigned long iFrame, qtractorTrack::TrackType syncType ) { m_pSession = pSession; m_iFrame = iFrame; m_syncType = syncType; m_iTracks = 0; m_ppClips = nullptr; m_iSize = 0; resetClips(); reset(); } // Destructor. qtractorSessionCursor::~qtractorSessionCursor (void) { m_pSession->unlinkSessionCursor(this); if (m_ppClips) delete [] m_ppClips; } // Session accessor. qtractorSession *qtractorSessionCursor::session (void) const { return m_pSession; } // Clip sync flag accessor. void qtractorSessionCursor::setSyncType ( qtractorTrack::TrackType syncType ) { m_syncType = syncType; } qtractorTrack::TrackType qtractorSessionCursor::syncType (void) const { return m_syncType; } // General bi-directional locate method. void qtractorSessionCursor::seek ( unsigned long iFrame, bool bSync ) { if (iFrame == m_iFrame) return; unsigned int iTrack = 0; qtractorTrack *pTrack = m_pSession->tracks().first(); while (pTrack && iTrack < m_iTracks) { qtractorClip *pClip = nullptr; qtractorClip *pClipLast = m_ppClips[iTrack]; // Optimize if seeking forward... if (iFrame > m_iFrame) pClip = pClipLast; // Locate first clip not past the target frame position.. pClip = seekClip(pTrack, pClip, iFrame); // Update cursor track clip... m_ppClips[iTrack] = pClip; // Now something fulcral for clips around... if (pTrack->trackType() == m_syncType) { // Tell whether play-head is after loop-start position... const bool bLooping = (iFrame >= m_pSession->loopStart()); // Care for old/previous clip... if (pClipLast && pClipLast != pClip) pClipLast->reset(bLooping); // Set final position within target clip... if (pClip && bSync) { // Take care of overlapping clips... const unsigned long iClipEnd = pClip->clipStart() + pClip->clipLength(); while (pClip) { const unsigned long iClipStart = pClip->clipStart(); if (iClipStart > iClipEnd) break; if (iFrame >= iClipStart && iFrame < iClipStart + pClip->clipLength()) { pClip->seek(iFrame - iClipStart); } else { pClip->reset(bLooping); } pClip = pClip->next(); } } } // Next track... pTrack = pTrack->next(); ++iTrack; } // Done. m_iFrame = iFrame; } // Current frame position accessor. unsigned long qtractorSessionCursor::frame (void) const { return m_iFrame; } // Absolute frame-time position accessors. unsigned long qtractorSessionCursor::frameTime (void) const { return m_iFrameTime; } unsigned long qtractorSessionCursor::frameTimeEx (void) const { return m_iFrameTime + m_iFrameDelta; } // Current track clip accessor. qtractorClip *qtractorSessionCursor::clip ( unsigned int iTrack ) const { return (iTrack < m_iTracks ? m_ppClips[iTrack] : nullptr); } // Clip locate method. qtractorClip *qtractorSessionCursor::seekClip ( qtractorTrack *pTrack, qtractorClip *pClip, unsigned long iFrame ) const { if (pClip == nullptr) pClip = pTrack->clips().first(); while (pClip && iFrame > pClip->clipStart() + pClip->clipLength()) { // if (pTrack->trackType() == m_syncType) // pClip->reset(m_pSession->isLooping()); pClip = pClip->next(); } if (pClip == nullptr) pClip = pTrack->clips().last(); return pClip; } // Add a track to cursor. void qtractorSessionCursor::addTrack ( qtractorTrack *pTrack ) { if (pTrack == nullptr) pTrack = m_pSession->tracks().last(); const unsigned int iTracks = m_iTracks + 1; if (iTracks < m_iSize) { updateClips(m_ppClips, iTracks); } else { m_iSize += iTracks; qtractorClip **ppOldClips = m_ppClips; qtractorClip **ppNewClips = new qtractorClip * [m_iSize]; updateClips(ppNewClips, iTracks); m_ppClips = ppNewClips; if (ppOldClips) delete [] ppOldClips; } m_iTracks = iTracks; } // Update track after adding/removing a clip from cursor. void qtractorSessionCursor::updateTrack ( qtractorTrack *pTrack ) { const int iTrack = m_pSession->tracks().find(pTrack); if (iTrack >= 0) { qtractorClip *pClip = seekClip(pTrack, nullptr, m_iFrame); if (pClip && pTrack->trackType() == m_syncType && m_iFrame >= pClip->clipStart() && m_iFrame < pClip->clipStart() + pClip->clipLength()) { pClip->seek(m_iFrame - pClip->clipStart()); } m_ppClips[iTrack] = pClip; } } // Remove a track from cursor. void qtractorSessionCursor::removeTrack ( qtractorTrack *pTrack ) { const int iTrack = m_pSession->tracks().find(pTrack); if (iTrack >= 0) removeTrack((unsigned int) iTrack); } void qtractorSessionCursor::removeTrack ( unsigned int iTrack ) { --m_iTracks; for ( ; iTrack < m_iTracks; ++iTrack) m_ppClips[iTrack] = m_ppClips[iTrack + 1]; m_ppClips[iTrack] = nullptr; } // Update current track clip under cursor. void qtractorSessionCursor::updateTrackClip ( qtractorTrack *pTrack ) { const int iTrack = m_pSession->tracks().find(pTrack); if (iTrack >= 0) { qtractorClip *pClip = m_ppClips[iTrack]; if (pClip && pTrack->trackType() == m_syncType) { if (m_iFrame >= pClip->clipStart() && m_iFrame < pClip->clipStart() + pClip->clipLength()) { pClip->seek(m_iFrame - pClip->clipStart()); } else { pClip->reset(m_iFrame >= m_pSession->loopStart()); } } } } // Update (stabilize) cursor. void qtractorSessionCursor::updateClips ( qtractorClip **ppClips, unsigned int iTracks ) { // Reset clip positions... unsigned int iTrack = 0; qtractorTrack *pTrack = m_pSession->tracks().first(); while (pTrack && iTrack < iTracks) { qtractorClip *pClip = seekClip(pTrack, nullptr, m_iFrame); if (pClip && pTrack->trackType() == m_syncType && m_iFrame >= pClip->clipStart() && m_iFrame < pClip->clipStart() + pClip->clipLength()) { pClip->seek(m_iFrame - pClip->clipStart()); } ppClips[iTrack] = pClip; pTrack = pTrack->next(); ++iTrack; } } // Reset cursor. void qtractorSessionCursor::reset (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorSessionCursor[%p,%d]::reset()", this, (int) m_syncType); #endif m_iFrameTime = 0; m_iFrameDelta = m_iFrame; } // Reset track/clips cache. void qtractorSessionCursor::resetClips (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorSessionCursor[%p,%d]::resetClips()", this, (int) m_syncType); #endif // Free existing clip references. if (m_ppClips) { qtractorClip **ppOldClips = m_ppClips; m_ppClips = nullptr; delete [] ppOldClips; } // Rebuild the whole bunch... m_iTracks = m_pSession->tracks().count(); m_iSize = (m_iTracks << 1); if (m_iSize > 0) { qtractorClip **ppNewClips = new qtractorClip * [m_iSize]; updateClips(ppNewClips, m_iTracks); m_ppClips = ppNewClips; } } // Frame-time processor (increment only). void qtractorSessionCursor::process ( unsigned int nframes ) { m_iFrameTime += nframes; } // end of qtractorSessionCursor.cpp qtractor-1.5.11/src/PaxHeaders/qtractorAudioFile.h0000644000000000000000000000013215124701674017104 xustar0030 mtime=1767080892.779263512 30 atime=1767080892.779263512 30 ctime=1767080892.779263512 qtractor-1.5.11/src/qtractorAudioFile.h0000644000175000001440000001020115124701674017066 0ustar00rncbcusers// qtractorAudioFile.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorAudioFile_h #define __qtractorAudioFile_h #include #include //---------------------------------------------------------------------- // class qtractorAudioFile -- Abstract audio file mockup. // class qtractorAudioFile { public: // Virtual destructor. virtual ~qtractorAudioFile() {} // Basic file open mode. enum { None = 0, Read = 1, Write = 2 }; // Pure virtual method mockups. virtual bool open (const QString& sFilename, int iMode = Read) = 0; virtual int read (float **ppFrames, unsigned int iFrames) = 0; virtual int write (float **ppFrames, unsigned int iFrames) = 0; virtual bool seek (unsigned long iOffset) = 0; virtual void close () = 0; // Pure virtual accessor mockups. virtual int mode() const = 0; // These shall give us a clue on the size // of the ring buffer size (in frames). virtual unsigned short channels() const = 0; virtual unsigned long frames() const = 0; // Other special informational methods. virtual unsigned int sampleRate() const = 0; }; //---------------------------------------------------------------------- // class qtractorAudioFileFactory -- Audio file factory (singleton). // class qtractorAudioFileFactory { public: // Constructor. qtractorAudioFileFactory(); // Destructor. ~qtractorAudioFileFactory(); // Singleton instance accessor. static qtractorAudioFileFactory *getInstance(); // Supported file types. enum FileType { SndFile, VorbisFile, MadFile }; // Audio file format descriptor. struct FileFormat { FileType type; QString name; QString ext; int data; }; // The supported file types/format global map. typedef QList FileFormats; static const FileFormats& formats(); // The supported file types/extension map. typedef QHash FileTypes; static const FileTypes& types(); // The supported file types/names format lists. static const QStringList& filters(); static const QStringList& exts(); // Default audio file format accessors // (specific to capture/recording) static void setDefaultType( const QString& sExt, int iType, int iFormat = 0, int iQuality = 4); static QString defaultExt(); // Check whether given file type/format is valid. (static) static bool isValidFormat(const FileFormat *pFormat, int iFormat); // Factory method. (static) static qtractorAudioFile *createAudioFile (const QString& sFilename, unsigned short iChannels = 0, unsigned int iSampleRate = 0, unsigned int iBufferSize = 0, int iFormat = -1); protected: // Instance factory method. qtractorAudioFile *newAudioFile (const QString& sFilename, unsigned short iChannels, unsigned int iSampleRate, unsigned int iBufferSize, int iFormat); static int defaultFormat(); static int defaultQuality(); private: // Instance members. FileFormats m_formats; FileTypes m_types; // Supported filter strings. QStringList m_filters; QStringList m_exts; // Default file format/type (for capture/record) FileFormat *m_pDefaultFormat; int m_iDefaultFormat; int m_iDefaultQuality; // The singleton instance. static qtractorAudioFileFactory *g_pInstance; }; #endif // __qtractorAudioFile_h // end of qtractorAudioFile.h qtractor-1.5.11/src/PaxHeaders/qtractorInstrument.cpp0000644000000000000000000000013215124701674017746 xustar0030 mtime=1767080892.785263487 30 atime=1767080892.785263487 30 ctime=1767080892.785263487 qtractor-1.5.11/src/qtractorInstrument.cpp0000644000175000001440000007553315124701674017753 0ustar00rncbcusers// qtractorInstrument.cpp // /**************************************************************************** Copyright (C) 2005-2022, 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 "qtractorInstrument.h" #include #include #include #include #include #include #include #include // Deprecated QTextStreamFunctions/Qt namespaces workaround. #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) #define endl Qt::endl #endif //---------------------------------------------------------------------- // class qtractorInstrument -- instrument definition instance class. // // Retrieve patch/program list for given bank address. const qtractorInstrumentData& qtractorInstrument::patch ( int iBank ) const { if (m_pData->patches.contains(iBank)) return m_pData->patches[iBank]; return m_pData->patches[-1]; } // Retrieve key/notes list for given (bank, prog) pair. qtractorInstrumentData& qtractorInstrument::notes ( int iBank, int iProg ) const { if (m_pData->keys.contains(iBank)) { if (m_pData->keys[iBank].contains(iProg)) { return m_pData->keys[iBank][iProg]; } else { return m_pData->keys[iBank][-1]; } } else if (iBank >= 0) return notes(-1, iProg); return m_pData->keys[-1][-1]; } // Check if given (bank, prog) pair is a drum patch. bool qtractorInstrument::isDrum ( int iBank, int iProg ) const { if (m_pData->drums.contains(iBank)) { if (m_pData->drums[iBank].contains(iProg)) { return (bool) m_pData->drums[iBank][iProg]; } else { return (bool) m_pData->drums[iBank][-1]; } } else if (iBank >= 0) return isDrum(-1, iProg); return false; } //---------------------------------------------------------------------- // class qtractorInstrumentList -- A Cakewalk .ins file container class. // // Clear all contents. void qtractorInstrumentList::clearAll (void) { clear(); m_patches.clear(); m_notes.clear(); m_controllers.clear(); m_rpns.clear(); m_nrpns.clear(); m_files.clear(); } // Special list merge method. void qtractorInstrumentList::merge ( const qtractorInstrumentList& instruments ) { // Maybe its better not merging to itself. if (this == &instruments) return; // Names data lists merge... mergeDataList(m_patches, instruments.patches()); mergeDataList(m_notes, instruments.notes()); mergeDataList(m_controllers, instruments.controllers()); mergeDataList(m_rpns, instruments.rpns()); mergeDataList(m_nrpns, instruments.nrpns()); // Instrument merge... qtractorInstrumentList::ConstIterator it = instruments.constBegin(); const qtractorInstrumentList::ConstIterator& it_end = instruments.constEnd(); for ( ; it != it_end; ++it) { qtractorInstrument& instr = (*this)[it.key()]; instr = it.value(); } } // Special instrument data list merge method. void qtractorInstrumentList::mergeDataList ( qtractorInstrumentDataList& dst, const qtractorInstrumentDataList& src ) { qtractorInstrumentDataList::ConstIterator it = src.constBegin(); const qtractorInstrumentDataList::ConstIterator& it_end = src.constEnd(); for ( ; it != it_end; ++it) dst[it.key()] = it.value(); } // The official loaded file list. const QStringList& qtractorInstrumentList::files (void) const { return m_files; } // File load method. bool qtractorInstrumentList::load ( const QString& sFilename ) { // Open and read from real file. QFile file(sFilename); if (!file.open(QIODevice::ReadOnly)) return false; // Check for a soundfont... if (loadSoundFont(&file)) { file.close(); appendFile(sFilename); return true; } // Check for a MIDINameDocument... file.seek(0); QDomDocument doc; if (doc.setContent(&file) && loadMidiNameDocument(doc)) { file.close(); appendFile(sFilename); return true; } // Proceed with regulat Cakewalk .ins file... file.seek(0); enum FileSection { None = 0, PatchNames = 1, NoteNames = 2, ControllerNames = 3, RpnNames = 4, NrpnNames = 5, InstrDefs = 6 } sect = None; qtractorInstrument *pInstrument = nullptr; qtractorInstrumentData *pData = nullptr; QRegularExpression rxTitle ("^\\[([^\\]]+)\\]$"); QRegularExpression rxData ("^([0-9]+)=(.+)$"); QRegularExpression rxBasedOn ("^BasedOn=(.+)$"); QRegularExpression rxBankSel ("^BankSelMethod=(0|1|2|3)$"); QRegularExpression rxUseNotes("^UsesNotesAsControllers=(0|1)$"); QRegularExpression rxControl ("^Control=(.+)$"); QRegularExpression rxRpn ("^RPN=(.+)$"); QRegularExpression rxNrpn ("^NRPN=(.+)$"); QRegularExpression rxPatch ("^Patch\\[([0-9]+|\\*)\\]=(.+)$"); QRegularExpression rxKey ("^Key\\[([0-9]+|\\*),([0-9]+|\\*)\\]=(.+)$"); QRegularExpression rxDrum ("^Drum\\[([0-9]+|\\*),([0-9]+|\\*)\\]=(0|1)$"); QRegularExpressionMatch match; const QString s0_127 = "0..127"; const QString s1_128 = "1..128"; const QString s0_16383 = "0..16383"; const QString sAsterisk = "*"; // Read the file. unsigned int iLine = 0; QTextStream ts(&file); while (!ts.atEnd()) { // Read the line. ++iLine; const QString& sLine = ts.readLine().simplified(); // If not empty, nor a comment, call the server... if (sLine.isEmpty() || sLine.at(0) == ';') continue; // Check for section intro line... if (sLine.at(0) == '.') { pInstrument = nullptr; pData = nullptr; if (sLine == ".Patch Names") { sect = PatchNames; // m_patches.clear(); m_patches[s0_127].setName(s0_127); m_patches[s1_128].setName(s1_128); } else if (sLine == ".Note Names") { sect = NoteNames; // m_notes.clear(); m_notes[s0_127].setName(s0_127); } else if (sLine == ".Controller Names") { sect = ControllerNames; // m_controllers.clear(); m_controllers[s0_127].setName(s0_127); } else if (sLine == ".RPN Names") { sect = RpnNames; // m_rpns.clear(); m_rpns[s0_16383].setName(s0_16383); } else if (sLine == ".NRPN Names") { sect = NrpnNames; // m_nrpns.clear(); m_nrpns[s0_16383].setName(s0_16383); } else if (sLine == ".Instrument Definitions") { sect = InstrDefs; // clear(); } else { // Unknown section found... qWarning("%s(%d): %s: Unknown section.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); } // Go on... continue; } // Now it depends on the section... switch (sect) { case PatchNames: { match = rxTitle.match(sLine); if (match.hasMatch()) { // New patch name... const QString& sTitle = match.captured(1); pData = &(m_patches[sTitle]); pData->setName(sTitle); break; } if (pData == nullptr) { qWarning("%s(%d): %s: Untitled .Patch Names entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); break; } match = rxBasedOn.match(sLine); if (match.hasMatch()) { pData->setBasedOn(match.captured(1)); break; } match = rxData.match(sLine); if (match.hasMatch()) { (*pData)[match.captured(1).toInt()] = match.captured(2); } else { qWarning("%s(%d): %s: Unknown .Patch Names entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); } break; } case NoteNames: { match = rxTitle.match(sLine); if (match.hasMatch()) { // New note name... const QString& sTitle = match.captured(1); pData = &(m_notes[sTitle]); pData->setName(sTitle); break; } if (pData == nullptr) { qWarning("%s(%d): %s: Untitled .Note Names entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); break; } match = rxBasedOn.match(sLine); if (match.hasMatch()) { pData->setBasedOn(match.captured(1)); break; } match = rxData.match(sLine); if (match.hasMatch()) { (*pData)[match.captured(1).toInt()] = match.captured(2); } else { qWarning("%s(%d): %s: Unknown .Note Names entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); } break; } case ControllerNames: { match = rxTitle.match(sLine); if (match.hasMatch()) { // New controller name... const QString& sTitle = match.captured(1); pData = &(m_controllers[sTitle]); pData->setName(sTitle); break; } if (pData == nullptr) { qWarning("%s(%d): %s: Untitled .Controller Names entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); break; } match = rxBasedOn.match(sLine); if (match.hasMatch()) { pData->setBasedOn(match.captured(1)); break; } match = rxData.match(sLine); if (match.hasMatch()) { (*pData)[match.captured(1).toInt()] = match.captured(2); } else { qWarning("%s(%d): %s: Unknown .Controller Names entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); } break; } case RpnNames: { match = rxTitle.match(sLine); if (match.hasMatch()) { // New RPN name... const QString& sTitle = match.captured(1); pData = &(m_rpns[sTitle]); pData->setName(sTitle); break; } if (pData == nullptr) { qWarning("%s(%d): %s: Untitled .RPN Names entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); break; } match = rxBasedOn.match(sLine); if (match.hasMatch()) { pData->setBasedOn(match.captured(1)); break; } match = rxData.match(sLine); if (match.hasMatch()) { (*pData)[match.captured(1).toInt()] = match.captured(2); } else { qWarning("%s(%d): %s: Unknown .RPN Names entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); } break; } case NrpnNames: { match = rxTitle.match(sLine); if (match.hasMatch()) { // New NRPN name... const QString& sTitle = match.captured(1); pData = &(m_nrpns[sTitle]); pData->setName(sTitle); break; } if (pData == nullptr) { qWarning("%s(%d): %s: Untitled .NRPN Names entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); break; } match = rxBasedOn.match(sLine); if (match.hasMatch()) { pData->setBasedOn(match.captured(1)); break; } match = rxData.match(sLine); if (match.hasMatch()) { (*pData)[match.captured(1).toInt()] = match.captured(2); } else { qWarning("%s(%d): %s: Unknown .NRPN Names entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); } break; } case InstrDefs: { match = rxTitle.match(sLine); if (match.hasMatch()) { // New instrument definition... const QString& sTitle = match.captured(1); pInstrument = &((*this)[sTitle]); pInstrument->setInstrumentName(sTitle); break; } if (pInstrument == nullptr) { // New instrument definition (use filename as default) const QString& sTitle = QFileInfo(sFilename).completeBaseName(); pInstrument = &((*this)[sTitle]); pInstrument->setInstrumentName(sTitle); } match = rxBankSel.match(sLine); if (match.hasMatch()) { pInstrument->setBankSelMethod( match.captured(1).toInt()); break; } match = rxUseNotes.match(sLine); if (match.hasMatch()) { pInstrument->setUsesNotesAsControllers( bool(match.captured(1).toInt())); break; } match = rxPatch.match(sLine); if (match.hasMatch()) { const QString& cap1 = match.captured(1); const int iBank = (cap1 == sAsterisk ? -1 : cap1.toInt()); pInstrument->setPatch(iBank, m_patches[match.captured(2)]); break; } match = rxControl.match(sLine); if (match.hasMatch()) { pInstrument->setControllers(m_controllers[match.captured(1)]); break; } match = rxRpn.match(sLine); if (match.hasMatch()) { pInstrument->setRpns(m_rpns[match.captured(1)]); break; } match = rxNrpn.match(sLine); if (match.hasMatch()) { pInstrument->setNrpns(m_nrpns[match.captured(1)]); break; } match = rxKey.match(sLine); if (match.hasMatch()) { const QString& cap1 = match.captured(1); const QString& cap2 = match.captured(2); const int iBank = (cap1 == sAsterisk ? -1 : cap1.toInt()); const int iProg = (cap2 == sAsterisk ? -1 : cap2.toInt()); pInstrument->setNotes(iBank, iProg, m_notes[match.captured(3)]); break; } match = rxDrum.match(sLine); if (match.hasMatch()) { const QString& cap1 = match.captured(1); const QString& cap2 = match.captured(2); const int iBank = (cap1 == sAsterisk ? -1 : cap1.toInt()); const int iProg = (cap2 == sAsterisk ? -1 : cap2.toInt()); pInstrument->setDrum(iBank, iProg, bool(match.captured(3).toInt())); } else { qWarning("%s(%d): %s: Unknown .Instrument Definitions entry.", sFilename.toUtf8().constData(), iLine, sLine.toUtf8().constData()); } break; } default: break; } } // Ok. We've read it all. file.close(); // We're in business... appendFile(sFilename); return true; } // File save method. bool qtractorInstrumentList::save ( const QString& sFilename ) const { // Open and write into real file. QFile file(sFilename); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false; // A visula separator line. const QString sepl = "; -----------------------------" "------------------------------------------------"; // Write the file. QTextStream ts(&file); ts << sepl << endl; ts << "; " << QObject::tr("Cakewalk Instrument Definition File") << endl; ts << ";" << endl; ts << "; " << QObject::tr("File") << ": " << QFileInfo(sFilename).fileName() << endl; ts << "; " << QObject::tr("Date") << ": " << QDate::currentDate().toString("MMM dd yyyy") << " " << QTime::currentTime().toString("hh:mm:ss") << endl; ts << ";" << endl; // - Patch Names... ts << sepl << endl << endl; ts << ".Patch Names" << endl; saveDataList(ts, m_patches); // - Note Names... ts << sepl << endl << endl; ts << ".Note Names" << endl; saveDataList(ts, m_notes); // - Controller Names... ts << sepl << endl << endl; ts << ".Controller Names" << endl; saveDataList(ts, m_controllers); // - RPN Names... ts << sepl << endl << endl; ts << ".RPN Names" << endl; saveDataList(ts, m_rpns); // - NRPN Names... ts << sepl << endl << endl; ts << ".NRPN Names" << endl; saveDataList(ts, m_nrpns); // - Instrument Definitions... ts << sepl << endl << endl; ts << ".Instrument Definitions" << endl; ts << endl; qtractorInstrumentList::ConstIterator iter = constBegin(); const qtractorInstrumentList::ConstIterator& iter_end = constEnd(); for ( ; iter != iter_end; ++iter) { const qtractorInstrument& instr = *iter; ts << "[" << instr.instrumentName() << "]" << endl; if (instr.bankSelMethod() > 0) ts << "BankSelMethod=" << instr.bankSelMethod() << endl; if (!instr.controllers().name().isEmpty()) ts << "Control=" << instr.controllers().name() << endl; if (!instr.rpns().name().isEmpty()) ts << "RPN=" << instr.rpns().name() << endl; if (!instr.nrpns().name().isEmpty()) ts << "NRPN=" << instr.nrpns().name() << endl; // - Patches... const qtractorInstrumentPatches& patches = instr.patches(); qtractorInstrumentPatches::ConstIterator pit = patches.constBegin(); const qtractorInstrumentPatches::ConstIterator& pit_end = patches.constEnd(); for ( ; pit != pit_end; ++pit) { const QString& sPatch = pit.value().name(); if (!sPatch.isEmpty()) { int iBank = pit.key(); const QString& sBank = (iBank < 0 ? QString("*") : QString::number(iBank)); ts << "Patch[" << sBank << "]=" << sPatch << endl; } } // - Keys... const qtractorInstrumentKeys& keys = instr.keys(); qtractorInstrumentKeys::ConstIterator kit = keys.constBegin(); const qtractorInstrumentKeys::ConstIterator kit_end = keys.constEnd(); for ( ; kit != kit_end; ++kit) { const int iBank = kit.key(); const QString& sBank = (iBank < 0 ? QString("*") : QString::number(iBank)); const qtractorInstrumentNotes& notes = kit.value(); qtractorInstrumentNotes::ConstIterator nit = notes.constBegin(); const qtractorInstrumentNotes::ConstIterator& nit_end = notes.constEnd(); for ( ; nit != nit_end; ++nit) { const QString& sKey = nit.value().name(); if (!sKey.isEmpty()) { const int iProg = nit.key(); const QString& sProg = (iProg < 0 ? QString("*") : QString::number(iProg)); ts << "Key[" << sBank << "," << sProg << "]=" << sKey << endl; } } } // - Drums... const qtractorInstrumentDrums& drums = instr.drums(); qtractorInstrumentDrums::ConstIterator dit = drums.constBegin(); const qtractorInstrumentDrums::ConstIterator& dit_end = drums.constEnd(); for ( ; dit != dit_end; ++dit) { const int iBank = dit.key(); const QString& sBank = (iBank < 0 ? QString("*") : QString::number(iBank)); const qtractorInstrumentDrumFlags& flags = dit.value(); qtractorInstrumentDrumFlags::ConstIterator fit = flags.constBegin(); const qtractorInstrumentDrumFlags::ConstIterator& fit_end = flags.constEnd(); for ( ; fit != fit_end; ++fit) { const int iProg = fit.key(); const QString& sProg = (iProg < 0 ? QString("*") : QString::number(iProg)); ts << "Drum[" << sBank << "," << sProg << "]=" << fit.value() << endl; } } ts << endl; } // Done. file.close(); return true; } void qtractorInstrumentList::saveDataList ( QTextStream& ts, const qtractorInstrumentDataList& list ) const { ts << endl; qtractorInstrumentDataList::ConstIterator it = list.constBegin(); const qtractorInstrumentDataList::ConstIterator& it_end = list.constEnd(); for ( ; it != it_end; ++it) { const QString& sName = it.value().name(); if (!sName.isEmpty()) { ts << "[" << sName << "]" << endl; saveData(ts, it.value()); } } } void qtractorInstrumentList::saveData ( QTextStream& ts, const qtractorInstrumentData& data ) const { if (!data.basedOn().isEmpty()) ts << "BasedOn=" << data.basedOn() << endl; qtractorInstrumentData::ConstIterator it = data.constBegin(); const qtractorInstrumentData::ConstIterator& it_end = data.constEnd(); for ( ; it != it_end; ++it) ts << it.key() << "=" << it.value() << endl; ts << endl; } // SoundFont chunk record header. typedef struct _SoundFontChunk { char id[4]; int32_t size; } SoundFontChunk; // Special SoundFont loader. bool qtractorInstrumentList::loadSoundFont ( QFile *pFile ) { pFile->seek(0); // Check RIFF file header... SoundFontChunk chunk; pFile->read((char *) &chunk, sizeof(chunk)); if (::strncmp(chunk.id, "RIFF", 4) != 0) return false; // Check file id... pFile->read(chunk.id, sizeof(chunk.id)); if (::strncmp(chunk.id, "sfbk", 4) != 0) return false; for (;;) { pFile->read((char *) &chunk, sizeof(chunk)); if (pFile->atEnd()) break; int iSize = chunk.size; if (::strncmp(chunk.id, "LIST", 4) == 0) { // Process a list chunk. pFile->read(chunk.id, sizeof(chunk.id)); iSize -= sizeof(chunk.id); if (::strncmp(chunk.id, "pdta", 4) == 0) { loadSoundFontPresets(pFile, iSize); break; // We have enough! } } // Maybe illegal; ignored; skip it. pFile->seek(pFile->pos() + iSize); } return true; } void qtractorInstrumentList::loadSoundFontPresets ( QFile *pFile, int iSize ) { // Parse the buffer... while (iSize > 0) { // Read a sub-chunk... SoundFontChunk chunk; pFile->read((char *) &chunk, sizeof(chunk)); if (pFile->atEnd()) break; iSize -= sizeof(chunk); if (::strncmp(chunk.id, "phdr", 4) == 0) { // Instrument name based on SoundFont file name... const QString& sInstrumentName = QFileInfo(pFile->fileName()).baseName(); qtractorInstrument& instr = (*this)[sInstrumentName]; instr.setInstrumentName(sInstrumentName); // Preset header... int iDrums = 0; const int iPresets = (chunk.size / 38); for (int i = 0; i < iPresets; ++i) { char name[20]; int16_t prog; int16_t bank; pFile->read(name, sizeof(name)); pFile->read((char *) &(prog), sizeof(int16_t)); pFile->read((char *) &(bank), sizeof(int16_t)); pFile->seek(pFile->pos() + 14); // Add actual preset name... if (i < iPresets - 1) { const QString& sBank = QObject::tr("%1 Bank %2") .arg(instr.instrumentName()).arg(int(bank)); qtractorInstrumentData& patch = m_patches[sBank]; patch.setName(sBank); instr.setPatch(int(bank), patch); patch[int(prog)] = QString(name).simplified(); if (bank == 128 && iDrums == 0) { instr.setDrum(128, -1, true); // Usual SF2 standard ;) ++iDrums; } } } // Enough done. break; } // Ignored; skip it. pFile->seek(pFile->pos() + chunk.size); iSize -= chunk.size; } } // Special MIDINameDocument loader. bool qtractorInstrumentList::loadMidiNameDocument ( const QDomDocument& doc ) { QDomElement eRoot = doc.documentElement(); if (eRoot.tagName() != "MIDINameDocument") return false; for (QDomNode nItem = eRoot.firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { // Convert item node to element... QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; const QString& sTagName = eItem.tagName(); if (sTagName == "MasterDeviceNames") loadMidiDeviceNames(&eItem); } return true; } void qtractorInstrumentList::loadMidiDeviceNames ( QDomElement *pElement ) { QString sInstrumentName; for (QDomNode nItem = pElement->firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { // Convert item node to element... QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; const QString& sTagName = eItem.tagName(); const QString& sName = eItem.attribute("Name"); if (sTagName == "Manufacturer") { if (!sInstrumentName.isEmpty()) sInstrumentName += ' '; sInstrumentName += eItem.text(); } else if (sTagName == "Model") { if (!sInstrumentName.isEmpty()) sInstrumentName += ' '; sInstrumentName += eItem.text(); } else if (sTagName == "ChannelNameSet") { if (!sInstrumentName.isEmpty()) sInstrumentName += ' '; sInstrumentName += sName; qtractorInstrument& instr = (*this)[sInstrumentName]; instr.setInstrumentName(sInstrumentName); loadMidiChannelNameSet(&eItem, instr); } else if (sTagName == "PatchNameList") { qtractorInstrument& instr = (*this)[sInstrumentName]; loadMidiPatchNameList(&eItem, instr, sName); } else if (sTagName == "NoteNameList") loadMidiNoteNameList(&eItem, sName); else if (sTagName == "ControlNameList") loadMidiControlNameList(&eItem, sName); } } void qtractorInstrumentList::loadMidiChannelNameSet ( QDomElement *pElement, qtractorInstrument& instr ) { for (QDomNode nItem = pElement->firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; const QString& sTagName = eItem.tagName(); const QString& sName = eItem.attribute("Name"); if (sTagName == "PatchBank") loadMidiPatchBank(&eItem, instr, sName); else if (sTagName == "PatchNameList") loadMidiPatchNameList(&eItem, instr, sName); else if (sTagName == "NoteNameList") loadMidiNoteNameList(&eItem, sName); else if (sTagName == "ControlNameList") loadMidiControlNameList(&eItem, sName); else if (sTagName == "UsesPatchNameList") instr.setPatch(-1, m_patches[sName]); else if (sTagName == "UsesNotesNameList") instr.setNotes(-1, -1, m_notes[sName]); else if (sTagName == "UsesControlNameList") { instr.setControllers(m_controllers[sName]); instr.setRpns(m_rpns[sName]); instr.setNrpns(m_nrpns[sName]); } } } void qtractorInstrumentList::loadMidiPatchBank ( QDomElement *pElement, qtractorInstrument& instr, const QString& sName ) { int iBank = -1; for (QDomNode nItem = pElement->firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; const QString& sTagName = eItem.tagName(); QString sSubName = eItem.attribute("Name"); if (sSubName.isEmpty()) sSubName = sName; if (sTagName == "MIDICommands") { iBank = 0; for (QDomNode nCommand = eItem.firstChild(); !nCommand.isNull(); nCommand = nCommand.nextSibling()) { QDomElement eCommand = nCommand.toElement(); if (eCommand.isNull()) continue; if (eCommand.tagName() == "ControlChange") { unsigned short iControl = eCommand.attribute("Control").toUShort(); unsigned short iValue = eCommand.attribute("Value").toUShort(); if (iControl == 0) // Bank MSB. iBank |= ((iValue << 7) & 0x3f80); else if (iControl == 32) // Bank LSB. iBank |= (iValue & 0x7f); } } } else if (sTagName == "PatchNameList") { loadMidiPatchNameList(&eItem, instr, sSubName); instr.setPatch(iBank, m_patches[sSubName]); } else if (sTagName == "UsesPatchNameList") instr.setPatch(iBank, m_patches[sSubName]); } } void qtractorInstrumentList::loadMidiPatchNameList ( QDomElement *pElement, qtractorInstrument& instr, const QString& sName ) { qtractorInstrumentData& patches = m_patches[sName]; patches.setName(sName); for (QDomNode nItem = pElement->firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; const QString& sTagName = eItem.tagName(); if (sTagName == "Patch") { int iBank = -1; int iProg = -1; const QString& sProgramChange = eItem.attribute("ProgramChange"); const QString& sPatchNumber = eItem.attribute("Number"); QString sPatchName = eItem.attribute("Name"); if (sPatchName.isEmpty()) sPatchName = sName; if (!sPatchNumber.isEmpty()) sPatchName = sPatchNumber + ' ' + sPatchName; if (!sProgramChange.isEmpty()) { iProg = sProgramChange.toInt(); patches[iProg] = sPatchName; } for (QDomNode nSubItem = eItem.firstChild(); !nSubItem.isNull(); nSubItem = nSubItem.nextSibling()) { QDomElement eSubItem = nSubItem.toElement(); if (eSubItem.isNull()) continue; QString sSubName = eSubItem.attribute("Name"); if (sSubName.isEmpty()) sSubName = sPatchName; const QString& sSubTagName = eSubItem.tagName(); if (sSubTagName == "PatchMIDICommands") { iBank = 0; for (QDomNode nCommand = eSubItem.firstChild(); !nCommand.isNull(); nCommand = nCommand.nextSibling()) { QDomElement eCommand = nCommand.toElement(); if (eCommand.isNull()) continue; const QString& sCommandTagName = eCommand.tagName(); if (sCommandTagName == "ControlChange") { const unsigned short iControl = eCommand.attribute("Control").toUShort(); const unsigned short iValue = eCommand.attribute("Value").toUShort(); if (iControl == 0) // Bank MSB. iBank |= ((iValue << 7) & 0x3f80); else if (iControl == 32) // Bank LSB. iBank |= (iValue & 0x7f); } else if (sCommandTagName == "ProgramChange") iProg = eCommand.attribute("Number").toInt(); } } else if (sSubTagName == "NoteNameList") loadMidiNoteNameList(&eSubItem, sSubName); else if (sSubTagName == "ControlNameList") loadMidiControlNameList(&eSubItem, sSubName); else if (sSubTagName == "UsesNoteNameList") instr.setNotes(iBank, iProg, m_notes[sSubName]); else if (sSubTagName == "UsesControlNameList") { instr.setControllers(m_controllers[sSubName]); instr.setRpns(m_rpns[sSubName]); instr.setNrpns(m_nrpns[sSubName]); } } // Is it a brand new patch bank, program?... if (iBank >= 0) { const QString& sBank = QString(" (%1)").arg(iBank); QString sBankName = instr.bankName(iBank); if (sBankName.isEmpty()) { sBankName = sName + sBank; } else { const QString sep(", "); QStringList list = sBankName .remove(sName).remove(sBank) #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) .split(sep, Qt::SkipEmptyParts); #else .split(sep, QString::SkipEmptyParts); #endif list.append(sName + sBank); sBankName = list.join(sep); } instr.setBankName(iBank, sBankName); } if (iProg >= 0) instr.setProgName(iBank, iProg, sPatchName); } } } void qtractorInstrumentList::loadMidiNoteNameList ( QDomElement *pElement, const QString& sName ) { qtractorInstrumentData& notes = m_notes[sName]; notes.setName(sName); for (QDomNode nItem = pElement->firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; const QString& sTagName = eItem.tagName(); if (sTagName == "Note") { const QString& sNote = eItem.attribute("Name"); const unsigned short iNote = eItem.attribute("Number").toUShort(); notes[int(iNote)] = sNote; } else if (sTagName == "NoteGroup") { QString sSubName = eItem.attribute("Name"); if (!sSubName.isEmpty()) sSubName += ' '; for (QDomNode nSubItem = eItem.firstChild(); !nSubItem.isNull(); nSubItem = nSubItem.nextSibling()) { QDomElement eSubItem = nSubItem.toElement(); if (eSubItem.isNull()) continue; const QString& sSubTagName = eSubItem.tagName(); if (sSubTagName == "Note") { const QString& sNote = eSubItem.attribute("Name"); const int iNote = eSubItem.attribute("Number").toInt(); notes[iNote] = sSubName + sNote; } } } } } void qtractorInstrumentList::loadMidiControlNameList ( QDomElement *pElement, const QString& sName ) { qtractorInstrumentData& controllers = m_controllers[sName]; qtractorInstrumentData& rpns = m_rpns[sName]; qtractorInstrumentData& nrpns = m_nrpns[sName]; controllers.setName(sName); rpns.setName(sName); nrpns.setName(sName); for (QDomNode nItem = pElement->firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; const QString& sTagName = eItem.tagName(); if (sTagName == "Control") { const QString& sControlType = eItem.attribute("Type"); const QString& sControl = eItem.attribute("Name"); const int iControl = eItem.attribute("Number").toInt(); if (sControlType == "NRPN") nrpns[iControl] = sControl; else if (sControlType == "RPN") rpns[iControl] = sControl; else // if (sControlType == "7bit" || sControlType == "14bit") controllers[iControl] = sControl; } } } // end of qtractorInstrument.cpp qtractor-1.5.11/src/PaxHeaders/appdata0000644000000000000000000000013215124701674014647 xustar0030 mtime=1767080892.773779218 30 atime=1767080892.773458803 30 ctime=1767080892.773779218 qtractor-1.5.11/src/appdata/0000755000175000001440000000000015124701674014714 5ustar00rncbcusersqtractor-1.5.11/src/appdata/PaxHeaders/org.rncbc.qtractor.metainfo.xml0000644000000000000000000000013115124701674022761 xustar0030 mtime=1767080892.773779218 29 atime=1767080892.77374496 30 ctime=1767080892.773779218 qtractor-1.5.11/src/appdata/org.rncbc.qtractor.metainfo.xml0000644000175000001440000000400215124701674022746 0ustar00rncbcusers org.rncbc.qtractor FSFAP GPL-2.0+ Qtractor An Audio/MIDI multi-track sequencer

Qtractor is an Audio/MIDI multi-track sequencer application written in C++ around the Qt framework using Qt Designer.

The initial target platform will be Linux, where the Jack Audio Connection Kit (JACK) for audio, and the Advanced Linux Sound Architecture (ALSA) for MIDI, are the main infrastructures to evolve as a fairly-featured Linux Desktop Audio Workstation GUI, specially dedicated to the personal home-studio.

org.rncbc.qtractor.desktop qtractor qtractor_plugin_scan https://qtractor.org/image/qtractor-screenshot21.png The main window showing the application in action Audio MIDI Multitrack Sequencer DAW ALSA JACK LADSPA DSSI VST VST3 CLAP LV2 Qt https://qtractor.org rncbc.org rncbc aka. Rui Nuno Capela rncbc@rncbc.org
qtractor-1.5.11/src/appdata/PaxHeaders/org.rncbc.qtractor.desktop0000644000000000000000000000012715124701674022036 xustar0029 mtime=1767080892.77374496 29 atime=1767080892.77374496 29 ctime=1767080892.77374496 qtractor-1.5.11/src/appdata/org.rncbc.qtractor.desktop0000644000175000001440000000146015124701674022023 0ustar00rncbcusers[Desktop Entry] Name=Qtractor Version=1.0 GenericName=Multi-track Sequencer GenericName[fr]=Séquenceur multi-pistes GenericName[de]=Mehrspuriger Sequenzer Comment=Qtractor is an Audio/MIDI multi-track sequencer application Comment[fr]=Qtractor est un séquenceur multi-pistes audio/MIDI Comment[de]=Qtractor ist ein Audio / MIDI-Mehrspur-Sequenzer Exec=qtractor -platform xcb %f Icon=org.rncbc.qtractor Categories=Audio;AudioVideo;Midi;Sequencer;X-Multitrack;X-Alsa;X-Jack;Qt; MimeType=application/x-qtractor-session;application/x-qtractor-template;application/x-qtractor-archive; Keywords=Audio;MIDI;Multitrack;Sequencer;DAW;ALSA;JACK;LADSPA;DSSI;VST;VST2;VST3;CLAP;LV2;Qt; Terminal=false Type=Application StartupWMClass=qtractor X-Window-Icon=qtractor X-SuSE-translate=true X-NSM-Capable=true X-NSM-Exec=qtractor qtractor-1.5.11/src/PaxHeaders/qtractorOptionsForm.h0000644000000000000000000000013215124701674017522 xustar0030 mtime=1767080892.797263437 30 atime=1767080892.797263437 30 ctime=1767080892.797263437 qtractor-1.5.11/src/qtractorOptionsForm.h0000644000175000001440000001002015124701674017503 0ustar00rncbcusers// qtractorOptionsForm.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorOptionsForm_h #define __qtractorOptionsForm_h #include "ui_qtractorOptionsForm.h" // Forward declarations... class qtractorOptions; class qtractorTimeScale; //---------------------------------------------------------------------------- // qtractorOptionsForm -- UI wrapper form. class qtractorOptionsForm : public QDialog { Q_OBJECT public: // Constructor. qtractorOptionsForm(QWidget *pParent = nullptr); // Destructor. ~qtractorOptionsForm(); void setOptions(qtractorOptions *pOptions); qtractorOptions *options() const; // Spacial meter colors dirty flag. bool isDirtyMeterColors() const; // Spacial custom color themes dirty flag. bool isDirtyCustomColorThemes() const; protected slots: void accept(); void reject(); void changed(); void audioCaptureTypeChanged(int); void chooseMetroBarFilename(); void chooseMetroBeatFilename(); void updateMetroNoteNames(); void displayFormatChanged(int); void editCustomColorThemes(); void chooseCustomStyleSheet(); void chooseCustomIconsTheme(); void changeAudioMeterLevel(int); void changeMidiMeterLevel(int); void changeAudioMeterColor(const QString&); void changeMidiMeterColor(const QString&); void chooseAudioMeterColor(); void chooseMidiMeterColor(); void resetMeterColors(); void choosePluginType(int); void changePluginPath(const QString&); void choosePluginPath(); void selectPluginPath(); void addPluginPath(); void removePluginPath(); void moveUpPluginPath(); void moveDownPluginPath(); void chooseLv2PresetDir(); void changePluginBlacklist(const QString&); void choosePluginBlacklist(); void selectPluginBlacklist(); void addPluginBlacklist(); void removePluginBlacklist(); void clearPluginBlacklist(); void chooseMessagesFont(); void chooseMessagesLogPath(); void chooseSessionTemplatePath(); void stabilizeForm(); protected: // Browse for an existing audio filename. QString getOpenAudioFileName( const QString& sTitle, const QString& sFilename); // Special combo-box color item helpers. void updateColorText(QLineEdit *pLineEdit, const QColor& color); // Custom color/style themes settlers. void resetCustomColorThemes(const QString& sCustomColorTheme); void resetCustomStyleThemes(const QString& sCustomStyleTheme); // Session format ext/suffix table initializer. static void initSessionFormats(); private: // The Qt-designer UI struct... Ui::qtractorOptionsForm m_ui; // Instance variables... qtractorOptions *m_pOptions; int m_iDirtyCount; qtractorTimeScale *m_pTimeScale; // Meter colors. QColor *m_paAudioMeterColors; QColor *m_paMidiMeterColors; int m_iDirtyMeterColors; // Custom color themes flag. int m_iDirtyCustomColorThemes; // Plug-ins path cache. QStringList m_ladspaPaths; QStringList m_dssiPaths; QStringList m_vst2Paths; QStringList m_vst3Paths; QStringList m_clapPaths; QStringList m_lv2Paths; int m_iDirtyLadspaPaths; int m_iDirtyDssiPaths; int m_iDirtyVst2Paths; int m_iDirtyVst3Paths; int m_iDirtyClapPaths; int m_iDirtyLv2Paths; int m_iDirtyBlacklist; }; #endif // __qtractorOptionsForm_h // end of qtractorOptionsForm.h qtractor-1.5.11/src/PaxHeaders/qtractorTimeStretcher.cpp0000644000000000000000000000012715124701674020364 xustar0029 mtime=1767080892.80126342 29 atime=1767080892.80126342 29 ctime=1767080892.80126342 qtractor-1.5.11/src/qtractorTimeStretcher.cpp0000644000175000001440000002041315124701674020350 0ustar00rncbcusers// qtractorTimeStretcher.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorTimeStretcher.h" #include #include "qdebug.h" // Constructor. qtractorTimeStretcher::qtractorTimeStretcher ( unsigned short iChannels, unsigned int iSampleRate, float fTimeStretch, float fPitchShift, unsigned int iFlags, unsigned int iBufferSize ) : m_pWsolaTimeStretcher(nullptr) #ifdef CONFIG_LIBRUBBERBAND , m_pRubberBandStretcher(nullptr) , m_iRubberBandChannels(iChannels) , m_iRubberBandLatency(0) , m_iRubberBandPadding(0) , m_iRubberBandFrames(0) , m_ppRubberBandFrames(nullptr) , m_ppRubberBandBuffer(nullptr) , m_bRubberBandFlush(false) #endif { if (fTimeStretch > 0.0f) { if (fTimeStretch < 0.1f) fTimeStretch = 0.1f; else if (fTimeStretch > 10.0f) fTimeStretch = 10.0f; else if (fTimeStretch > 1.0f - 1e-3f && fTimeStretch < 1.0f + 1e-3f) fTimeStretch = 0.0f; // Disable time-stretcher. } if (fPitchShift > 0.0f) { if (fPitchShift < 0.1f) fPitchShift = 0.1f; else if (fPitchShift > 10.0f) fPitchShift = 10.0f; else if (fPitchShift > 1.0f - 1e-3f && fPitchShift < 1.0f + 1e-3f) fPitchShift = 0.0f; // Disable pitch-shifter. } if (fTimeStretch > 0.0f && (iFlags & WsolaTimeStretch)) { m_pWsolaTimeStretcher = new qtractorWsolaTimeStretcher(iChannels, iSampleRate); m_pWsolaTimeStretcher->setTempo(1.0f / fTimeStretch); m_pWsolaTimeStretcher->setQuickSeek(iFlags & WsolaQuickSeek); fTimeStretch = 0.0f; // Avoid RubberBandStretcher... } #ifdef CONFIG_LIBRUBBERBAND if (fTimeStretch > 0.0f || fPitchShift > 0.0f) { if (fTimeStretch < 1e-3f) fTimeStretch = 1.0f; if (fPitchShift < 1e-3f) fPitchShift = 1.0f; RubberBand::RubberBandStretcher::Options options = RubberBand::RubberBandStretcher::OptionProcessRealTime; if (iFlags & RubberBandFormant) options |= RubberBand::RubberBandStretcher::OptionFormantPreserved; #ifdef CONFIG_LIBRUBBERBAND_R3 if (iFlags & RubberBandFinerR3) options |= RubberBand::RubberBandStretcher::OptionEngineFiner; #endif m_pRubberBandStretcher = new RubberBand::RubberBandStretcher( iSampleRate, iChannels, options, fTimeStretch, fPitchShift); m_pRubberBandStretcher->setMaxProcessSize(iBufferSize); m_ppRubberBandBuffer = new float * [m_iRubberBandChannels]; #ifdef CONFIG_LIBRUBBERBAND_R3 m_iRubberBandLatency = m_pRubberBandStretcher->getStartDelay(); m_iRubberBandPadding = m_pRubberBandStretcher->getPreferredStartPad(); m_iRubberBandPadding += m_iRubberBandLatency; m_iRubberBandFrames = qMax(m_iRubberBandLatency, m_iRubberBandPadding); #else m_iRubberBandLatency = m_pRubberBandStretcher->getLatency(); m_iRubberBandPadding = m_iRubberBandLatency; m_iRubberBandFrames = m_iRubberBandLatency; #endif if (m_iRubberBandFrames > 0) { m_ppRubberBandFrames = new float * [m_iRubberBandChannels]; m_ppRubberBandFrames[0] = new float [m_iRubberBandFrames]; ::memset(m_ppRubberBandFrames[0], 0, m_iRubberBandFrames * sizeof(float)); for (unsigned short i = 1; i < m_iRubberBandChannels; ++i) m_ppRubberBandFrames[i] = m_ppRubberBandFrames[0]; } } #endif } // Destructor. qtractorTimeStretcher::~qtractorTimeStretcher() { if (m_pWsolaTimeStretcher) delete m_pWsolaTimeStretcher; #ifdef CONFIG_LIBRUBBERBAND if (m_ppRubberBandBuffer) delete [] m_ppRubberBandBuffer; if (m_ppRubberBandFrames) { delete [] m_ppRubberBandFrames[0]; delete [] m_ppRubberBandFrames; } if (m_pRubberBandStretcher) delete m_pRubberBandStretcher; #endif } // Adds frames of samples into the input buffer. void qtractorTimeStretcher::process ( float **ppFrames, unsigned int iFrames ) { if (m_pWsolaTimeStretcher) { m_pWsolaTimeStretcher->putFrames(ppFrames, iFrames); #ifdef CONFIG_LIBRUBBERBAND if (m_pRubberBandStretcher) { unsigned int noffs = 0; unsigned int nread = iFrames; while (nread > 0 && noffs < iFrames) { for (unsigned short i = 0; i < m_iRubberBandChannels; ++i) m_ppRubberBandBuffer[i] = ppFrames[i] + noffs; nread = m_pWsolaTimeStretcher->receiveFrames( m_ppRubberBandBuffer, iFrames - noffs); noffs += nread; } iFrames = noffs; } #endif } #ifdef CONFIG_LIBRUBBERBAND if (m_pRubberBandStretcher) { // Process the first dummy empty buffer... if (m_iRubberBandPadding > 0) { m_pRubberBandStretcher->process( m_ppRubberBandFrames, m_iRubberBandPadding, false); m_iRubberBandPadding = 0; } m_pRubberBandStretcher->process(ppFrames, iFrames, false); } #endif } // Copies requested frames output buffer and removes them // from the sample buffer. If there are less than available() // samples in the buffer, returns all that available. duh? unsigned int qtractorTimeStretcher::retrieve ( float **ppFrames, unsigned int iFrames ) { #ifdef CONFIG_LIBRUBBERBAND if (m_pRubberBandStretcher) { unsigned int nread = m_pRubberBandStretcher->retrieve(ppFrames, iFrames); if (nread > 0 && m_iRubberBandLatency > 0) { if (m_iRubberBandLatency > nread) { m_iRubberBandLatency -= nread; nread = 0; } else { unsigned int noffs = m_iRubberBandLatency; nread -= m_iRubberBandLatency; m_iRubberBandLatency = 0; for (unsigned int i = 0; i < m_iRubberBandChannels; ++i) { float *pFrames = ppFrames[i]; ::memmove(pFrames, pFrames + noffs, nread * sizeof(float)); } } } return nread; } #endif return (m_pWsolaTimeStretcher ? m_pWsolaTimeStretcher->receiveFrames(ppFrames, iFrames) : 0); } // Returns number of frames currently available. unsigned int qtractorTimeStretcher::available (void) const { int iAvailable = 0; #ifdef CONFIG_LIBRUBBERBAND if (m_pRubberBandStretcher) iAvailable = m_pRubberBandStretcher->available(); else #endif if (m_pWsolaTimeStretcher) iAvailable = m_pWsolaTimeStretcher->frames(); return (iAvailable > 0 ? iAvailable : 0); } // Flush any last samples that are hiding // in the internal processing pipeline. void qtractorTimeStretcher::flush (void) { if (m_pWsolaTimeStretcher) m_pWsolaTimeStretcher->flushInput(); #ifdef CONFIG_LIBRUBBERBAND if (m_pRubberBandStretcher && !m_bRubberBandFlush) { // Process a last dummy empty buffer... if (m_iRubberBandFrames > 0) { m_pRubberBandStretcher->process( m_ppRubberBandFrames, m_iRubberBandFrames, true); } m_bRubberBandFlush = true; } #endif } // Clears all buffers. void qtractorTimeStretcher::reset (void) { if (m_pWsolaTimeStretcher) m_pWsolaTimeStretcher->clear(); #ifdef CONFIG_LIBRUBBERBAND if (m_pRubberBandStretcher) { m_pRubberBandStretcher->reset(); #ifdef CONFIG_LIBRUBBERBAND_R3 m_iRubberBandLatency = m_pRubberBandStretcher->getStartDelay(); m_iRubberBandPadding = m_pRubberBandStretcher->getPreferredStartPad(); m_iRubberBandFrames = qMax(m_iRubberBandLatency, m_iRubberBandPadding); #else m_iRubberBandLatency = m_pRubberBandStretcher->getLatency(); m_iRubberBandPadding = m_iRubberBandLatency; m_iRubberBandFrames = m_iRubberBandLatency; #endif if (m_iRubberBandFrames > 0) { if (m_ppRubberBandFrames) { delete [] m_ppRubberBandFrames[0]; // delete [] m_ppRubberBandFrames; } // m_ppRubberBandFrames = new float * [m_iRubberBandChannels]; m_ppRubberBandFrames[0] = new float [m_iRubberBandFrames]; ::memset(m_ppRubberBandFrames[0], 0, m_iRubberBandFrames * sizeof(float)); for (unsigned short i = 1; i < m_iRubberBandChannels; ++i) m_ppRubberBandFrames[i] = m_ppRubberBandFrames[0]; } m_bRubberBandFlush = false; } #endif } // end of qtractorTimeStretcher.cpp qtractor-1.5.11/src/PaxHeaders/qtractorAudioMeter.cpp0000644000000000000000000000013215124701674017634 xustar0030 mtime=1767080892.779263512 30 atime=1767080892.779263512 30 ctime=1767080892.779263512 qtractor-1.5.11/src/qtractorAudioMeter.cpp0000644000175000001440000004206415124701674017632 0ustar00rncbcusers// qtractorAudioMeter.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorAudioMeter.h" #include "qtractorAudioMonitor.h" #include "qtractorObserverWidget.h" #include "qtractorMidiControlObserver.h" #include #include #include #include #include #include #include // Meter level limits (in dB). #define QTRACTOR_AUDIO_METER_MAXDB +6.0f #define QTRACTOR_AUDIO_METER_MINDB -70.0f // The decay rates (magic goes here :). // - value decay rate (faster) #define QTRACTOR_AUDIO_METER_DECAY_RATE1 (1.0f - 1E-2f) // - peak decay rate (slower) #define QTRACTOR_AUDIO_METER_DECAY_RATE2 (1.0f - 1E-6f) // Number of cycles the peak stays on hold before fall-off. #define QTRACTOR_AUDIO_METER_PEAK_FALLOFF 32 // Possible 20 * log10(x) optimization // (borrowed from musicdsp.org) static inline float log10f2_opt ( float x ) { #ifdef CONFIG_FLOAT32_NOP # define M_LOG10F20 6.0205999132796239042f // (= 20.0f * M_LN2 / M_LN10) // Avoid strict-aliasing optimization (gcc -O2). union { float f; int i; } u; u.f = x; return M_LOG10F20 * ((((u.i & 0x7f800000) >> 23) - 0x7f) + (u.i & 0x007fffff) / (float) 0x800000); #else return 20.0f * ::log10f(x); #endif } static inline float log10f2 ( float x ) { return (x > 0.0f ? ::log10f2_opt(x) : QTRACTOR_AUDIO_METER_MINDB); } static inline float pow10f2 ( float x ) { return ::powf(10.0f, 0.05f * x); } // Ref. P.448. Approximate cube root of an IEEE float // Hacker's Delight (2nd Edition), by Henry S. Warren // http://www.hackersdelight.org/hdcodetxt/acbrt.c.txt // static inline float cbrtf2 ( float x ) { #ifdef CONFIG_FLOAT32//_NOP // Avoid strict-aliasing optimization (gcc -O2). union { float f; int i; } u; u.f = x; u.i = (u.i >> 4) + (u.i >> 2); u.i += (u.i >> 4) + 0x2a6a8000; // 0x2a6497f8; // return 0.33333333f * (2.0f * u.f + x / (u.f * u.f)); return u.f; #else return ::cbrtf(x); #endif } static inline float cubef2 ( float x ) { return x * x * x; } // Audio meter default color array. QColor qtractorAudioMeter::g_defaultColors[ColorCount] = { QColor(240, 0, 20), // ColorOver QColor(240,160, 20), // Color0dB QColor(220,220, 20), // Color3dB QColor(160,220, 20), // Color6dB QColor( 40,160, 40), // Color10dB QColor( 20, 40, 20), // ColorBack QColor( 80, 80, 80) // ColorFore }; // Audio meter color array. QColor qtractorAudioMeter::g_currentColors[ColorCount] = { g_defaultColors[ColorOver], g_defaultColors[Color0dB], g_defaultColors[Color3dB], g_defaultColors[Color6dB], g_defaultColors[Color10dB], g_defaultColors[ColorBack], g_defaultColors[ColorFore] }; //---------------------------------------------------------------------------- // IEC standard dB scaling -- as borrowed from meterbridge (c) Steve Harris static inline float IEC_Scale ( float dB ) { float fScale = 1.0f; if (dB < -70.0f) fScale = 0.0f; else if (dB < -60.0f) fScale = (dB + 70.0f) * 0.0025f; else if (dB < -50.0f) fScale = (dB + 60.0f) * 0.005f + 0.025f; else if (dB < -40.0) fScale = (dB + 50.0f) * 0.0075f + 0.075f; else if (dB < -30.0f) fScale = (dB + 40.0f) * 0.015f + 0.15f; else if (dB < -20.0f) fScale = (dB + 30.0f) * 0.02f + 0.3f; else if (dB < -0.001f || dB > 0.001f) /* if (dB < 0.0f) */ fScale = (dB + 20.0f) * 0.025f + 0.5f; return fScale; } static inline float IEC_dB ( float fScale ) { float dB = 0.0f; if (fScale < 0.025f) // IEC_Scale(-60.0f) dB = (fScale / 0.0025f) - 70.0f; else if (fScale < 0.075f) // IEC_Scale(-50.0f) dB = (fScale - 0.025f) / 0.005f - 60.0f; else if (fScale < 0.15f) // IEC_Scale(-40.0f) dB = (fScale - 0.075f) / 0.0075f - 50.0f; else if (fScale < 0.3f) // IEC_Scale(-30.0f) dB = (fScale - 0.15f) / 0.015f - 40.0f; else if (fScale < 0.5f) // IEC_Scale(-20.0f) dB = (fScale - 0.3f) / 0.02f - 30.0f; else /* if (fScale < 1.0f) // IED_Scale(0.0f)) */ dB = (fScale - 0.5f) / 0.025f - 20.0f; return (dB > -0.001f && dB < 0.001f ? 0.0f : dB); } //---------------------------------------------------------------------------- // qtractorAudioMeterScale -- Meter bridge scale widget. // Constructor. qtractorAudioMeterScale::qtractorAudioMeterScale ( qtractorAudioMeter *pAudioMeter ) : qtractorMeterScale(pAudioMeter) { // Nothing much to do... } // Actual scale drawing method. void qtractorAudioMeterScale::paintScale ( QPainter *p ) { qtractorAudioMeter *pAudioMeter = static_cast (meter()); if (pAudioMeter == nullptr) return; // p->setWindow(0, -4, QWidget::width(), QWidget::height() + 8); drawLineLabel(p, pAudioMeter->iec_level(qtractorAudioMeter::Color0dB), "0"); drawLineLabel(p, pAudioMeter->iec_level(qtractorAudioMeter::Color3dB), "3"); drawLineLabel(p, pAudioMeter->iec_level(qtractorAudioMeter::Color6dB), "6"); drawLineLabel(p, pAudioMeter->iec_level(qtractorAudioMeter::Color10dB), "10"); for (float dB = -20.0f; dB > QTRACTOR_AUDIO_METER_MINDB; dB -= 10.0f) drawLineLabel(p, pAudioMeter->iec_scale(dB), QString::number(-int(dB))); } //---------------------------------------------------------------------------- // qtractorAudioMeterValue -- Meter bridge value widget. // Constructor. qtractorAudioMeterValue::qtractorAudioMeterValue ( qtractorAudioMeter *pAudioMeter, unsigned short iChannel ) : qtractorMeterValue(pAudioMeter), m_iChannel(iChannel) { // Avoid intensively annoying repaints... QWidget::setAttribute(Qt::WA_StaticContents); QWidget::setAttribute(Qt::WA_OpaquePaintEvent); QWidget::setBackgroundRole(QPalette::NoRole); m_iValue = 0; m_fValueDecay = QTRACTOR_AUDIO_METER_DECAY_RATE1; m_iPeak = 0; m_iPeakHold = 0; m_fPeakDecay = QTRACTOR_AUDIO_METER_DECAY_RATE2; m_iPeakColor = qtractorAudioMeter::Color6dB; QWidget::setMinimumWidth(2); QWidget::setMaximumWidth(14); } // Value refreshment. void qtractorAudioMeterValue::refresh ( unsigned long iStamp ) { qtractorAudioMeter *pAudioMeter = static_cast (meter()); if (pAudioMeter == nullptr) return; qtractorAudioMonitor *pAudioMonitor = pAudioMeter->audioMonitor(); if (pAudioMonitor == nullptr) return; const float fValue = pAudioMonitor->value_stamp(m_iChannel, iStamp); if (fValue < 0.001f && m_iPeak < 1) return; #if 0 float dB = QTRACTOR_AUDIO_METER_MINDB; if (fValue > 0.0f) dB = log10f2_opt(fValue); if (dB < QTRACTOR_AUDIO_METER_MINDB) dB = QTRACTOR_AUDIO_METER_MINDB; else if (dB > QTRACTOR_AUDIO_METER_MAXDB) dB = QTRACTOR_AUDIO_METER_MAXDB; int iValue = m_pAudioMeter->iec_scale(dB); #else int iValue = 0; if (fValue > 0.001f) iValue = pAudioMeter->scale(::cbrtf2(fValue)); #endif if (iValue < m_iValue) { iValue = int(m_fValueDecay * float(m_iValue)); m_fValueDecay *= m_fValueDecay; } else { m_fValueDecay = QTRACTOR_AUDIO_METER_DECAY_RATE1; } int iPeak = m_iPeak; if (iPeak < iValue) { iPeak = iValue; m_iPeakHold = 0; m_fPeakDecay = QTRACTOR_AUDIO_METER_DECAY_RATE2; m_iPeakColor = qtractorAudioMeter::Color10dB; for (; m_iPeakColor > qtractorAudioMeter::ColorOver && iPeak >= pAudioMeter->iec_level(m_iPeakColor); --m_iPeakColor) /* empty body loop */; } else if (++m_iPeakHold > pAudioMeter->peakFalloff()) { iPeak = int(m_fPeakDecay * float(iPeak)); if (iPeak < iValue) { iPeak = iValue; } else { m_fPeakDecay *= m_fPeakDecay; } } if (iValue == m_iValue && iPeak == m_iPeak) return; m_iValue = iValue; m_iPeak = iPeak; update(); } // Paint event handler. void qtractorAudioMeterValue::paintEvent ( QPaintEvent * ) { qtractorAudioMeter *pAudioMeter = static_cast (meter()); if (pAudioMeter == nullptr) return; QPainter painter(this); const int w = QWidget::width(); const int h = QWidget::height(); int y; if (isEnabled()) { painter.fillRect(0, 0, w, h, pAudioMeter->color(qtractorAudioMeter::ColorBack)); y = h - pAudioMeter->iec_level(qtractorAudioMeter::Color0dB); painter.setPen(pAudioMeter->color(qtractorAudioMeter::ColorFore)); painter.drawLine(0, y, w, y); } else { painter.fillRect(0, 0, w, h, Qt::gray); } y = h - m_iValue; painter.drawPixmap(0, y, pAudioMeter->pixmap(), 0, y, w, m_iValue); y = h - m_iPeak; painter.setPen(pAudioMeter->color(m_iPeakColor)); painter.drawLine(0, y, w, y); } // Resize event handler. void qtractorAudioMeterValue::resizeEvent ( QResizeEvent *pResizeEvent ) { m_iPeak = 0; qtractorMeterValue::resizeEvent(pResizeEvent); } //---------------------------------------------------------------------------- // qtractorAudioMeter -- Audio meter bridge slot widget. // Constructor. qtractorAudioMeter::qtractorAudioMeter ( qtractorAudioMonitor *pAudioMonitor, QWidget *pParent ) : qtractorMeter(pParent) { m_pAudioMonitor = pAudioMonitor; m_iChannels = 0; m_ppAudioValues = nullptr; m_iRegenerate = 0; m_fScale0dB = 0.85f; setPeakFalloff(QTRACTOR_AUDIO_METER_PEAK_FALLOFF); for (int i = 0; i < LevelCount; ++i) m_levels[i] = 0; reset(); } // Default destructor. qtractorAudioMeter::~qtractorAudioMeter (void) { // No need to delete child widgets, Qt does it all for us //for (unsigned short i = 0; i < m_iChannels; ++i) // delete m_ppAudioValues[i]; delete [] m_ppAudioValues; } // IEC standard int qtractorAudioMeter::iec_scale ( float dB ) const { return scale(IEC_Scale(dB)); } int qtractorAudioMeter::iec_level ( int iIndex ) const { return m_levels[iIndex]; } // Audio monitor reset void qtractorAudioMeter::reset (void) { if (m_pAudioMonitor == nullptr) return; const unsigned short iChannels = m_pAudioMonitor->channels(); if (m_iChannels == iChannels) return; if (m_ppAudioValues) { qtractorMeter::hide(); for (unsigned short i = 0; i < m_iChannels; ++i) { // m_ppAudioValues[i]->hide(); boxLayout()->removeWidget(m_ppAudioValues[i]); delete m_ppAudioValues[i]; } delete [] m_ppAudioValues; m_ppAudioValues = nullptr; ++m_iRegenerate; } m_iChannels = iChannels; if (m_iChannels > 0) { m_ppAudioValues = new qtractorAudioMeterValue * [m_iChannels]; for (unsigned short i = 0; i < m_iChannels; ++i) { m_ppAudioValues[i] = new qtractorAudioMeterValue(this, i); boxLayout()->addWidget(m_ppAudioValues[i]); // m_ppAudioValues[i]->show(); } if (m_iRegenerate > 0) qtractorMeter::show(); } } // Pixmap accessors. const QPixmap& qtractorAudioMeter::pixmap (void) const { return m_pixmap; } void qtractorAudioMeter::updatePixmap (void) { const int w = QWidget::width(); const int h = QWidget::height(); m_pixmap = QPixmap(w, h); #if 1//def CONFIG_GRADIENT const float f0dB = 1.0f - m_fScale0dB; QLinearGradient grad(0, 0, 0, h); grad.setColorAt(f0dB * 0.5f, color(ColorOver)); grad.setColorAt(f0dB, color(Color0dB)); grad.setColorAt(f0dB + 0.1f, color(Color3dB)); grad.setColorAt(f0dB + 0.2f, color(Color6dB)); grad.setColorAt(f0dB + 0.6f, color(Color10dB)); QPainter(&m_pixmap).fillRect(0, 0, w, h, grad); #else QPainter painter(&m_pixmap); int y0 = 0; for (int i = Color10dB; i > ColorOver; --i) { const int y1 = iec_level(i); painter.fillRect(0, h - y1, w, y1 - y0, color(i)); y0 = y1; } painter.fillRect(0, 0, w, h - y0, color(ColorOver)); #endif } // Resize event handler. void qtractorAudioMeter::resizeEvent ( QResizeEvent *pResizeEvent ) { qtractorMeter::setScale(m_fScale0dB * float(QWidget::height())); m_levels[Color0dB] = iec_scale( 0.0f); m_levels[Color3dB] = iec_scale( -3.0f); m_levels[Color6dB] = iec_scale( -6.0f); m_levels[Color10dB] = iec_scale(-10.0f); updatePixmap(); if (m_iChannels > 0) { const int w = QWidget::width(); int iMaxWidth = w / m_iChannels - 1; if (iMaxWidth < 2) iMaxWidth = 2; else if (iMaxWidth > 14) iMaxWidth = 14; int iSpacing = 2; int w2 = m_iChannels * iMaxWidth + (m_iChannels - 1) * iSpacing - 2; while (iSpacing > 0 && w < w2) { w2 -= (m_iChannels - 1); --iSpacing; } boxLayout()->setSpacing(iSpacing); for (unsigned short i = 0; i < m_iChannels; ++i) m_ppAudioValues[i]->setMaximumWidth(iMaxWidth); } qtractorMeter::resizeEvent(pResizeEvent); } // Virtual monitor accessor. void qtractorAudioMeter::setMonitor ( qtractorMonitor *pMonitor ) { setAudioMonitor(static_cast (pMonitor)); } qtractorMonitor *qtractorAudioMeter::monitor (void) const { return audioMonitor(); } // Audio monitor accessor. void qtractorAudioMeter::setAudioMonitor ( qtractorAudioMonitor *pAudioMonitor ) { m_pAudioMonitor = pAudioMonitor; reset(); } qtractorAudioMonitor *qtractorAudioMeter::audioMonitor (void) const { return m_pAudioMonitor; } // Common resource accessor (static). void qtractorAudioMeter::setColor ( int iIndex, const QColor& color ) { g_currentColors[iIndex] = color; } const QColor& qtractorAudioMeter::color ( int iIndex ) { return g_currentColors[iIndex]; } const QColor& qtractorAudioMeter::defaultColor ( int iIndex ) { return g_defaultColors[iIndex]; } //---------------------------------------------------------------------- // class qtractorAudioMixerMeter::GainSpinBoxInterface -- Observer interface. // // Local converter interface. class qtractorAudioMixerMeter::GainSpinBoxInterface : public qtractorObserverSpinBox::Interface { public: // Formerly Pure virtuals. float scaleFromValue ( float fValue ) const { return log10f2(fValue); } float valueFromScale ( float fScale ) const { return pow10f2(fScale); } }; //---------------------------------------------------------------------- // class qtractorAudioMixerMeter::GainSliderInterface -- Observer interface. // // Local converter interface. class qtractorAudioMixerMeter::GainSliderInterface : public qtractorObserverSlider::Interface { public: // Formerly Pure virtuals. float scaleFromValue ( float fValue ) const { return 10000.0f * IEC_Scale(log10f2(fValue)); } float valueFromScale ( float fScale ) const { return pow10f2(IEC_dB(fScale / 10000.0f)); } }; //---------------------------------------------------------------------------- // qtractorAudioMeter -- Audio meter bridge slot widget. // Constructor. qtractorAudioMixerMeter::qtractorAudioMixerMeter ( qtractorAudioMonitor *pAudioMonitor, QWidget *pParent ) : qtractorMixerMeter(pParent) { m_pAudioMeter = new qtractorAudioMeter(pAudioMonitor); m_pAudioScale = new qtractorAudioMeterScale(m_pAudioMeter); topWidget()->hide(); boxLayout()->addWidget(m_pAudioScale); boxLayout()->addWidget(m_pAudioMeter); gainSlider()->setInterface(new GainSliderInterface()); gainSpinBox()->setInterface(new GainSpinBoxInterface()); gainSlider()->setMaximum(11500); gainSpinBox()->setMinimum(QTRACTOR_AUDIO_METER_MINDB); gainSpinBox()->setMaximum(QTRACTOR_AUDIO_METER_MAXDB); gainSpinBox()->setToolTip(tr("Gain (dB)")); gainSpinBox()->setSuffix(tr(" dB")); reset(); updatePanning(); updateGain(); } // Default destructor. qtractorAudioMixerMeter::~qtractorAudioMixerMeter (void) { delete m_pAudioScale; delete m_pAudioMeter; // No need to delete child widgets, Qt does it all for us } // Audio monitor reset void qtractorAudioMixerMeter::reset (void) { qtractorAudioMonitor *pAudioMonitor = m_pAudioMeter->audioMonitor(); if (pAudioMonitor == nullptr) return; m_pAudioMeter->reset(); setPanningSubject(pAudioMonitor->panningSubject()); setGainSubject(pAudioMonitor->gainSubject()); const unsigned short iChannels = pAudioMonitor->channels(); panSlider()->setEnabled(iChannels > 1); panSpinBox()->setEnabled(iChannels > 1); } // Virtual monitor accessor. void qtractorAudioMixerMeter::setMonitor ( qtractorMonitor *pMonitor ) { m_pAudioMeter->setMonitor(pMonitor); } qtractorMonitor *qtractorAudioMixerMeter::monitor (void) const { return m_pAudioMeter->monitor(); } // Audio monitor accessor. void qtractorAudioMixerMeter::setAudioMonitor ( qtractorAudioMonitor *pAudioMonitor ) { m_pAudioMeter->setAudioMonitor(pAudioMonitor); reset(); } qtractorAudioMonitor *qtractorAudioMixerMeter::audioMonitor (void) const { return m_pAudioMeter->audioMonitor(); } // Pan-slider value change method. void qtractorAudioMixerMeter::updatePanning (void) { // setPanning(m_pAudioMonitor->panning()); panSlider()->setToolTip( tr("Pan: %1").arg(panning(), 0, 'g', 1)); } // Gain-slider value change method. void qtractorAudioMixerMeter::updateGain (void) { // setGain(m_pAudioMonitor->gain()); gainSlider()->setToolTip( tr("Gain: %1 dB").arg(gainSpinBox()->value(), 0, 'g', 3)); } // end of qtractorAudioMeter.cpp qtractor-1.5.11/src/PaxHeaders/qtractorTimeScale.cpp0000644000000000000000000000012715124701674017450 xustar0029 mtime=1767080892.80126342 29 atime=1767080892.80126342 29 ctime=1767080892.80126342 qtractor-1.5.11/src/qtractorTimeScale.cpp0000644000175000001440000006262615124701674017450 0ustar00rncbcusers// qtractorTimeScale.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorTimeScale.h" //---------------------------------------------------------------------- // class qtractorTimeScale -- Time scale conversion helper class. // // Node list cleaner. void qtractorTimeScale::reset (void) { m_nodes.setAutoDelete(true); m_markers.setAutoDelete(true); // Clear/reset location-markers... m_markers.clear(); m_markerCursor.reset(); // Try to preserve previous tempo/time-sig, if any... Node *pNode = m_nodes.first(); const float fTempo = (pNode ? pNode->tempo : 120.0f); const unsigned short iBeatType = (pNode ? pNode->beatType : 2); const unsigned short iBeatsPerBar = (pNode ? pNode->beatsPerBar : 4); const unsigned short iBeatDivisor = (pNode ? pNode->beatDivisor : 2); // Clear/reset tempo-map... m_nodes.clear(); m_cursor.reset(); // There must always be one node, always at zero-frame... addNode(0, fTempo, iBeatType, iBeatsPerBar, iBeatDivisor); // Commit new scale... updateScale(); } // (Re)nitializer method. void qtractorTimeScale::clear (void) { m_iSnapPerBeat = 4; m_iHorizontalZoom = 100; m_iVerticalZoom = 100; // m_displayFormat = Frames; // m_iSampleRate = 44100; m_iTicksPerBeat = TICKS_PER_BEAT_DEF; m_iPixelsPerBeat = 32; m_iBeatsPerBar2 = 0; m_iBeatDivisor2 = 0; // Clear/reset tempo-map... reset(); } // Sync method. void qtractorTimeScale::sync ( const qtractorTimeScale& ts ) { // Copy master parameters... m_iSampleRate = ts.m_iSampleRate; m_iTicksPerBeat = ts.m_iTicksPerBeat; m_iPixelsPerBeat = ts.m_iPixelsPerBeat; // Copy location markers... m_markers.clear(); Marker *pMarker = ts.m_markers.first(); while (pMarker) { m_markers.append(new Marker(*pMarker)); pMarker = pMarker->next(); } m_markerCursor.reset(); // Copy tempo-map nodes... m_nodes.clear(); Node *pNode = ts.nodes().first(); while (pNode) { m_nodes.append(new Node(this, pNode->frame, pNode->tempo, pNode->beatType, pNode->beatsPerBar, pNode->beatDivisor)); pNode = pNode->next(); } m_cursor.reset(); updateScale(); } // Copy method. qtractorTimeScale& qtractorTimeScale::copy ( const qtractorTimeScale& ts ) { if (&ts != this) { m_nodes.setAutoDelete(true); m_markers.setAutoDelete(true); m_iSnapPerBeat = ts.m_iSnapPerBeat; m_iHorizontalZoom = ts.m_iHorizontalZoom; m_iVerticalZoom = ts.m_iVerticalZoom; m_displayFormat = ts.m_displayFormat; m_iBeatsPerBar2 = ts.m_iBeatsPerBar2; m_iBeatDivisor2 = ts.m_iBeatDivisor2; // Sync/copy tempo-map nodes... sync(ts); } return *this; } // Update scale coefficient divisor factors. void qtractorTimeScale::Node::update (void) { ticksPerBeat = ts->ticksPerBeat(); tickRate = tempo * ticksPerBeat; beatRate = tempo; #if 1// nb. standard MIDI tempo (BPM) is beatType=2 (quarter notes) per minute. if (beatDivisor > beatType) { const unsigned short n = (beatDivisor - beatType); ticksPerBeat >>= n; beatRate *= float(1 << n); } else if (beatDivisor < beatType) { const unsigned short n = (beatType - beatDivisor); ticksPerBeat <<= n; beatRate /= float(1 << n); } #endif } // Update time-scale node position metrics. void qtractorTimeScale::Node::reset ( qtractorTimeScale::Node *pNode ) { if (bar > pNode->bar) frame = pNode->frameFromBar(bar); else bar = pNode->barFromFrame(frame); beat = pNode->beatFromFrame(frame); tick = pNode->tickFromFrame(frame); pixel = ts->pixelFromFrame(frame); } // Tempo accessor/convertors. (default's quarter notes per minute) void qtractorTimeScale::Node::setTempoEx ( float fTempo, unsigned short iBeatType ) { if (iBeatType > beatType) fTempo /= float(1 << (iBeatType - beatType)); else if (beatType > iBeatType) fTempo *= float(1 << (beatType - iBeatType)); tempo = fTempo; } float qtractorTimeScale::Node::tempoEx ( unsigned short iBeatType ) const { float fTempo = tempo; if (beatType > iBeatType) fTempo /= float(1 << (beatType - iBeatType)); else if (iBeatType > beatType) fTempo *= float(1 << (iBeatType - beatType)); return fTempo; } // Beat/frame snap filters. unsigned long qtractorTimeScale::Node::tickSnap ( unsigned long iTick, unsigned short p ) const { #if 0 unsigned long iTickSnap = iTick - tick; if (ts->snapPerBeat() > 0) { const unsigned long q = ticksPerBeat / ts->snapPerBeat(); iTickSnap = q * ((iTickSnap + (q >> p)) / q); } return tick + iTickSnap; #else const unsigned short iTicksPerBar = ticksPerBeat * beatsPerBar; const unsigned long iTickFromBar = tick + iTicksPerBar * ((iTick - tick) / iTicksPerBar); const unsigned short iBeatsPerBar2 = beatsPerBar2(); unsigned short iTicksPerBeat2 = iTicksPerBar / iBeatsPerBar2; unsigned long iTickSnap = iTick - iTickFromBar; if (ts->snapPerBeat() > 0) { const unsigned long q = iTicksPerBeat2 / ts->snapPerBeat(); iTickSnap = q * ((iTickSnap + (q >> p)) / q); } return iTickFromBar + iTickSnap; #endif } // Alternate (secondary) time-sig helper methods unsigned short qtractorTimeScale::Node::beatsPerBar2 (void) const { unsigned short iBeatsPerBar2 = ts->beatsPerBar2(); if (iBeatsPerBar2 < 1) iBeatsPerBar2 = beatsPerBar; const unsigned short iBeatDivisor2 = ts->beatDivisor2(); if (iBeatDivisor2 > 0) { if (beatDivisor > iBeatDivisor2) iBeatsPerBar2 >>= (beatDivisor - iBeatDivisor2); else if (beatDivisor < iBeatDivisor2) iBeatsPerBar2 <<= (iBeatDivisor2 - beatDivisor); } return iBeatsPerBar2; } unsigned short qtractorTimeScale::Node::ticksPerBeat2 (void) const { return (ticksPerBeat * beatsPerBar) / beatsPerBar2(); } // Time-scale cursor frame positioning reset. void qtractorTimeScale::Cursor::reset ( qtractorTimeScale::Node *pNode ) { node = (pNode ? pNode : ts->nodes().first()); } // Time-scale cursor node seeker (by frame). qtractorTimeScale::Node *qtractorTimeScale::Cursor::seekFrame ( unsigned long iFrame ) { if (node == nullptr) { node = ts->nodes().first(); if (node == nullptr) return nullptr; } if (iFrame > node->frame) { // Seek frame forward... while (node && node->next() && iFrame >= (node->next())->frame) node = node->next(); } else if (iFrame < node->frame) { // Seek frame backward... while (node && node->frame > iFrame) node = node->prev(); if (node == nullptr) node = ts->nodes().first(); } return node; } // Time-scale cursor node seeker (by bar). qtractorTimeScale::Node *qtractorTimeScale::Cursor::seekBar ( unsigned short iBar ) { if (node == nullptr) { node = ts->nodes().first(); if (node == nullptr) return nullptr; } if (iBar > node->bar) { // Seek bar forward... while (node && node->next() && iBar >= (node->next())->bar) node = node->next(); } else if (iBar < node->bar) { // Seek bar backward... while (node && node->bar > iBar) node = node->prev(); if (node == nullptr) node = ts->nodes().first(); } return node; } // Time-scale cursor node seeker (by beat). qtractorTimeScale::Node *qtractorTimeScale::Cursor::seekBeat ( unsigned int iBeat ) { if (node == nullptr) { node = ts->nodes().first(); if (node == nullptr) return nullptr; } if (iBeat > node->beat) { // Seek beat forward... while (node && node->next() && iBeat >= (node->next())->beat) node = node->next(); } else if (iBeat < node->beat) { // Seek beat backward... while (node && node->beat > iBeat) node = node->prev(); if (node == nullptr) node = ts->nodes().first(); } return node; } // Time-scale cursor node seeker (by tick). qtractorTimeScale::Node *qtractorTimeScale::Cursor::seekTick ( unsigned long iTick ) { if (node == nullptr) { node = ts->nodes().first(); if (node == nullptr) return nullptr; } if (iTick > node->tick) { // Seek tick forward... while (node && node->next() && iTick >= (node->next())->tick) node = node->next(); } else if (iTick < node->tick) { // Seek tick backward... while (node && node->tick > iTick) node = node->prev(); if (node == nullptr) node = ts->nodes().first(); } return node; } // Time-scale cursor node seeker (by pixel). qtractorTimeScale::Node *qtractorTimeScale::Cursor::seekPixel ( int x ) { if (node == nullptr) { node = ts->nodes().first(); if (node == nullptr) return nullptr; } if (x > node->pixel) { // Seek pixel forward... while (node && node->next() && x >= (node->next())->pixel) node = node->next(); } else if (x < node->pixel) { // Seek tick backward... while (node && node->pixel > x) node = node->prev(); if (node == nullptr) node = ts->nodes().first(); } return node; } // Node list specifics. qtractorTimeScale::Node *qtractorTimeScale::addNode ( unsigned long iFrame, float fTempo, unsigned short iBeatType, unsigned short iBeatsPerBar, unsigned short iBeatDivisor ) { Node *pNode = nullptr; // Seek for the nearest preceding node... Node *pPrev = m_cursor.seekFrame(iFrame); // Snap frame to nearest bar... if (pPrev) { iFrame = pPrev->frameSnapToBar(iFrame); pPrev = m_cursor.seekFrame(iFrame); } // Either update existing node or add new one... Node *pNext = (pPrev ? pPrev->next() : nullptr); if (pPrev && pPrev->frame == iFrame) { // Update exact matching node... pNode = pPrev; pNode->tempo = fTempo; pNode->beatType = iBeatType; pNode->beatsPerBar = iBeatsPerBar; pNode->beatDivisor = iBeatDivisor; } else if (pPrev && qAbs(pPrev->tempo - fTempo) < 0.001f && pPrev->beatType == iBeatType && pPrev->beatsPerBar == iBeatsPerBar && pPrev->beatDivisor == iBeatDivisor) { // No need for a new node... return pPrev; } else if (pNext && qAbs(pNext->tempo - fTempo) < 0.001f && pNext->beatType == iBeatType && pNext->beatsPerBar == iBeatsPerBar && pNext->beatDivisor == iBeatDivisor) { // Update next exact matching node... pNode = pNext; pNode->frame = iFrame; pNode->bar = 0; } else { // Add/insert a new node... pNode = new Node(this, iFrame, fTempo, iBeatType, iBeatsPerBar, iBeatDivisor); if (pPrev) m_nodes.insertAfter(pNode, pPrev); else m_nodes.append(pNode); } // Update coefficients and positioning thereafter... updateNode(pNode); return pNode; } void qtractorTimeScale::updateNode ( qtractorTimeScale::Node *pNode ) { // Update coefficients... pNode->update(); // Relocate internal cursor... m_cursor.reset(pNode); // Update positioning on all nodes thereafter... Node *pNext = pNode; Node *pPrev = pNext->prev(); while (pNext) { if (pPrev) pNext->reset(pPrev); pPrev = pNext; pNext = pNext->next(); } // And update marker/bar positions too... updateMarkers(pNode->prev()); } void qtractorTimeScale::removeNode ( qtractorTimeScale::Node *pNode ) { // Don't ever remove the very first node... Node *pNodePrev = pNode->prev(); if (pNodePrev == nullptr) return; // Relocate internal cursor... m_cursor.reset(pNodePrev); // Update positioning on all nodes thereafter... Node *pPrev = pNodePrev; Node *pNext = pNode->next(); while (pNext) { if (pPrev) pNext->reset(pPrev); pPrev = pNext; pNext = pNext->next(); } // Actually remove/unlink the node... m_nodes.remove(pNode); // Then update marker/bar positions too... updateMarkers(pNodePrev); } // Complete time-scale update method. void qtractorTimeScale::updateScale (void) { // Update time-map independent coefficients... m_fPixelRate = 1.20f * float(m_iHorizontalZoom * m_iPixelsPerBeat); m_fFrameRate = 60.0f * float(m_iSampleRate); // Update all nodes thereafter... Node *pPrev = nullptr; Node *pNext = m_nodes.first(); while (pNext) { pNext->update(); if (pPrev) pNext->reset(pPrev); pPrev = pNext; pNext = pNext->next(); } // Also update all marker/bar positions too... updateMarkers(m_nodes.first()); } // Convert frames to time string and vice-versa. unsigned long qtractorTimeScale::frameFromTextEx ( DisplayFormat displayFormat, const QString& sText, bool bDelta, unsigned long iFrame ) { switch (displayFormat) { case BBT: { // Time frame code in bars.beats.ticks ... unsigned short bars = sText.section('.', 0, 0).toUShort(); unsigned int beats = sText.section('.', 1, 1).toUInt(); unsigned long ticks = sText.section('.', 2).toULong(); Node *pNode; if (bDelta) { pNode = m_cursor.seekFrame(iFrame); if (pNode) { beats += bars * pNode->beatsPerBar; ticks += beats * pNode->ticksPerBeat2(); ticks += pNode->tickFromFrame(iFrame); iFrame = pNode->frameFromTick(ticks) - iFrame; } } else { if (bars > 0) --bars; if (beats > 0) --beats; pNode = m_cursor.seekBar(bars); if (pNode) { beats += (bars - pNode->bar) * pNode->beatsPerBar2(); ticks += pNode->tick + beats * pNode->ticksPerBeat2(); iFrame = pNode->frameFromTick(ticks); } } break; } case Time: { // Time frame code in hh:mm:ss.zzz ... unsigned int hh = sText.section(':', 0, 0).toUInt(); unsigned int mm = sText.section(':', 1, 1).toUInt(); float secs = sText.section(':', 2).toFloat(); mm += 60 * hh; secs += 60.f * float(mm); iFrame = uroundf(secs * float(m_iSampleRate)); break; } case Frames: { iFrame = sText.toULong(); break; } } return iFrame; } unsigned long qtractorTimeScale::frameFromText ( const QString& sText, bool bDelta, unsigned long iFrame ) { return frameFromTextEx(m_displayFormat, sText, bDelta, iFrame); } QString qtractorTimeScale::textFromFrameEx ( DisplayFormat displayFormat, unsigned long iFrame, bool bDelta, unsigned long iDelta ) { QString sText; switch (displayFormat) { case BBT: { // Time frame code in bars.beats.ticks ... unsigned short bars = 0; unsigned int beats = 0; unsigned long ticks = 0; Node *pNode = m_cursor.seekFrame(iFrame); if (pNode) { const unsigned long t0 = pNode->tickFromFrame(iFrame); if (bDelta) { const unsigned long iFrameEnd = iFrame + iDelta; pNode = m_cursor.seekFrame(iFrameEnd); ticks = pNode->tickFromFrame(iFrameEnd) - t0; } else { ticks = t0 - pNode->tick; } if (ticks >= (unsigned long) pNode->ticksPerBeat) { beats = (unsigned int) (ticks / pNode->ticksPerBeat); ticks -= (unsigned long) (beats * pNode->ticksPerBeat); } if (beats >= (unsigned int) pNode->beatsPerBar) { bars = (unsigned short) (beats / pNode->beatsPerBar); beats -= (unsigned int) (bars * pNode->beatsPerBar); } if (beatsPerBar2() > 0 || beatDivisor2() > 0) { const unsigned short iTicksPerBeat2 = pNode->ticksPerBeat2(); ticks += (unsigned long) (beats * pNode->ticksPerBeat); if (ticks >= (unsigned long) iTicksPerBeat2) { beats = (unsigned int) (ticks / iTicksPerBeat2); ticks -= (unsigned long) (beats * iTicksPerBeat2); } } if (!bDelta) bars += pNode->bar; } if (!bDelta) { ++bars; ++beats; } #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) sText.sprintf("%u.%u.%03lu", bars, beats, ticks); #else sText = QString::asprintf("%u.%u.%03lu", bars, beats, ticks); #endif break; } case Time: { // Time frame code in hh:mm:ss.zzz ... unsigned int hh, mm, ss, zzz; float secs = float(bDelta ? iDelta : iFrame) / float(m_iSampleRate); hh = mm = ss = 0; if (secs >= 3600.0f) { hh = (unsigned int) (secs / 3600.0f); secs -= float(hh) * 3600.0f; } if (secs >= 60.0f) { mm = (unsigned int) (secs / 60.0f); secs -= float(mm) * 60.0f; } if (secs >= 0.0f) { ss = (unsigned int) secs; secs -= float(ss); } zzz = (unsigned int) (secs * 1000.0f); #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) sText.sprintf("%02u:%02u:%02u.%03u", hh, mm, ss, zzz); #else sText = QString::asprintf("%02u:%02u:%02u.%03u", hh, mm, ss, zzz); #endif break; } case Frames: { sText = QString::number(bDelta ? iDelta : iFrame); break; } } return sText; } QString qtractorTimeScale::textFromFrame ( unsigned long iFrame, bool bDelta, unsigned long iDelta ) { return textFromFrameEx(m_displayFormat, iFrame, bDelta, iDelta); } // Convert ticks to time string and vice-versa. unsigned long qtractorTimeScale::tickFromText ( const QString& sText, bool bDelta, unsigned long iTick ) { unsigned long iFrame = 0; if (bDelta) { Node *pNode = m_cursor.seekTick(iTick); iFrame = (pNode ? pNode->frameFromTick(iTick) : 0); } return tickFromFrame(frameFromText(sText, bDelta, iFrame)); } QString qtractorTimeScale::textFromTick ( unsigned long iTick, bool bDelta, unsigned long iDelta ) { Node *pNode = m_cursor.seekTick(iTick); const unsigned long iFrame = (pNode ? pNode->frameFromTick(iTick) : 0); if (bDelta > 0 && pNode) { iTick += iDelta; pNode = m_cursor.seekTick(iTick); iDelta = (pNode ? pNode->frameFromTick(iTick) - iFrame : 0); } return textFromFrame(iFrame, bDelta, iDelta); } // Beat divisor (snap index) map. static int s_aiSnapPerBeat[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 21, 24, 28, 32, 48, 64, 96 }; const int c_iSnapItemCount = sizeof(s_aiSnapPerBeat) / sizeof(int); // Beat divisor (snap index) accessors. unsigned short qtractorTimeScale::snapFromIndex ( int iSnap ) { return s_aiSnapPerBeat[iSnap]; } // Beat divisor (snap index) accessors. int qtractorTimeScale::indexFromSnap ( unsigned short iSnapPerBeat ) { for (int iSnap = 0; iSnap < c_iSnapItemCount; ++iSnap) { if (s_aiSnapPerBeat[iSnap] == iSnapPerBeat) return iSnap; } return 0; } // Beat divisor (snap index) text item list. QStringList qtractorTimeScale::snapItems (void) { QStringList items; int iSnap = 0; items.append(QObject::tr("None")); ++iSnap; QString sPrefix = QObject::tr("Beat"); items.append(sPrefix); ++iSnap; sPrefix += "/%1"; while (iSnap < c_iSnapItemCount) items.append(sPrefix.arg(s_aiSnapPerBeat[iSnap++])); return items; } // Tick/Frame range conversion (delta conversion). unsigned long qtractorTimeScale::frameFromTickRange ( unsigned long iTickStart, unsigned long iTickEnd, bool bOffset ) { Node *pNode = m_cursor.seekTick(iTickStart); const unsigned long iFrameStart = (pNode ? pNode->frameFromTick(iTickStart) : 0); if (!bOffset) pNode = m_cursor.seekTick(iTickEnd); const unsigned long iFrameEnd = (pNode ? pNode->frameFromTick(iTickEnd) : 0); return (iFrameEnd > iFrameStart ? iFrameEnd - iFrameStart : 0); } unsigned long qtractorTimeScale::tickFromFrameRange ( unsigned long iFrameStart, unsigned long iFrameEnd, bool bOffset ) { Node *pNode = m_cursor.seekFrame(iFrameStart); const unsigned long iTickStart = (pNode ? pNode->tickFromFrame(iFrameStart) : 0); if (!bOffset) pNode = m_cursor.seekFrame(iFrameEnd); const unsigned long iTickEnd = (pNode ? pNode->tickFromFrame(iFrameEnd) : 0); return (iTickEnd > iTickStart ? iTickEnd - iTickStart : 0); } // Location marker reset method. void qtractorTimeScale::MarkerCursor::reset ( qtractorTimeScale::Marker *pMarker ) { marker = (pMarker ? pMarker : ts->markers().first()); } // Location marker seek methods. qtractorTimeScale::Marker *qtractorTimeScale::MarkerCursor::seekFrame ( unsigned long iFrame ) { if (marker == nullptr) { marker = ts->markers().first(); if (marker == nullptr) return nullptr; } if (iFrame > marker->frame) { // Seek frame forward... while (marker && marker->next() && iFrame >= (marker->next())->frame) marker = marker->next(); } else if (iFrame < marker->frame) { // Seek frame backward... while (marker && marker->frame > iFrame) marker = marker->prev(); if (marker == nullptr) marker = ts->markers().first(); } return marker; } qtractorTimeScale::Marker *qtractorTimeScale::MarkerCursor::seekBar ( unsigned short iBar ) { return seekFrame(ts->frameFromBar(iBar)); } qtractorTimeScale::Marker *qtractorTimeScale::MarkerCursor::seekBeat ( unsigned int iBeat ) { return seekFrame(ts->frameFromBeat(iBeat)); } qtractorTimeScale::Marker *qtractorTimeScale::MarkerCursor::seekTick ( unsigned long iTick ) { return seekFrame(ts->frameFromTick(iTick)); } qtractorTimeScale::Marker *qtractorTimeScale::MarkerCursor::seekPixel ( int x ) { return seekFrame(ts->frameFromPixel(x)); } // Location markers list specifics. qtractorTimeScale::Marker *qtractorTimeScale::addMarker ( unsigned long iFrame, const QString& sText, const QColor& rgbColor ) { Marker *pMarker = nullptr; // Snap to nearest bar... unsigned short iBar = 0; Node *pNodePrev = m_cursor.seekFrame(iFrame); if (pNodePrev) { iBar = pNodePrev->barFromFrame(iFrame); iFrame = pNodePrev->frameFromBar(iBar); } // Seek for the nearest marker... Marker *pMarkerNear = m_markerCursor.seekFrame(iFrame); // Either update existing marker or add new one... if (pMarkerNear && pMarkerNear->frame == iFrame) { // Update exact matching marker... pMarker = pMarkerNear; pMarker->bar = iBar; pMarker->text = sText; pMarker->color = rgbColor; } else { // Add/insert a new marker... pMarker = new Marker(iFrame, iBar, sText, rgbColor); if (pMarkerNear && pMarkerNear->frame > iFrame) m_markers.insertBefore(pMarker, pMarkerNear); else if (pMarkerNear && pMarkerNear->frame < iFrame) m_markers.insertAfter(pMarker, pMarkerNear); else m_markers.append(pMarker); } // Update positioning... updateMarker(pMarker); return pMarker; } // Key-signature list specifics. qtractorTimeScale::Marker *qtractorTimeScale::addKeySignature ( unsigned long iFrame, int iAccidentals, int iMode ) { Marker *pMarker = nullptr; // Snap to nearest bar... unsigned short iBar = 0; Node *pNodePrev = m_cursor.seekFrame(iFrame); if (pNodePrev) { iBar = pNodePrev->barFromFrame(iFrame); iFrame = pNodePrev->frameFromBar(iBar); } // Seek for the nearest marker... Marker *pMarkerNear = m_markerCursor.seekFrame(iFrame); // Either update existing marker or add new one... if (pMarkerNear && pMarkerNear->frame == iFrame) { // Update exact matching marker... pMarker = pMarkerNear; pMarker->bar = iBar; pMarker->accidentals = iAccidentals; pMarker->mode = iMode; } else { // Add/insert a new marker... pMarker = new Marker(iFrame, iBar, iAccidentals, iMode); if (pMarkerNear && pMarkerNear->frame > iFrame) m_markers.insertBefore(pMarker, pMarkerNear); else if (pMarkerNear && pMarkerNear->frame < iFrame) m_markers.insertAfter(pMarker, pMarkerNear); else m_markers.append(pMarker); } // Update positioning... updateMarker(pMarker); return pMarker; } void qtractorTimeScale::updateMarker ( qtractorTimeScale::Marker *pMarker ) { // Relocate internal cursor... m_markerCursor.reset(pMarker); } void qtractorTimeScale::removeMarker ( qtractorTimeScale::Marker *pMarker ) { // Actually remove/unlink the marker // and relocate internal cursor... Marker *pMarkerPrev = pMarker->prev(); m_markers.remove(pMarker); m_markerCursor.reset(pMarkerPrev); } // Update markers from given node position. void qtractorTimeScale::updateMarkers ( qtractorTimeScale::Node *pNode ) { if (pNode == nullptr) pNode = m_nodes.first(); if (pNode == nullptr) return; Marker *pMarker = m_markerCursor.seekFrame(pNode->frame); while (pMarker) { while (pNode->next() && pMarker->frame > pNode->next()->frame) pNode = pNode->next(); if (pMarker->frame >= pNode->frame) pMarker->frame = pNode->frameFromBar(pMarker->bar); pMarker = pMarker->next(); } } // Key signature map accessor. // bool qtractorTimeScale::isKeySignature ( int iAccidentals, int iMode ) { return (MinAccidentals < iAccidentals && MaxAccidentals >= iAccidentals && iMode >= 0); } QString qtractorTimeScale::keySignatureName ( int iAccidentals, int iMode, char chMinor ) { static struct { const char *natural; const char *sharp; const char *flat; } s_aAccidentalsTab[] = { { QT_TR_NOOP("C"), QT_TR_NOOP("B#"), nullptr }, { nullptr, QT_TR_NOOP("C#"), QT_TR_NOOP("Db") }, { QT_TR_NOOP("D"), nullptr, nullptr }, { nullptr, QT_TR_NOOP("D#"), QT_TR_NOOP("Eb") }, { QT_TR_NOOP("E"), nullptr, QT_TR_NOOP("Fb") }, { QT_TR_NOOP("F"), QT_TR_NOOP("E#"), nullptr }, { nullptr, QT_TR_NOOP("F#"), QT_TR_NOOP("Gb") }, { QT_TR_NOOP("G"), nullptr, nullptr }, { nullptr, QT_TR_NOOP("G#"), QT_TR_NOOP("Ab") }, { QT_TR_NOOP("A"), nullptr, nullptr }, { nullptr, QT_TR_NOOP("A#"), QT_TR_NOOP("Bb") }, { QT_TR_NOOP("B"), nullptr, QT_TR_NOOP("Cb") } }; QString sKeySignature('-'); if (!isKeySignature(iAccidentals, iMode)) return sKeySignature; const int i = (iAccidentals * (iAccidentals < 0 ? -5 : 7) + (iMode > 0 ? 9 : 0)) % 12; const char *name = nullptr; if (iAccidentals < 0) name = s_aAccidentalsTab[i].flat; else if (iAccidentals > 0) name = s_aAccidentalsTab[i].sharp; if (name == nullptr) name = s_aAccidentalsTab[i].natural; sKeySignature = tr(name); if (iMode && chMinor) sKeySignature += chMinor; return sKeySignature; } // end of qtractorTimeScale.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMessageBox.h0000644000000000000000000000013215124701674017300 xustar0030 mtime=1767080892.788263475 30 atime=1767080892.788263475 30 ctime=1767080892.788263475 qtractor-1.5.11/src/qtractorMessageBox.h0000644000175000001440000000443115124701674017272 0ustar00rncbcusers// qtractorMessageBox.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorMessageBox_h #define __qtractorMessageBox_h #include #include // Forward decls. class QVBoxLayout; class QAbstractButton; class QDialogButtonBox; class QLabel; //---------------------------------------------------------------------------- // qtractorMessageBox -- UI wrapper form. class qtractorMessageBox: public QDialog { Q_OBJECT public: // Constructor. qtractorMessageBox(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()); // Accessors. void setText(const QString& sText); QString text() const; void setIcon(QMessageBox::Icon icon); QMessageBox::Icon icon() const; void setIconPixmap(const QPixmap& pixmap); QPixmap iconPixmap() const; void setStandardButtons(QMessageBox::StandardButtons buttons); QMessageBox::StandardButtons standardButtons() const; void addCustomButton(QAbstractButton *pButton); void addCustomSpacer(); void addButton(QAbstractButton *pButton, QMessageBox::ButtonRole role); protected slots: // Dialog slots. void standardButtonClicked(QAbstractButton *); private: // UI structs. QMessageBox::Icon m_icon; QLabel *m_pIconLabel; QLabel *m_pTextLabel; QVBoxLayout *m_pCustomButtonLayout; QDialogButtonBox *m_pDialogButtonBox; }; #endif // __qtractorMessageBox_h // end of qtractorMessageBox.h qtractor-1.5.11/src/PaxHeaders/qtractorPluginCommand.h0000644000000000000000000000013215124701674020000 xustar0030 mtime=1767080892.798263432 30 atime=1767080892.798263432 30 ctime=1767080892.798263432 qtractor-1.5.11/src/qtractorPluginCommand.h0000644000175000001440000002672415124701674020003 0ustar00rncbcusers// qtractorPluginCommand.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorPluginCommand_h #define __qtractorPluginCommand_h #include "qtractorCommand.h" #include "qtractorPlugin.h" #include #include // Forward declarations... class qtractorAuxSendPlugin; class qtractorPluginPortWidget; class qtractorMidiManager; //---------------------------------------------------------------------- // class qtractorPluginCommand - declaration. // class qtractorPluginCommand : public qtractorCommand { public: // Constructor. qtractorPluginCommand(const QString& sName, qtractorPlugin *pPlugin = nullptr); // Destructor. virtual ~qtractorPluginCommand(); // Plugin list accessors. const QList& plugins() const { return m_plugins; } void addPlugin(qtractorPlugin *pPlugin) { m_plugins.append(pPlugin); } protected: // Add new plugin(s) command method. bool addPlugins(); // Remove existing plugin(s) command method. bool removePlugins(); private: // Instance variables. QList m_plugins; }; //---------------------------------------------------------------------- // class qtractorAddPluginCommand - declaration. // class qtractorAddPluginCommand : public qtractorPluginCommand { public: // Constructor. qtractorAddPluginCommand(qtractorPlugin *pPlugin = nullptr); // Plugin insertion command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorAddInsertPluginCommand - declaration. // class qtractorAddInsertPluginCommand : public qtractorPluginCommand { public: // Constructor. qtractorAddInsertPluginCommand(qtractorPlugin *pPlugin = nullptr); // Plugin insertion command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorAddAuxSendPluginCommand - declaration. // class qtractorAddAuxSendPluginCommand : public qtractorPluginCommand { public: // Constructor. qtractorAddAuxSendPluginCommand(qtractorPlugin *pPlugin = nullptr); // Plugin insertion command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorAddMidiControlPluginCommand - declaration. // class qtractorAddMidiControlPluginCommand : public qtractorPluginCommand { public: // Constructor. qtractorAddMidiControlPluginCommand(qtractorPlugin *pPlugin = nullptr); // Plugin insertion command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorAuxSendPluginCommand - declaration. // class qtractorAuxSendPluginCommand : public qtractorPluginCommand { public: // Constructor. qtractorAuxSendPluginCommand( qtractorPlugin *pPlugin, const QString& sAuxSendBusName); // Plugin insertion command methods. bool redo(); bool undo(); private: // Instance variables. QString m_sAuxSendBusName; }; //---------------------------------------------------------------------- // class qtractorAuxSendIOMatrixCommand - declaration. // class qtractorAuxSendIOMatrixCommand : public qtractorPluginCommand { public: // Constructor. qtractorAuxSendIOMatrixCommand( qtractorPlugin *pPlugin, const QList& matrix); // Plugin insertion command methods. bool redo(); bool undo(); private: // Instance variables. QList m_matrix; }; //---------------------------------------------------------------------- // class qtractorRemovePluginCommand - declaration. // class qtractorRemovePluginCommand : public qtractorPluginCommand { public: // Constructor. qtractorRemovePluginCommand(qtractorPlugin *pPlugin = nullptr); // Plugin-removal command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorInsertPluginCommand - declaration. // class qtractorInsertPluginCommand : public qtractorPluginCommand { public: // Constructor. qtractorInsertPluginCommand(const QString& sName, qtractorPlugin *pPlugin, qtractorPlugin *pNextPlugin); // Plugin-move command methods. bool redo(); bool undo(); protected: // The anchor plugin reference. void setNextPlugin(qtractorPlugin *pNextPlugin) { m_pNextPlugin = pNextPlugin; } qtractorPlugin *nextPlugin() const { return m_pNextPlugin; } private: // Instance variables. qtractorPlugin *m_pNextPlugin; }; //---------------------------------------------------------------------- // class qtractorMovePluginCommand - declaration. // class qtractorMovePluginCommand : public qtractorInsertPluginCommand { public: // Constructor. qtractorMovePluginCommand(qtractorPlugin *pPlugin, qtractorPlugin *pNextPlugin, qtractorPluginList *pPluginList); // Plugin-move command methods. bool redo(); bool undo(); private: // Instance variables. qtractorPluginList *m_pPluginList; // Special case for aux-sends moved into output buses... qtractorAuxSendPlugin *m_pAuxSendPlugin; QString m_sAuxSendBusName; }; //---------------------------------------------------------------------- // class qtractorActivatePluginCommand - declaration. // class qtractorActivatePluginCommand : public qtractorPluginCommand { public: // Constructor. qtractorActivatePluginCommand(qtractorPlugin *pPlugin, bool bActivated); // Plugin-activate command methods. bool redo(); bool undo(); private: // Instance variables. bool m_bActivated; }; //---------------------------------------------------------------------- // class qtractorPresetPluginCommand - declaration. // class qtractorPresetPluginCommand : public qtractorPluginCommand { public: // Constructor. qtractorPresetPluginCommand(qtractorPlugin *pPlugin, const QString& sPreset, const QStringList& vlist); // Plugin-preset command methods. bool redo(); bool undo(); private: // Instance variables. QString m_sPreset; QStringList m_vlist; }; //---------------------------------------------------------------------- // class qtractorResetPluginCommand - declaration. // class qtractorResetPluginCommand : public qtractorPluginCommand { public: // Constructor. qtractorResetPluginCommand(qtractorPlugin *pPlugin); // Plugin-reset command methods. bool redo(); bool undo(); private: // Instance variables. QString m_sPreset; QStringList m_vlist; }; //---------------------------------------------------------------------- // class qtractorPluginProgramCommand - declaration. // class qtractorPluginProgramCommand : public qtractorPluginCommand { public: // Constructor. qtractorPluginProgramCommand(qtractorPlugin *pPlugin, int iBank, int iProg); // Plugin-change command methods. bool redo(); bool undo(); private: // Instance variables. int m_iBank; int m_iProg; }; //---------------------------------------------------------------------- // class qtractorAliasPluginCommand - declaration. // class qtractorPluginAliasCommand : public qtractorPluginCommand { public: // Constructor. qtractorPluginAliasCommand(qtractorPlugin *pPlugin, const QString& sAlias); // Plugin-alias command methods. bool redo(); bool undo(); private: // Instance variables. QString m_sAlias; }; //---------------------------------------------------------------------- // class qtractorPluginParamCommand - declaration. // class qtractorPluginParamCommand : public qtractorCommand { public: // Constructor. qtractorPluginParamCommand( qtractorPlugin::Param *pParam, float fValue, bool bUpdate); // Plugin-port command methods. bool redo(); bool undo(); private: // Instance variables. qtractorPlugin::Param *m_pParam; float m_fValue; bool m_bUpdate; qtractorPlugin::Param *m_pLastUpdatedParam; }; //---------------------------------------------------------------------- // class qtractorPluginParamValuesCommand - declaration. // class qtractorPluginParamValuesCommand : public qtractorCommand { public: // Constructor. qtractorPluginParamValuesCommand(const QString& sName); // Destructor. ~qtractorPluginParamValuesCommand(); // Plugin-value list accessor. void updateParamValue(qtractorPlugin::Param *pParam, float fValue, bool bUpdate); // Composite predicate. bool isEmpty() const; // Plugin-values command methods. bool redo(); bool undo(); private: // Instance variables. QList m_paramCommands; }; //---------------------------------------------------------------------- // class qtractorPluginPropertyCommand - declaration. // class qtractorPluginPropertyCommand : public qtractorCommand { public: // Constructor. qtractorPluginPropertyCommand( qtractorPlugin::Property *pProp, const QVariant& value); // Plugin-port command methods. bool redo(); bool undo(); private: // Instance variables. qtractorPlugin::Property *m_pProp; QVariant m_value; qtractorPlugin::Property *m_pLastUpdatedProperty; }; //---------------------------------------------------------------------- // class qtractorAudioOutputBusCommand - declaration. // class qtractorAudioOutputBusCommand : public qtractorCommand { public: // Constructor. qtractorAudioOutputBusCommand(qtractorMidiManager *pMidiManager, bool bAudioOutputBus, bool bAudioOutputAutoConnect, const QString& sAudioOutputBusName); // Plugin audio ouput bus command methods. bool redo(); bool undo(); private: // Instance variables. qtractorMidiManager *m_pMidiManager; bool m_bAudioOutputBus; bool m_bAudioOutputAutoConnect; QString m_sAudioOutputBusName; }; //---------------------------------------------------------------------- // class qtractorDirectAccessParamCommand - declaration. // class qtractorDirectAccessParamCommand : public qtractorPluginCommand { public: // Constructor. qtractorDirectAccessParamCommand(qtractorPlugin *pPlugin, long iDirectAccessParamIndex); // Plugin-change command methods. bool redo(); bool undo(); private: // Instance variables. long m_iDirectAccessParamIndex; }; //---------------------------------------------------------------------- // class qtractorImportPluginsCommand - declaration. // class qtractorImportPluginsCommand : public qtractorCommand { public: // Constructor. qtractorImportPluginsCommand(); // Destructor. ~qtractorImportPluginsCommand(); // Plugin lists accessors. void addPlugin(qtractorPlugin *pPlugin) { m_pAddCommand->addPlugin(pPlugin); } void removePlugin(qtractorPlugin *pPlugin) { m_pRemoveCommand->addPlugin(pPlugin); } // Import plugins command methods. bool redo(); bool undo(); private: // Instance variables. qtractorAddPluginCommand *m_pAddCommand; qtractorRemovePluginCommand *m_pRemoveCommand; }; #endif // __qtractorPluginCommand_h // end of qtractorPluginCommand.h qtractor-1.5.11/src/PaxHeaders/audio0000644000000000000000000000013215124701674014336 xustar0030 mtime=1767080892.773882937 30 atime=1767080892.773779218 30 ctime=1767080892.773882937 qtractor-1.5.11/src/audio/0000755000175000001440000000000015124701674014403 5ustar00rncbcusersqtractor-1.5.11/src/audio/PaxHeaders/metro_beat.wav0000644000000000000000000000013215124701674017253 xustar0030 mtime=1767080892.773882937 30 atime=1767080892.773779218 30 ctime=1767080892.773882937 qtractor-1.5.11/src/audio/metro_beat.wav0000644000175000001440000002455415124701674017255 0ustar00rncbcusersRIFFd)WAVEfmt €»wdata@)  ÿÿøÿëÿÓÿ£ÿWÿÿ¸þkþ'þþ×ý³ü0û†‹zü¤ó\ôzòìãè'ç,y@H$¿À™7¹ŸQmwýç”,Xj§{ùK?Ctœe³:‚Ó´ëí’g·wµrµ'Ù•v—ê¡€Q“¼Óó-ÄC„Á„>Ž ÙÛ]Šg€䉆úmu~v‹H+ùìÒ’ß9B  4\/zfq}oBd{yÄq,îÝ—¬¸ŸÛeAz]ƒaa0}±W‚§—Ÿ‚ɉ{±¾Ï ¾ZˆË˜¦ % ˆØ„¦…5ƒîS×PU^|ltsW¦Jô­ôî6Xxž`¢:þa-u£z÷q zÉu'F¹ ¸t×ÕDýN&-¾ÈˆÞ‹l…‡› ¶Z³ý‹ŠÜ1â×(¼ñЊӓÃx3 oJpÖ^‡.ª/ô-¥ouN„_@qÜzuwyv%?äÞŒÁiÚcÛ1c=Ú'Þ$–†ä†m‡’¥<©ÇL‡„ºqì.Û ÇFŸÝ”^še¼/yZ8j"`÷=& ¾-f/yU`•^n'vŸx¹xpt6S¡ÿÏÒúßóÇ!È,î!@ñ_¦ž‰Ë‡¦ˆ‡…›H¢Ê“ ˆ ¢ÕSÙœÈÆ¯gt ¹áü$D`W]1G2ð$61\áuŽl…DçT³h'n³j^hÚh›h_iHdäUž9?ÝùüÕÿŠ'þHì¥ËV­¾ã—J˜:˜6˜–×’÷–/¨ç¸á¿é ¾ÄÀÌFäz q+Ñ;NBâ-=Â>§BëLß]hhmjg=fÀdÒd[bY’H )0‹ þríûò Ý˜¾}«  œ>œvšŠ™ð–M—šž¯{º"¿ýÃJ¹ÊÊÚÜôºé,Ä8={<áAìI²WôdiYg]ed­bCaBZYL¤3 r¤ýô¾âµÇá±É¤ðžž!œ ›ï˜*˜«W·E¾DÄæÃÝÊØxíp/%c3m;;8AfGÂRî`§fºfŽd[ca`·Z¡NÄ;c!¿‡ÿ½þrõ)ç6Ѹ“ª¢þŸŽž®œèš™Ëœó¦ë³É¼ìÂáÅ;Ê•Õðç³£.å7±9t? E Oa\Ìc¥eÐcFbÙ_^\ZiPA(uÈ Ÿ°ÿcö¼êHØš¿~°q¦4¢ß nžãœ8›,¤ô°ªºœÀÐÆˆÉžÓãêú´'3‚8=<ßB{KQW·` dcHa._]ýYR×D./Xl ]7øíí›ÝôÆþµ¢ªâ¤ê¢E ´ž ³Ä¢=®R¸H¿˜ÆÉÒà¾ó‚L!,.Ÿ6·9 AcH¬Rm]ãa)bz`¹^ü[‚YSiG96!Ô†Mãúlð@â†Î–»l¯¨¥{¢v ÐžžE¢«þµ¾jÅÔÉCÑÝRî™)‡3~7ê>¤EãN²Yb_ú` _÷][ÍX„SfI/;c&}d { ý¶òBæŒÕVÁr´¢«E§á¤K¢¬ ÏŸe¢8©²³b¼NÃÊ®ÏzÚ³éùþÚb$S/l5Ê;çB`K‘U®\q_Á^]ŠZðW¨S KÆ>Û+7¯°þÍôÃé¶ÚSÇb¹a¯·©#§-¤~¢T¡Î¢Þ§¬±†ºšÁyÉsÎkØþå2øŽòä*A38H@HXQÆYu]¸]<\#ZWœS™LzAi1÷EšÕ÷íLߦÍB¾k³³¬R©O¦R¤ü¢‡£f§Ë¯Í¸aÀDÈêͼÖÞâñòc‘}&c0Ò5É=ýD–M‡V![p\P[ƒYwVdSnM}C"6r$@j ŽÇúþïŽã Ô<Ãî· °±«¿¨=¦»¤Š¤r§®ú¶¿ƒÆoÍÊÔçߘîö#$"·,<3Ê:ýA%JSžXîZ^Z¶XæUäRÕMKE”9Ë(T­ý“òQç.ÙsÈW¼f³®üª¨t¦Å¥¾§Ê¬Zµj½£ÄšÌÓpÝ®ê–û°]¢(¼07(?÷FVO V/Y^YùWUfR9NÒF:§1E"\Ÿ ¼øôí¯á_ÓÅ麵³t¯D¬÷©¯¨©¬²gºùÁ«ÉÁÐCÙeäIò¼t ª*¨1›9Ü@H5PßTÄV7V‹T˜Q>N³H-@ô4&ýß rûøð‹å]Ø«Éò¾ñ¶Í±†®Ò«Nª ªS¬a± ¹—ÀäÇ¿Ï0לá~îlþê?ú&è.e6ß=cEðLvRU-UËSCQìM3I¸A7À)ücêýêóé¡ÜyÎÃCº]´Ã°»­ý«R«È¬Ò°Ú·8¿aÆgÎyÕUß>ë‰ù2 Úù"0,13÷:@BŽIéO0SûSøRçPŸMŒI C³9•- œ B ÷ìœàMÓÇʽ2·³Ë¯µ­²¬n­›°³¶ì½,ÅïÌ,Ô)Ý!ètõyŒGü(A078N?€Fˆ5 *ʳpDÿ õPëàœÓàÈcÀMº;¶³8±~°±Õ´™ºVÁQÈ÷Ïp׋àtëþ÷ZHž'/Ã6¯=kD@J§MáNANxLdIjEˆ?Q7-ù Ȉ °©ørî¥ãû×™ÌÊý}¸$µâ²Ô±K²á´Ä¹2ÀÇbÎóÕoÞ‘èrôEP®_$,ß3Ý:‡A¡G›KxMSMÛK)IŒEI@Ò8‰/Ú#·cc€ûfñç ÜqÐBÇø¿ÚºF·¥´F³;³+µ2¹2¿ÓÅÝÌ‘ÔrÜ æEñþ] êõ A)Ò0 8º>èDIëKOL7KäHEÝ@.:™1Ã&¼@:"þaôaêžß[Ô¸ÊäÂ?½[¹d¶·´@´ŽµÞ¸P¾£ÄmË Ó‹Ú»ãSî7úƒ;v&é-[5<-BGG=J4K‚JœH~EWAe;i3•)~ °g÷zí'ãFØ ÎÆË¿}»B¸9¶fµ$¶Ó¸•½ŸÃ8Ê{ÑöØŠá‚ëæö¥œe#+’2?9…?÷DdHóI´I*HhE AK<õ4ÿ+K!Ôò HZú„ðqæÜ§Ñ-ɇ­½>ºÔ·ª¶ä¶ù¸ù¼¦ÂÉáÏe×zßîè¶óÇÿó ·& 5(±/‡6ê<~BqF‡HÂH˜G2E½Aê;9µ2µ*5!  Æùûžò8éŸßÖÎ…Ç’Â ¿Š¼E»K»ô¼HÀ@Å˃ÑÄØ[àBéóåý 4\g$¡+e2e8¤=—AÌC[DCoA8>Ø9ò3z,˜#©gŠþõ@ìÓâ]ÙÑ.ÊÚÄöÀ*¾•¼6¼Z½ÀšÄ ÊGÐFבÞç{ð¸ú±çAž!ì(Ò/ó5R;ž?CBGCÑB"AD>Q:õ4.¸%- "Oø!ïèå·ÜÔåÌ1ÇýÂæ¿ù½@½ì½3ÀÄ6ÉÏÓÕüÜåöíÜ÷rÄ 0Ã9&=-h3í8‹=•@BBº@0>œ:Â5_/­'Ū­ ¤óúôñóèäß׷ϲÉŵÁt¿f¾¢¾gÀ¼Ã{ÈÎpÔoÛã¨ëõLÿ˜ +Á‰#*å0”6`;Õ>¼@A-@>Ò:[6‚0c)ã *ƒý¸ôÛëõâ8Ú’Ò2Ì:Ç—Ã Á§¿}¿ÉÀ™ÃçÇ%Í.ÓÚFáwé‘òYüáË Ø'c.74(9ý€=ë:I7e26,Ñ${Ö; ‚úJñ¬è6à,Ø_Ñ»ËoÇDÄ>ÂbÁÑÁ«ÃóÆ„Ë÷Ð,×ÞŽåàíðö“ @±"x)ƒ/È4$95<Ù=>=à:“73b-s&x’ ï•üô†ëãÛûÓÎlÉÆ«Ã}†ÂñÃÅÆæÊÐÙÕ‚ÜÅã½ëzô½ý<0„ ä&'-27…:Š<*=‚<­:¾7³3i.ï'g 3æ^ÿšö'îæåÒÝžÖ}ÐŽËÒÇ-ůÃ^Ã]ĵÆmÊ.ϺÔ!ÛâÍéòûZS ºla$´*N05¾8;#<Û;b:É74?/5)0"=Ì y7ùÓð•è¯àFÙæÒ¾Í¶ÉÉÆúÄQÄçÄÒÆÊrμÓÍÙvàãçöïŠø¤„ úÆÍ!O(.ð2â69ð:;Þ9ž7J4Ù/@*œ#ù Ʊûˆówëšã=ÜÅÕ^Ð+ÌÉõÆÆAÆÇǑʎÎ{Ó%ÙßxæîUöÿ¥þº¼#%Ü*Ò/ä3Û6Š8õ818P6f3p/_*H$.¦æ ÷þGökîÛæßÙšÓ-ÏÜËŒÉRÈ5ÈNÉ£Ë%Ï¥ÓÚØæÞtå¬ì[ô›üê ©¢ú!Å'½,é04 6Í6f6ê4a2Ú.=*­$2  kÉø:ñÕéÇâRÜ»Ö1Ò¥Î!Ì¥Ê:ÊðÊÙÌÞÏçÓÅØaÞ…äUë£ò^úsN ð Á$È) .]13¯4Ÿ43R10. *û$L3æ ‘2ûÛó³ìÀådßÄÙÕcÑ«ÎìÌ7Ì”ÌΪÐLÔ¼Øìݹã&êñcø­Ï%Ö!æ&4+³.(1†2È2ü1.0o-¾)%“J–¥ týYö`ï±èTâ³Üô×Ô8ÑAÏHÎUÎsϤÑÚÔߨ±Ý(ã"é±ï¨öáý6X !^${(,¾.[0ì0r0/œ,P)% 5å< aŽÿ­øãñUë+åŠß°Ú¹Ö¦Ó|ÑDÐÐÌИÒgÕÙ}Ý‘â;è^îÿôëûýé ‰¬Vk!Ú%ˆ)Y,7./ã.Ä-·+È(ü$R ÷ùª ÝúVôîíÚçBâ`ÝTÙÖÀÓPÒÎÑCÒµÓ$ÖjÙ€Ý4â€çIí‚óúØ›#»ËD#ÿ&ø),"-C-r,»*'(¸$} „çð¸ Nçü”öRðiêéäàÎÛuØôÕPÔŽÓ¹ÓÖÔãÖÒÙˆÝëáÝæOì6òuøëþd½ ²FOà ž$°'î)D+©+$+º)z'e$ öà Îþ®ø¦òÙìiçƒâBÞÏÚ*ØQÖWÕ=Õ ÖÃ×Xڲݹá_æ|ëñóö#ýa‰ Z×ÕW5"d%Ì'V)ú)º)¡(®&ø#t Aih Ž£¨úÌô.ïÐéïä§àÝRÚRØ ×ÇÖK×±ØðÚòÝ¡áïå¸êüï£õûx4 —‰ ó2#¿%{'Z(](Š'ê%~#W zøô— FüßöRñì?çýâYßpÜDÚâØPؑجٚÛHÞ¬á¨å êïdôýùÂÿ‡ cQÀ± !±#—%«&í&^&%å" b¯¡E ×IþÇømóLî€é>åá†Þ9Ü¯ÚæÙéÙºÚ[ܸÞÊáså§éCîUóœø$þµ J&šŽï°!¾#%~%.%$J"‰·Ty ;ãÿœúXõ]ð ë_çžãŠàÞhÜoÛ9ÛÈÛ Ý3ßöáQå8é–íUòg÷¦üD\ !~té¹é!Z# $ö###•!VríØ^ mHü9÷Mò¯ívé¯åŒâà,ÞÝšÜìÜÞÊßBâZåöè í‚ñEöGûnƒx &ræÐ °!‘"¶"!"Ø à> C„ µÌÖýîø.ô£ïrë©çqäÖáÜߎÞòÝ ÞÞÞcà“âbå¶è…ì¹ðIõú÷þà²G ž€óU !v!! X–©_ ÎWÿ—úêõñRí–éUæ§ã‘áàSß9ßÏßáã‰åŸè)ìð_ôëøœýWˆ À£ k£*  4¿¦Î&Ê P¯üš÷=ó(ïpë)èlå7ã£á±àfàÅàÌáwã¼åŽèÕë†ï—óÞ÷[üêqÙ ÿ ØZ_çÝ5èõZ KßöƯ _ûŽý$ùíôèð7ííéçÚä(ãâ—áÄá”âäæ”èšëïßòùö5û•ÿö?N $˜¢9F¿šÕpnÒªúÜRv ^+Þþ¤ú‰ö—òïî¦ëÍèxæ§äoãÎâÊâdã˜äaæ­è|ë°î?òö4ú[þ”½À |뜾TU¼ˆºZoüÄ H:" üø-ôðIíiêèæÆäþãÏã6ä4åÃæÛègëhî¾ñ]õ?ùCýTQ; ñ Vb;ê ›“ùÐç9%» :MQýoù©õòäîìŒé—ç!æ:åàäåãå:çéoë.îKñ¾ôbø7ü Êm ÈÚ„Å…Æzž3:¾ÄQuB È %Y‹þÇú÷™ó`ð}íëùèoçhæèåõåæ²çYézëîðð&ô™÷AûÿÆyþ V [ U.‡[¥eŸVŒU±³ m ðX°ÿüuøõØñ÷îsì`ê½è›çûæàæNç>è±é›ëôí¥ð§óòöcúúýš0©è çœëÓE7¦øÜKCÚý ¯9¶2ýµùdö?óaðÙíµëêÈèèÇç èÎèêÄëèífð>óRö ùý„ý` Ž ;‘‡¬¼Mb+ïWs [²Eþëú®÷—ô¿ñ5ïíFëñéé´èÏèhé|êìñíDðãòÈõéø0üÿÜ&M 8 â@<Ôú©Ý•Ô¡öÝ êÑ‘Nÿüàøàõó†ðRîìë$ê¢é™éêïêGì î'ð˜òVõEøcûšþ×ü ¡ û¨å°ãLDÍõ¿9 r ygEýú÷HôÂñï²í;ì+ëêdê­êjë–ì+îðcòîô²÷ªú½ýÚòëÅ c ÃÍб$%´Ó‹áÙ€ æ * þ û9ø~õýòÃðâîXí8ì€ë6ë\ëðëòìZî"ð9ò˜ô:÷úñüðÿíÚž: ‘  _¿¹HhcEÁé½ L ¤Õô ÿüQù¡ö&ôëñþïmî7íhìììyìTíî-ðòUôÈörù7üÿùÐŒ k D²Áh§{éï˜åâ   u¶áÿýUú»÷Gõóñ~ï;îXí×ì¾ì íÂíÚîJðòôoöìø•ûNþÖ€ U e .ªÊ‰ãØf”eÛ á ‡a±þýPûÄøYö#ô/ò†ð5ï<î¥íoíí/î!ïnð òõóösøúú›ýHò„ÿF V )ªÙ­!2â3&Æ  æ‚iÐþ>üºùZ÷/õ:ó‹ñ+ð$ïwî'î7î¨îvïðò×ó×õøpúòüƒÿ›@ R " ®èÏZ‡TÆß  D 5 õšÿýªúUø+ö7ôˆòñðEïÞîÒî#ïÏïÒð(òÃó¤õ¶÷óùVüÌþH»OX ) ¼ û›ßÊ[’z e z [¼OçýŒûCù ÷.õxó òäðð•ïpï¤ï1ðñCòÀóyõi÷‹ùÊû$þ‡æ5`i 9 Ì #Õ16ã<Eÿ w ® ±ŒJý«þ\ü'ú øögôïòÁñßðMðð*ð˜ðZñlòÂó]õ-÷*ùPû‰ýÕÿ ^„O ê @ Rˆ¥næ é  Ö ùøÑ\ÿ%ýøúíø÷KõÎó–ò¥ññ°ð°ðñ¦ñ—òÍóGõööÚøÞúý5ÿf”­¢r f ƒ VÛò„Ê Ç } õ 8OK+âýÄû¿ùá÷+ö«ôiónò»ñTñ=ñtñûñÍòçó>õÒö’øxúüœþ¾ÚáÒ˜1 • ¸ ™ /ys!„ œ t n¢´³žþ„ü‰ú­øúö|õ3ô+ójòòñÅñåñPòóô>õ°öSø"ú üþ'%Éd È ö á ‡ â ó º 6 m _  —é(02ÿ4ýKûwùÈ÷Eöüôëóó”òTò]ò¯òIó(ôGõœö&øÕù¤ûŽý†ÿ~nF™ÿ 3 ) Ü J o M ã 6 E  ·%m˜¶ÇÿÞýü7úŒø ÷ºõ¤ôÊó3óàòÔòóóTôTõöú÷ùJûýûþáÔHÙD x w 7 µ î à Ž ù %  ËTºþ-V~þªüíúHùÉ÷vöXõvôÒónóOósóÜó†ônõŒöÛ÷Zù÷ú³ü{þO!æ•"‡ Š f m 2 µ ù  Ø{÷WÚÿMý—ûýù€ø-÷ öõnôüóÊóÚó+ô¼ô‹õŽöÇ÷(ù­úRü þÌÿŽBéqÙ  ñ Š ä þ × q Î ò ßœ1¤O™ÿæý;ü¤ú,ùÜ÷µöÂõõˆôEôBô~ôøô®õœö¸÷ùqúûûžýNÿ¬DÊ+fv R õ ^ ‰ v % š Ô Ü²_ê[¿rþ×üIûÕù„ø[÷eö õõÃô®ôÖô:õØõ­öµ÷åø;ú²û=ýÙþ{­'…ÃÔ· f Ü   Ù b µ ÒÇ)¯'ùþiýäûuú&ùü÷÷3öõ=õõ.õ~õöÃö³÷Íøúnûäünþ‘Œé#6 × X ¢ ± ˆ & ÃȦ^ù€þwÿðýxüûÄùšø™÷Èö&ö¼õˆõŒõÈõ<öäö¼÷Áøêù3û™ü þŽÿŽüPˆŸŠI Ö - L 5 æ c ®É»ˆ;Õ`èÿqþý£ûZú0ù,øS÷«ö5öòõæõöqö÷É÷¶øÊùûQü·ý(ÿ p¿÷ ýÀV ¹ ç à ¤ 4 ’ÅË­q!¾Uçþ„ý+üëúÃù½øà÷0÷¯öaöFö_ö­ö-÷Ü÷µø·ù×úüfýÇþ-’ë6h{o:ÖD € ˆ ] ÿr¹ÔË£b¶Yÿþ°üqûLúFùhø°÷'÷Íö¤ö®öêöW÷ò÷ºø¥ù²úÞûýmþÆÿr³ãöè·]Ô 1  ÊP¨ÙãΟ]Ãÿrþ-ýõûÓúÍùêø/øž÷9÷÷ÿö+÷†÷øÃøžù˜ú®ûÞüþfÿ³û8`pf6áa³ÖÊ%‘ÓóðÔ£h"àþ¤ýrüTûPúkù¬øø¦÷e÷R÷o÷º÷1øÑø™ùúˆû¤üÑý ÿNŒÁäôçºjòO~€TûyÍü ã³~GÿþéüÏûÌúçù"ù…øøÄ÷¥÷³÷î÷Uøåø™ùrúeûpü‘ý¿þðÿ#PpzlCõ„é$2ÌYÀ"(úϤÿ{þWýFüFû`ú™ùõøyø%øú÷û÷'ø~øýø¢ùgúIûGüUýuþœÿÅæÿøÍ……ÊåÔš8°4JI:ýÿÚþÂý³ü¶ûÑú úbùßø‚øMøBø`ø§øù«ù`ú4û üý1þKÿi…”™†\¯#q–“f™ý@fuqdM8ÿ'þý%üAûxúÎùEùáø£øŒøø×ø7ù»ùbú$ûüóüôýÿ&1.ðªGÂGO/ê€òF|š¥¢šŽÿƒþƒýŽü¬ûâú4ú¨ù=ùõøÔøÚøùXùÍùdúûæûÉü¾ý½þÅÿÎÒ̳‡DâcÀù øÀcåGŽºÓÝàÜÿÜþãýóüüJûšú ú˜ùIùùù9ù}ùåùlúûÑû¥üý‚þ|ÿ{wkO"Þg¨Å¾’CÔC—Òù(0ÿ;þSývü®ûýúgúòùœùiùYùmù¤ùþùxúûÁûˆüaýJþ<ÿ1#òÂ}"©[‚„d#¿> è@Zm|ÿ‘þ«ýÒü ü\ûÄúIúíù²ù™ù¢ùÎùú‡úûµûnü<ýþþþêÿÕº—d ÄP½ ;H3ü§3¢ù:j«Åÿâþþ-ýhü·ûû ú?úüùÛùÚùúù:úšúû­û[üýéýÅþ¨ÿ‰b.í–)¥Jpxb1ä~uÖ*v¿Rÿ¥þþjýãüqüüÍûŸû‰û‹û¥ûÕûüuüàüYýÞýmþÿ™ÿ/ÃOÐF®K~¨ „VÉm•£(¯ÿ8ÿÉþaþþ´ýqý>ýýýýý,ýWýýÓý!þyþÖþ9ÿÿeÅo·õ'Mfro`Eí²o%×…3áÿ‘ÿFÿþþ¾þ…þVþ1þþþþþþ/þRþ}þ°þéþ&ÿgÿªÿîÿ1q®æDgƒ—¡¢›‹sT.ÓŸi2ûÿÆÿ’ÿbÿ5ÿ ÿìþÑþ½þ°þªþ«þ´þÃþÙþõþÿ:ÿbÿÿ¹ÿçÿ@i²Ðêþ  þêÒ¶—uQ,ãÿÀÿŸÿ€ÿeÿNÿ:ÿ+ÿ!ÿÿÿ ÿ)ÿ6ÿGÿ\ÿtÿŽÿ«ÿÈÿæÿ">Yp†˜§±¸»ºµ­¡’€kU=% ôÿÜÿÆÿ°ÿÿÿÿtÿmÿhÿgÿiÿnÿvÿÿŽÿÿ¯ÿÁÿÕÿéÿýÿ$6GVbmuz}}zundYL=. ýÿíÿÝÿÏÿÂÿ¶ÿ¬ÿ¤ÿŸÿ›ÿšÿšÿÿ¢ÿ©ÿ±ÿ»ÿÆÿÒÿßÿìÿúÿ!,6?GMQSTSPKE>5," öÿìÿâÿÙÿÑÿÊÿÄÿÀÿ½ÿ¼ÿ¼ÿ½ÿÀÿÄÿÉÿÐÿ×ÿßÿçÿðÿùÿ ").25788630+% ûÿôÿîÿèÿâÿÝÿÙÿÖÿÔÿÒÿÒÿÓÿÕÿ×ÿÚÿÞÿãÿèÿîÿôÿúÿ !#%%%$#! þÿúÿöÿòÿîÿëÿéÿçÿåÿäÿäÿäÿåÿæÿèÿëÿîÿñÿôÿøÿûÿÿÿ  ýÿûÿøÿöÿõÿóÿòÿñÿñÿðÿñÿñÿòÿóÿôÿöÿøÿùÿûÿýÿÿÿ ÿÿþÿýÿüÿûÿúÿúÿùÿùÿùÿùÿùÿúÿúÿûÿûÿüÿýÿþÿþÿÿÿqtractor-1.5.11/src/audio/PaxHeaders/metro_bar.wav0000644000000000000000000000013215124701674017104 xustar0030 mtime=1767080892.773779218 30 atime=1767080892.773779218 30 ctime=1767080892.773779218 qtractor-1.5.11/src/audio/metro_bar.wav0000644000175000001440000002455415124701674017106 0ustar00rncbcusersRIFFd)WAVEfmt €»wdata@)óÿäÿÐÿ¼ÿ©ÿÿwÿxÿÿUÿ*ÿzÿ +Ïÿ+^KþF ûxZëªÑ*Bàûç{±d㱆?²Ü€ñ…;°Ô~Ž~Òèºc?ŸUª+£zƒ[Ð}ãU‘óCsgðG`‹ªº£ßÉZ¶×ƒ5:®jƒÝ‚s ~sS” á‘jF<ðfÙyàhæu¿×»‚Ic_¶6ƒw¤Êu‘¿¯Æžè†V”Ã9”{Ö)Üþê 'zY6y0wDx/Q ²ËæÇ?%K•ÉV‡í„Œ£€»N†$Þ†é4˜Š“´‘W­sHñî@ÖwíF¬xóuiy²ݶw…6ˆ*[¥ÆŠ‚ŽÅ¤Ï¦@‰ î¡Æ9–÷”¡ä#cÄbz"–ôai,^»wŠv#pû^Í595åT•tŠÉ—=¦ò‘÷š÷ë§»å™<¥m­fHJN&Í&Fp__$l;v_vD\úæ;â¬#!)‚ÛŽŽ+Š`—þ£ ‹øµEß²³W '¼¦1 a77%°=6té\Šp½trñ=9à½ñ2’º›ŒrŽ™Òœ™ŒųÌÞ¬Á¦³Ö¸BâY½0+cXr+aÃnïriŸŠâ´ó- ¨æe”J›’•¤“>Í0Ã\ª£³áùrJlP 5g4ófìkvfVn=pP]— Õí`_ðéSŸÄq˜*œ;&¡ZÍtÁ¡®>´MKCô5"Allgwimmék1LIþ[ó7‘ †Ì š ’˜²š‘µ±.ʤ½ŠµÔ÷(µJ¾9Y6NÇmçd‚iZl¿c?5@ùé÷È Kü.¹c——š ˜ä”•ºÎÃþ¸ª¼èà3»H*7A;ï\›lÄdgŸi´Z¦†ø€ÿήìÇ­˜‚šD›ë•›—ÀvÃn¸øÇ%î9«D?;æAPdiÿd=f¢e$P’¹ü4Küܨ™sœá›š•楫·ĉ½ªÓ¥n=‡=Ã;çIùffèd:eX`‰B§ zûmŒùjÈ™£;›œ@›E˜ ±hÃqÃÞÃñá"=ò8f=UR‡g©c_cÈcNX–0¼üÀ5ﻡ.ž‡„šœ€·úÁ!ÁÕÊ0ñ *>§8AA‹[ºfPb×`“`UPz!$Øÿ}ý®ã¬³ç / [ž[šá¡¬¼åÄkÂgÔL–/Ò<‰¯:¡¡ôžØ›OªÅ¿iÇÓÇDÞ9(3Î8X=ÎKLaýaÛ_ÿ]ÆVâ;ÚÙÿ­"ñÈY«¦¢¡ížäž‡²#ÂÁÇ¥Íñéê™47b?úQaº_™]\pOM-³ þòû©è¿õ¨l¤ù¡Ÿ}¢Þ·ÃQÇ‚Ôöh#Ù6I8îB2Xô`^#[X`H>"–&Þ÷\ß¹b¨¥„¢ÊŸŸ§v¼ÇïÉìÜR”(ê6 <G[?_q\bY[T]@h·Ãò>ÕXµ!¨Ï¥£Þ¡®õ¿ʛφå%, 50=8Kì[L]ØZ WæN´6s×ý(ìÎÉϱû¨Â¥Z£Ó¤æ´ÑÂvËÕ^ï×.ú4–?P \D[LX4U?Hå*$6øÇä»Â¶¯ª†¦ ¤G¨°¹ÅyÌÛú¥K1'7†B/Tq[rYËU’QB"p $MôoÝ𽿮¨ª§W¥í¬¾—ÉÐõâ§#22Y:ðE'VZšWÒS|MÂ:'B,ÿoïÝÔ„º/®ˆªž§§Ð²¯ÁËÌoÕ–ê?7'Õ1ä;8I¾V\XÕUÑQ MÞV‡VFS-OiB€(B?ö?ã'Æ3µÿ®« ©Š­€¼ÉÇÉЩàiün3-t5ã@PVV¾TåP KÅ<! ÞÿVòÎÜöÁ´?¯—«‡ª¼±ÀK̶Ô0ç­0 ¤.K8ÔCƒQUæRÂN©G/6À´Êý°íwÕѾ]³¯4¬Ë¬·CÄÏãÙ.îØ#!/î9…FîQSûP‡L´B—.P;ÛùbèNÎÕ»v³0¯ï¬x¯—»ŠÇÒ¶Þ˜õ&'º07ùKsQP5LSF8±ª €ÿDñúܟŵ¸“³°¯o¶‹ÃÏÀؚꬲ+6GAMXPQNJB2×–Ïü®ìxÖœÂó·T³¦°¬±»)Ç\Ò§Ý÷ð !Ï,Ø7³CuMìNdLºGÏ=2+Õ[Àøºç“Ðã¿Ø·v³o±4´ ¿QÊÕUâ¹÷®]$±.:EFiMUMýIÝDÖ8Ü#<n>ôšâ7ÌA¾Ñ·Þ³•²0·îÂÚÍ+ØÆçJÿM<'o1G<H×L¥K»GpAã3i ÿ}ð}ÝÉ ½º·f´?´Ñº˜ÆàÑQÜfí¤w )®3”>áHÌKçI‚E®=[.ÐTòûì½×)Æ:¼y·ü´S¶Ý¾ Ê!ÕóàIó^ Ë‘*ª5´@0I†JH%CQ9(Wè÷”çÉÒÂÃò»›·Íµ¬¸~Â?ÍØyånùM"¶,Ï7ßBI I½EO@Í4Á!Å=ÉóåâðÎÂÃ»î·ø¶z»Æ»Ð@Ûê9¯Ã$^/â9KDH|G–C =0‹ñ þ ð8ÞüËèÀ»l¸œ¸Õ¾ŽÉ…Ô_ßÓÂ&‡1å;òD”GÂESAf9ü*½ùMû×ë*ÙfÉ+À^»¹¤º…ÂîÌÎ×ÉãJõÇ Ü(v3À=ETF×CÞ><5F%+L÷¥çíÔ>Çп»ºø¼îÅÐÒÚ/èÞúQÿÄ*g5y?üDïD¯A<1ÀõoóaãŽÑ·Å‚¿Ù».»“¿EÉÓ>Þ íó\™"U-`7¨@wDuC–?é8¯,ð_ þ¸ïßÖÎŽÄ<¿S¼Ä¼«ÂzÌ ×:âÙñ‹¬$Z/792A“CÕA`=q5ë'‚m½úáë¤ÚrÌÆÃ ¿û¼°¾òÅ´ÏLÚfææö9 )°&:1Ú:\AnBö?õ:›1Æ"MÄßöóçþÖrÊSÃ3¿æ½ãÀÉÈÒeݦêüi!ñ(#3O<,AAä=,8œ-ÌV´@óäÔ ÉûÂŒ¿!¿hÃHÌ6Ö×à(ï˜6¼ L+ä44=›@¡?Å;5z)iº ¡ý°ï2à~ÑîǼÂÀ¼À]ÆvϬÙÈä¿óq‹¬"ú,)6:=K?=!9^1Á$ÕSú<ì¸ÜïÏêÇwìÁ}Ã'Ê1Ó\Ýékø{ &$.¼6bàLÕCÎAÊÀȇÊ`Ð\Ø©ádì úˆ U\ I)Ñ0‹5A64k/m'°Ò»E÷íêÞ|Ô–ÎBË~Ê1ͦӹÛïä:ð þ™ …Ô!*14X41\,®#ŸW ëÿsôXèdÜöÓÏ[ÌaÌýÏÐÖßOèûólG"#Ñ*Ï053T2ú.,)òË*2ýÈñÊåÛÉÓ’Ï¢Í~ÎóÒ÷ÙSâÏë©÷„Ù­$:+>0¯130a,â%Iý~ú4ïãÚáÓIРϽÐáÕÝ]å/ï<û˜î û$Š+…/0.Á)"< 4À÷Åì¹áRÙÔ'Ñ—ÐÓÂØÿßaè’òÅþa ÝQ¶%Ÿ+.`.Æ+÷&@¾é ¥ÿHõ‰êKàòØ{Ô1ÒUÒœÕ›Û ãtëçõ,Ë dj8&S+Y-…,u) $éf+ýàò_è*ßÃØúÔVÓ9Ô4ØbÞëå›î%ù)óÃY ˜&Ü*,­*-'@!—Q9¦úšðuæ?ÞÊØžÕšÔ4ÖÂÚá«è—ñ=üÐØ!Ð&)*œ*²(Å$L@e ±>ø~îîäªÝÙqÖ ÖSØZÝÍãkë™ôLÿ3 ‡"§!Ã&@) )¨&C"Z%¨^ÿö‰ì¹ãKÝUÙ[ט׌ÚÛß|æ-îˆ÷=h ô  "„&:(t'Ÿ$¾vC  ýôó½ê¹âÝÆÙbØGÙÕÜRâéùðXúÐ@2êg"& 'Á%…"%}ƒ èúùñ#éþá+ÝcÚ’ÙÛßÁä–ë¡óý&öBŒ…"a%º%õ#^ Ž î EÉøðÕçxáZÝÛ×ÚñÜYá*ç îFö·ÿ6 xS{"š$^$-")ðƒ)ÿÒösîÅæ%á¢ÝïÛ>ÜåÞœãéŽðàøB' Á(x5"¦#é"T âh^ 2!ýëôÚìöå áÞèÜÉÝïàÔåìë ó\ûzÅ ÕÜ­Á!”"b!p”Òð û-ó…ëMåá—ÞðÝZßëâüç)îlõÅý~LßvÂ.!p!Ò|IU§ìAù™ñlêÙä2á?ßßáìä%êgðÅ÷ NœÆá¦q 1 /}þû ÿ}÷(ð…éšä~á àdàÉâìæQì¡òú2ø ½‡,`—净¾· j+ýÜõÒîÉèzäØáßà¸á‰äâèYîÑô7üt È&Zø§ŒÔt} yfûUôªí@èäXâÔá&ãMæÒêdðïöMþãÊ «£[k›mNˆ¢¸ù÷ò¹ìáç¨äõâåâ£äèÆìlòøøEˆý ~þ<Ä‚«Sh= ˜çþ3ø¯ñðë¢çàä¡ãä)æÞé¯îaôõú) (9ýZ-‡h9 ¾<ýÅöðPëˆç:åjä;å¿ç¢ë‰ðTöæüÙM ë«V–$¢¶mYü®ûpõ™ïÞê“ç°åJåæRéZíaò3ø½þ`{ ¼X;Ûï‹ †]2ú9ôÈîŠê®ç7æ8æÎçäêï+ô úyËŠ mf9ƒ>Œƒ"´ ÎÕþÓøóîTêéçÙæ?ç0éwìÐðëõÌûo •óÎ01ä_ó*Sý”÷$òšíAê<èçTè•ê îtò£÷yýE Cw­šÙN£ DôûföOñ1íDê¢èTèséøë™ïôTùÿèN ùÔ©';÷t¼õ ­$¦úXõðéìdê"é0é¢êcí$ñ¶õìú˜&: •€™TÉ '[ Âþrùhôð»ì›ê³éêØëÆî¦òH÷züL =Hj› œ Ï­výXø“ó™ï¨ìäêUêëí.ð!ôÎøõýIVÆ ~KóQo`8 PM9üQ÷Üò=ï²ìEë ëìTî‘ñõGú\ÿx= g É=‡g Ì ™ àøÿûköFòïÏì¶ëÎëí“ïòò÷®û¯’ ï  É_ãh /‰¿þú—õÂñÛîýì9ìŸì2îÔðIôjøýå”Ï `"äðJœ Ï9“ý ùßô`ñÑîDíÐìíMïò£õÄùVþ }n ¸*•à1] ³ |ƒü,ø>ôñÕîšípídîeðNóéöû“ÿRÿ ü88( b7Õÿû\÷ºóÝðñîîîQï‚ñ„ô0øVü¾ v (ûÆ5ù â ÿÁþ’ú¥öPóÀð!ï{îßîLð¢ò¼õnù†ýÐô¯ Ó AÄF¿BÜ © éݸý²ùöôòµð\ïýî ïAñ½óëöŸú®þÏÃ= " F€»úJ½ }½ÄÂüíø{õ²ò¼ð«ïïpð=òØôøÅûÀÿÂv¹ ] 7' 'L ¢ Qœºÿßû>ø õ‹òÛð ð1ðFñ=óìõ1ùßüÈ›! Â}QO 4ŠÇþ ûš÷«ônòñuðÕð ò9ôþöHúòý¹d¯w • åPÒv N x!‰ÝýNú÷`ôfò;ñîð‡ñþò7õ øXûòþš) ¸ “ £Ï• P k‘ý¢ù™ö*ôpò…ñtñ@òßó/öù\üãÿj¶– è † TGd ¶ X h¦ÿ;üù4öô†òØñÿñüò¿ô&÷ú[ýÍ+Hñ  g ù ¶ ¥ Õ ]l*Êþƒûxøãõìó¯ò:ò–ò¿ó¢õø ûIþ¡ÚÇ9  7  ß ñ ivBüý×úø¤õèóæò¨ò5ó„ô€ö ùýû+ÿhz7s  ý  {   ~Œg=ý?ú™÷rõïó%óóØóJõ^÷ôùãü  –œ  ¶ ¤ Õ R 4—«—ÿŒü·ù@÷Rõôtóœó‚ôö<øØúÀýÎȈ߶ å `  ( ˆ T·ÓÕþëû@ùúöBõ.ôÌó$ô/õàöù´û’þŠcù  ½  ˜ | Áß þWû×øÁö=õ]ô,ô¯ôÞõ¤÷éùˆüZÿ9ïZQ ¿ ‡ Ÿ  Ë ù­DwýÒúzø™öHõšô—ôCõ“ölø¹úTýÚlªs ­ D / r  0àGÿÜü\ú/ø~ö^õàô õÙõE÷/ù‚ûþÌoÝðŠ “ û » Ü fo‰âþMüòùñ÷oöõ.õõsö÷÷ïùEüÑþr÷?&’ k ¦ ? @ ³°ZÔBþÌû˜ùÃ÷oö®õ‡õö÷«ø­úýÿ q’NŒ 9 I ¿ ¡õ¢(®ýWûFù ÷yöãõæõ‚ö°÷Wùdûµý,šàÛm~ ÿ è < PAò‡ÿ%ýïúùˆ÷ö"öLö ÷OøúübþÈB}c » ~ ³d£‘Iðþ¨ü”úÑø÷®ökö¹ö”÷ðø±úÄüÿZ”—D‚? o  (Æúç©cþ6üAú§ø~÷Öö¹ö(÷ øŒùWûiý¥ÿââi~  Ÿ'VDàýÏûüù‰ø‡÷÷÷÷­ø&úúû þ8_a €oÝÄ'е¨ƒÿhýtûÅùxø÷B÷l÷ø;ùÂú˜ü¢þÂÒ·RŽVŸf®‚òþþùü#û–ùnø¸÷÷Ê÷‘øÇùXû1ý3ÿC<|’6\3ö\€†þ•üÛúsùoøÞ÷È÷/øùTúíûÄýÀÿ»šB™Œ œµgÉïþ<ü¡ú[ùzø øø˜øŒùàú}üRþA)ïx®~ÜÂ48Ý;f‚ÿ¤ýëûoúJùŒø?øgøù úfû ýØþ»;¥ºh¥nÈ»V®å ÿCý¢ûGúDù§øyø¾øqù‡úìû‘ý\ÿ-ë{Ç»IgY;Ð)hŸþìügû*úGùÉøºøùáùûpüþÕÿ–>´â¶$%ºë¿Oªòÿ9þü2ûúOùñøþøtùOúûðüþHø‰ãó¨øÞ]{EÎ0ƒÿÜýWüûúaù ùGùÕùÀúùûlýÿ³QÊ û“Å‘û ËS»ÿ‡ýüæúúzùUù•ù7ú0ûnüäý{ÿ¢(ýxCššVÞOºþ:ýâûËúú™ùŽùåù™úžûãüWþçÿtë5>÷VTò7,ãmçÿaþõüµûºúú¾ùÌù8úýú üTýÆþLÊ,]Ké-œÓ¾q…ÿþ·ü‘û¯úúèùúŽú`ûxüÂý2ÿ«fS×ÐHoTš*ÿÄýüsû«ú5úúRúåúÃûàü,þ–ÿb˜™S½Ï‰ñ ë;ÔþýQü]û¯úRúKú›ú=û'üHý“þõÿUŸÂªL™@˜©ƒ9ßÿ†þDý*üNû¸úsúƒúæú•ûˆü¬ýôþN¢Ùæ·A{aõ?I Ùˆÿ=þ ý üEûÇú™ú½ú1ûîûçü þTÿ¢ç ½/R$¨æêÀ~8ÿüýÝüñûCûÝúÅúüú€ûGüFýlþ¬ÿï%6¼%äZŽd(íþÀýµüÞûFûöúòú;ûÍûžüŸýÆþ:^\)¶üõ¤ 81 Ùÿ§þ‹ý“üÑûOûû$û~ûüôüøýÿO|Ž{3«ÛÂ`½âÙ·ÿhþ[ýxüËû^û7ûYûÃûküJýOþqÿ˜¸º”7š¶‹n…gEÿ.þ1ýaüÉûpû]ûûü¹üý¢þ¿ÿßïà§6…T×!:3ÿúýýQüÌû‡û†ûÊûLüýìýòþ !´0kc‘ÓéåÕÿÆþÊýñüGüÖû¤û´ûü“üTý<þ@ÿNYK¼%N5ÝK‡œš‘ÿŽþ ýØüAüâûÂûâûAüØüŸýˆþ‰ÿŽr1À-¡<QSSÿ\þ}ýÆü@üóûäûüüýéýÒþÎÿ;“A¾ Òb¿òÿ.þ^ý¸üEü ü üHü½üdý1þÿé¯M¹ëâŸ${«ÃÐÿãþþDý®üLü!ü2ü|üûü¨ývþ]ÿM9ÆT¯Ñ¹jæ7f—ÿ±þßý/ýªüXü>ü\ü²ü:ýëýºþžÿˆg0ØU ³Ž3¨ó#B_ÿ…þÀýý«üiü]ü‰üéüxý,þüþÛÿ½’MçT“aüj³ä,ÿ[þ¥ýý®ü{ü~ü¶ü ýµýlþ;ÿî¸fðOzp3Å-t¦Ïÿýþ8þý ý¶ü’ü¢üåüWýñýªþwÿKÙzõDaKñ7lœÿÒþþ}ýýÂü«üÈüýý-þæþ°ÿEöŠ÷8G%ÓV¶ü4kÿ©þýýný ýÐüÇüïüFýÆýgþ ÿæÿ®j–ö(*ý¢|Ä>ÿ†þåýeý ýáüåüýwýüý þWÿÙ‹$žð ÓpéDÏÿÿfþÒý_ýýöüýBý©ý2þÖþŒÿI¨6¢çÿê¡2¤U§ÿÿqþúý¥ývýmý‹ýÎý0þ«þ7ÿËÿ_ébÃ,/Õ›¡ÿ,ÿÇþvþ@þ%þ&þCþzþÆþ"ÿ‰ÿõÿ_ÁV‚–‘vE±Yýÿ¤ÿRÿ ÿ×þµþ¦þ¬þÇþòþ-ÿrÿ¾ÿ XœÕ'à­p/íÿ®ÿtÿEÿ!ÿ ÿÿÿ$ÿGÿsÿ§ÿßÿM}¤ÀÑÕ̸™rEæÿ¹ÿ‘ÿqÿZÿMÿKÿTÿgÿ‚ÿ¤ÿÊÿóÿAb}™™‘€hK)åÿÅÿ©ÿ“ÿ…ÿ}ÿ~ÿ†ÿ•ÿªÿÄÿàÿþÿ6L^jonfYF0þÿæÿÐÿ½ÿ®ÿ¥ÿ¡ÿ£ÿªÿ¶ÿÇÿÚÿïÿ+;GNQOH=/ úÿéÿÙÿÌÿÃÿ½ÿ»ÿ¾ÿÄÿÎÿÚÿèÿ÷ÿ#-59:82*øÿìÿáÿØÿÒÿÏÿÎÿÑÿÖÿÞÿçÿòÿýÿ"'**(# øÿïÿèÿâÿÞÿÜÿÜÿßÿãÿéÿðÿøÿ ÿÿúÿôÿðÿíÿëÿêÿëÿìÿïÿóÿ÷ÿüÿ  ÿÿûÿøÿöÿôÿóÿóÿóÿõÿ÷ÿùÿüÿÿÿ ÿÿýÿûÿúÿùÿùÿùÿúÿúÿûÿýÿþÿÿÿþÿýÿýÿýÿýÿýÿýÿýÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqtractor-1.5.11/src/PaxHeaders/qtractorMidiSysexForm.cpp0000644000000000000000000000013215124701674020340 xustar0030 mtime=1767080892.795263445 30 atime=1767080892.795263445 30 ctime=1767080892.795263445 qtractor-1.5.11/src/qtractorMidiSysexForm.cpp0000644000175000001440000007067015124701674020342 0ustar00rncbcusers// qtractorMidiSysexForm.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorMidiSysexForm.h" #include "qtractorMidiSysex.h" #include "qtractorMidiEngine.h" #include "qtractorSession.h" #include "qtractorMidiFile.h" #include "qtractorOptions.h" #include #include #include #include #include #include //---------------------------------------------------------------------- // class qtractorMidiSysexItem -- custom list view item. // class qtractorMidiSysexItem : public QTreeWidgetItem { public: // Constructors. qtractorMidiSysexItem(qtractorMidiSysex *pSysex) : QTreeWidgetItem(), m_pSysex(pSysex) { update(); } qtractorMidiSysexItem(QTreeWidget *pTreeWidget, QTreeWidgetItem *pItem, qtractorMidiSysex *pSysex) : QTreeWidgetItem(pTreeWidget, pItem), m_pSysex(pSysex) { update(); } // Destructor. ~qtractorMidiSysexItem() { delete m_pSysex; } // Accessors. void setSysex(qtractorMidiSysex *pSysex) { m_pSysex = pSysex; update(); } qtractorMidiSysex *sysex() const { return m_pSysex; } // Updator. void update() { QTreeWidgetItem::setText(0, m_pSysex->name()); QTreeWidgetItem::setText(1, QString::number(m_pSysex->size())); QTreeWidgetItem::setText(2, m_pSysex->text()); } private: // Instance variable. qtractorMidiSysex *m_pSysex; }; //---------------------------------------------------------------------- // class qtractorMidiSysexForm -- instrument file manager form. // // Constructor. qtractorMidiSysexForm::qtractorMidiSysexForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); m_pSysexList = nullptr; m_iDirtyCount = 0; m_iDirtyItem = 0; m_iDirtySysex = 0; m_iUpdateSysex = 0; QHeaderView *pHeader = m_ui.SysexListView->header(); pHeader->setDefaultAlignment(Qt::AlignLeft); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // pHeader->setSectionResizeMode(QHeaderView::Custom); pHeader->setSectionResizeMode(1, QHeaderView::ResizeToContents); pHeader->setSectionsMovable(false); #else // pHeader->setResizeMode(QHeaderView::Custom); pHeader->setResizeMode(1, QHeaderView::ResizeToContents); pHeader->setMovable(false); #endif m_ui.NameComboBox->setInsertPolicy(QComboBox::NoInsert); m_ui.NameComboBox->setCompleter(nullptr); refreshSysex(); refreshForm(); stabilizeForm(); // adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.SysexListView, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), SLOT(stabilizeForm())); QObject::connect(m_ui.ImportButton, SIGNAL(clicked()), SLOT(importSlot())); QObject::connect(m_ui.ExportButton, SIGNAL(clicked()), SLOT(exportSlot())); QObject::connect(m_ui.MoveUpButton, SIGNAL(clicked()), SLOT(moveUpSlot())); QObject::connect(m_ui.MoveDownButton, SIGNAL(clicked()), SLOT(moveDownSlot())); QObject::connect(m_ui.NameComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(nameChanged(const QString&))); QObject::connect(m_ui.NameComboBox, SIGNAL(activated(int)), SLOT(loadSlot(int))); QObject::connect(m_ui.SysexTextEdit, SIGNAL(textChanged()), SLOT(textChanged())); QObject::connect(m_ui.OpenButton, SIGNAL(clicked()), SLOT(openSlot())); QObject::connect(m_ui.SaveButton, SIGNAL(clicked()), SLOT(saveSlot())); QObject::connect(m_ui.DeleteButton, SIGNAL(clicked()), SLOT(deleteSlot())); QObject::connect(m_ui.AddButton, SIGNAL(clicked()), SLOT(addSlot())); QObject::connect(m_ui.UpdateButton, SIGNAL(clicked()), SLOT(updateSlot())); QObject::connect(m_ui.RemoveButton, SIGNAL(clicked()), SLOT(removeSlot())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(clicked(QAbstractButton *)), SLOT(click(QAbstractButton *))); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); } // Destructor. qtractorMidiSysexForm::~qtractorMidiSysexForm (void) { } // SysEx list accessors. void qtractorMidiSysexForm::setSysexList ( qtractorMidiSysexList *pSysexList ) { m_pSysexList = pSysexList; refreshForm(); stabilizeForm(); } qtractorMidiSysexList *qtractorMidiSysexForm::sysexList (void) const { return m_pSysexList; } // Import new SysEx into listing. void qtractorMidiSysexForm::importSlot (void) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; QStringList files; const QString sExt("syx"); const QString& sTitle = tr("Import SysEx Files"); QStringList filters; filters.append(tr("SysEx files (*.%1)").arg(sExt)); filters.append(tr("MIDI files (*.mid *.smf *.midi)")); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to open... files = QFileDialog::getOpenFileNames(pParentWidget, sTitle, pOptions->sMidiSysexDir, sFilter, nullptr, options); #else // Construct open-files dialog... QFileDialog fileDialog(pParentWidget, sTitle, pOptions->sMidiSysexDir, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFiles); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sMidiSysexDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) files = fileDialog.selectedFiles(); #endif if (files.isEmpty()) return; // Tell that we may take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_ui.SysexListView->setUpdatesEnabled(false); // Start inserting in the current selected or last item... QTreeWidgetItem *pItem = m_ui.SysexListView->currentItem(); if (pItem == nullptr) { int iLastItem = m_ui.SysexListView->topLevelItemCount() - 1; if (iLastItem >= 0) pItem = m_ui.SysexListView->topLevelItem(iLastItem); } // For avery selected SysEx file to load... QList items; QStringListIterator ifile(files); while (ifile.hasNext()) { QApplication::processEvents(); // Merge the file contents into global container... const QString& sPath = ifile.next(); if (loadSysexItems(items, sPath)) { pOptions->sMidiSysexDir = QFileInfo(sPath).absolutePath(); ++m_iDirtyCount; } } m_ui.SysexListView->addTopLevelItems(items); // Done waiting. m_ui.SysexListView->setUpdatesEnabled(true); QApplication::restoreOverrideCursor(); stabilizeForm(); } // Export the whole state into a single SysEx file. void qtractorMidiSysexForm::exportSlot (void) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; QString sPath; const QString sExt("syx"); const QString& sTitle = tr("Export SysEx File"); QStringList filters; filters.append(tr("SysEx files (*.%1)").arg(sExt)); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1// QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to open... sPath = QFileDialog::getSaveFileName(pParentWidget, sTitle, pOptions->sMidiSysexDir, sFilter, nullptr, options); #else // Construct open-files dialog... QFileDialog fileDialog(pParentWidget, sTitle, pOptions->sMidiSysexDir, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sMidiSysexDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sPath = fileDialog.selectedFiles().first(); #endif if (sPath.isEmpty() || sPath.at(0) == '.') return; // Enforce .ins extension... if (QFileInfo(sPath).suffix().isEmpty()) { sPath += '.' + sExt; // Check if already exists... if (QFileInfo(sPath).exists()) { if (QMessageBox::warning(this, tr("Warning"), tr("The SysEx file already exists:\n\n" "\"%1\"\n\n" "Do you want to replace it?") .arg(sPath), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } } // Tell that we may take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Just save the whole bunch... QList items; int iItemCount = m_ui.SysexListView->topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) items.append(m_ui.SysexListView->topLevelItem(iItem)); QApplication::processEvents(); if (saveSysexItems(items, sPath)) pOptions->sMidiSysexDir = QFileInfo(sPath).absolutePath(); // Done with export. QApplication::restoreOverrideCursor(); } // Move a SysEx item up on the list. void qtractorMidiSysexForm::moveUpSlot (void) { QTreeWidgetItem *pItem = m_ui.SysexListView->currentItem(); if (pItem) { int iItem = m_ui.SysexListView->indexOfTopLevelItem(pItem); if (iItem > 0) { pItem = m_ui.SysexListView->takeTopLevelItem(iItem); m_ui.SysexListView->insertTopLevelItem(iItem - 1, pItem); m_ui.SysexListView->setCurrentItem(pItem); ++m_iDirtyCount; } } stabilizeForm(); } // Move a SysEx item down on the list. void qtractorMidiSysexForm::moveDownSlot (void) { QTreeWidgetItem *pItem = m_ui.SysexListView->currentItem(); if (pItem) { int iItem = m_ui.SysexListView->indexOfTopLevelItem(pItem); if (iItem < m_ui.SysexListView->topLevelItemCount() - 1) { pItem = m_ui.SysexListView->takeTopLevelItem(iItem); m_ui.SysexListView->insertTopLevelItem(iItem + 1, pItem); m_ui.SysexListView->setCurrentItem(pItem); ++m_iDirtyCount; } } stabilizeForm(); } // Load a SysEx item. void qtractorMidiSysexForm::loadSlot ( int iName ) { if (m_iUpdateSysex > 0) return; const QString& sName = m_ui.NameComboBox->itemText(iName); if (sName.isEmpty()) return; // We'll need this, sure. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; ++m_iUpdateSysex; QSettings& settings = pOptions->settings(); settings.beginGroup(sysexGroup()); loadSysexFile(settings.value(sName).toString()); settings.endGroup(); --m_iUpdateSysex; refreshSysex(); stabilizeForm(); } // Open a SysEx item. void qtractorMidiSysexForm::openSlot (void) { // We'll need this, sure. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // We'll assume that there's an external file... QString sFilename; // Prompt if file does not currently exist... const QString sExt("syx"); const QString& sTitle = tr("Open SysEx"); QStringList filters; filters.append(tr("SysEx files (*.%1)").arg(sExt)); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to save... sFilename = QFileDialog::getOpenFileName(pParentWidget, sTitle, pOptions->sMidiSysexDir, sFilter, nullptr, options); #else // Construct save-file dialog... QFileDialog fileDialog(pParentWidget, sTitle, pOptions->sMidiSysexDir, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFile); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sMidiSysexDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); #endif // Have we a filename to load a preset from? if (!sFilename.isEmpty()) { QFileInfo fi(sFilename); if (fi.exists()) { // Get it loaded alright... ++m_iUpdateSysex; loadSysexFile(sFilename); m_ui.NameComboBox->setEditText(fi.baseName()); pOptions->sMidiSysexDir = fi.absolutePath(); ++m_iDirtyItem; --m_iUpdateSysex; } } refreshSysex(); stabilizeForm(); } // Save a SysEx item. void qtractorMidiSysexForm::saveSlot (void) { const QString& sName = m_ui.NameComboBox->currentText(); if (sName.isEmpty()) return; // We'll need this, sure. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // The current state preset is about to be saved... // this is where we'll make it... QSettings& settings = pOptions->settings(); settings.beginGroup(sysexGroup()); // Sure, we'll have something complex enough // to make it save into an external file... const QString sExt("syx"); QFileInfo fi(QDir(pOptions->sMidiSysexDir), sName + '.' + sExt); QString sFilename = fi.absoluteFilePath(); // Prompt if file does not currently exist... if (!fi.exists()) { const QString& sTitle = tr("Save SysEx"); QStringList filters; filters.append(tr("SysEx files (*.%1)").arg(sExt)); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); qtractorOptions *pOptions = qtractorOptions::getInstance(); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to save... sFilename = QFileDialog::getSaveFileName(pParentWidget, sTitle, sFilename, sFilter, nullptr, options); #else // Construct save-file dialog... QFileDialog fileDialog(pParentWidget, sTitle, sFilename, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sMidiSysexDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); else sFilename.clear(); #endif } else if (pOptions->bConfirmRemove) { if (QMessageBox::warning(parentWidget(), tr("Warning"), tr("About to replace SysEx:\n\n" "\"%1\"\n\n" "Are you sure?") .arg(sName), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) { sFilename.clear(); } } // We've a filename to save the preset if (!sFilename.isEmpty() && sFilename.at(0) != '.') { if (QFileInfo(sFilename).suffix().isEmpty()) sFilename += '.' + sExt; // Get it saved alright... ++m_iUpdateSysex; saveSysexFile(sFilename); settings.setValue(sName, sFilename); --m_iUpdateSysex; pOptions->sMidiSysexDir = QFileInfo(sFilename).absolutePath(); } settings.endGroup(); refreshSysex(); stabilizeForm(); } // Delete a SysEx item. void qtractorMidiSysexForm::deleteSlot (void) { const QString& sName = m_ui.NameComboBox->currentText(); if (sName.isEmpty()) return; // We'll need this, sure. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // A preset entry is about to be removed; // prompt user if he/she's sure about this... if (pOptions->bConfirmRemove) { if (QMessageBox::warning(this, tr("Warning"), tr("About to delete SysEx:\n\n" "\"%1\"\n\n" "Are you sure?") .arg(sName), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } // Go ahead... ++m_iUpdateSysex; QSettings& settings = pOptions->settings(); settings.beginGroup(sysexGroup()); #ifdef QTRACTOR_REMOVE_PRESET_FILES const QString& sFilename = settings.value(sName).toString(); if (QFileInfo(sFilename).exists()) QFile(sFilename).remove(); #endif settings.remove(sName); settings.endGroup(); --m_iUpdateSysex; refreshSysex(); stabilizeForm(); } // Add a SysEx item to list. void qtractorMidiSysexForm::addSlot (void) { // Start inserting in the current selected or last item... QTreeWidgetItem *pItem = m_ui.SysexListView->currentItem(); if (pItem == nullptr) { int iLastItem = m_ui.SysexListView->topLevelItemCount() - 1; if (iLastItem >= 0) pItem = m_ui.SysexListView->topLevelItem(iLastItem); } // Add item... pItem = new qtractorMidiSysexItem( m_ui.SysexListView, pItem, new qtractorMidiSysex( m_ui.NameComboBox->currentText(), m_ui.SysexTextEdit->toPlainText()) ); m_ui.SysexListView->setCurrentItem(pItem); ++m_iDirtyCount; m_iDirtyItem = 0; stabilizeForm(); } // Update a SysEx item from list. void qtractorMidiSysexForm::updateSlot (void) { QTreeWidgetItem *pItem = m_ui.SysexListView->currentItem(); if (pItem == nullptr) return; // Update item... qtractorMidiSysexItem *pSysexItem = static_cast (pItem); qtractorMidiSysex *pSysex = pSysexItem->sysex(); pSysex->setName(m_ui.NameComboBox->currentText()); pSysex->setText(m_ui.SysexTextEdit->toPlainText()); pSysexItem->update(); m_ui.SysexListView->setCurrentItem(pItem); ++m_iDirtyCount; m_iDirtyItem = 0; stabilizeForm(); } // Remove a SysEx item from list. void qtractorMidiSysexForm::removeSlot (void) { QTreeWidgetItem *pItem = m_ui.SysexListView->currentItem(); if (pItem) { delete pItem; ++m_iDirtyCount; m_iDirtyItem = 0; } stabilizeForm(); } // Clear all SysEx items from list. void qtractorMidiSysexForm::clearSlot (void) { ++m_iUpdateSysex; m_ui.SysexListView->clear(); m_ui.NameComboBox->lineEdit()->clear(); m_ui.SysexTextEdit->clear(); --m_iUpdateSysex; ++m_iDirtyCount; m_iDirtyItem = 0; stabilizeForm(); } // SysEx item name change. void qtractorMidiSysexForm::nameChanged ( const QString& sName ) { if (m_iUpdateSysex > 0) return; if (!sName.isEmpty() && m_ui.NameComboBox->findText(sName) >= 0) ++m_iDirtySysex; ++m_iDirtyItem; stabilizeForm(); } void qtractorMidiSysexForm::textChanged (void) { if (m_iUpdateSysex > 0) return; ++m_iDirtySysex; ++m_iDirtyItem; stabilizeForm(); } // Reset settings (action button slot). void qtractorMidiSysexForm::click ( QAbstractButton *pButton ) { QDialogButtonBox::ButtonRole role = m_ui.DialogButtonBox->buttonRole(pButton); if ((role & QDialogButtonBox::ResetRole) == QDialogButtonBox::ResetRole) clearSlot(); } // Accept settings (OK button slot). void qtractorMidiSysexForm::accept (void) { if (m_iDirtyCount == 0) return; if (m_pSysexList == nullptr) return; // Just reload the whole bunch... m_pSysexList->clear(); int iItemCount = m_ui.SysexListView->topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { qtractorMidiSysexItem *pSysexItem = static_cast ( m_ui.SysexListView->topLevelItem(iItem)); if (pSysexItem) { m_pSysexList->append( new qtractorMidiSysex(*(pSysexItem->sysex()))); } } // AG: Reset the controllers so that changes take effect immediately... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession && pSession->midiEngine()) pSession->midiEngine()->resetAllControllers(true); // Just go with dialog acceptance. QDialog::accept(); } // Reject settings (Cancel button slot). void qtractorMidiSysexForm::reject (void) { bool bReject = true; // Check if there's any pending changes... if (m_iDirtyCount > 0) { QMessageBox::StandardButtons buttons = QMessageBox::Discard | QMessageBox::Cancel; if (m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->isEnabled()) buttons |= QMessageBox::Apply; switch (QMessageBox::warning(this, tr("Warning"), tr("SysEx settings have been changed.\n\n" "Do you want to apply the changes?"), buttons)) { case QMessageBox::Apply: accept(); return; case QMessageBox::Discard: break; default: // Cancel. bReject = false; } } if (bReject) QDialog::reject(); } // Stabilize form status. void qtractorMidiSysexForm::stabilizeForm (void) { int iItemCount = m_ui.SysexListView->topLevelItemCount(); m_ui.ExportButton->setEnabled(iItemCount > 0); QTreeWidgetItem *pItem = m_ui.SysexListView->currentItem(); if (pItem) { if (m_iDirtyItem == 0) { ++m_iUpdateSysex; qtractorMidiSysex *pSysex = static_cast (pItem)->sysex(); m_ui.NameComboBox->setEditText(pSysex->name()); m_ui.SysexTextEdit->setText(pSysex->text()); --m_iUpdateSysex; m_iDirtyItem = 0; } int iItem = m_ui.SysexListView->indexOfTopLevelItem(pItem); m_ui.MoveUpButton->setEnabled(iItem > 0); m_ui.MoveDownButton->setEnabled(iItem < iItemCount - 1); m_ui.RemoveButton->setEnabled(true); } else { m_ui.MoveUpButton->setEnabled(false); m_ui.MoveDownButton->setEnabled(false); m_ui.RemoveButton->setEnabled(false); } const QString& sName = m_ui.NameComboBox->currentText(); bool bEnabled = (!sName.isEmpty()); bool bExists = (m_ui.NameComboBox->findText(sName) >= 0); if (bEnabled && m_iDirtyItem > 0) { qtractorMidiSysex sysex(sName, m_ui.SysexTextEdit->toPlainText()); bEnabled = (sysex.size() > 0); } m_ui.SaveButton->setEnabled(bEnabled && (!bExists || m_iDirtySysex > 0)); m_ui.DeleteButton->setEnabled(bEnabled && bExists); m_ui.AddButton->setEnabled(bEnabled); m_ui.UpdateButton->setEnabled(bEnabled && pItem != nullptr); m_ui.DialogButtonBox->button(QDialogButtonBox::Reset)->setEnabled( bEnabled || iItemCount > 0); m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled( m_iDirtyCount > 0 && m_iDirtyItem == 0); } // Refresh all SysEx definition views. void qtractorMidiSysexForm::refreshForm (void) { if (m_pSysexList == nullptr) return; // Freeze... m_ui.SysexListView->setUpdatesEnabled(false); // Files list view... m_ui.SysexListView->clear(); qtractorMidiSysexItem *pItem = nullptr; QListIterator iter(*m_pSysexList); while (iter.hasNext()) { pItem = new qtractorMidiSysexItem( m_ui.SysexListView, pItem, new qtractorMidiSysex(*iter.next()) ); } // Bail out... m_ui.SysexListView->setUpdatesEnabled(true); // Clean. m_iDirtyCount = 0; m_iDirtyItem = 0; } // Refresh SysEx names (presets). void qtractorMidiSysexForm::refreshSysex (void) { if (m_iUpdateSysex > 0) return; ++m_iUpdateSysex; const QString sOldName = m_ui.NameComboBox->currentText(); m_ui.NameComboBox->clear(); qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { pOptions->settings().beginGroup(sysexGroup()); m_ui.NameComboBox->insertItems(0, pOptions->settings().childKeys()); pOptions->settings().endGroup(); } m_ui.NameComboBox->setEditText(sOldName); m_iDirtySysex = 0; --m_iUpdateSysex; } // SysEx file i/o methods. bool qtractorMidiSysexForm::loadSysexItems ( QList& items, const QString& sFilename ) { int iSysex = 0; QFileInfo info(sFilename); // Try on SMF files first... qtractorMidiFile midifile; if (midifile.open(sFilename)) { unsigned short iTracks = midifile.tracks(); for (unsigned int iTrack = 0; iTrack < iTracks; ++iTrack) { qtractorMidiSequence seq; if (midifile.readTrack(&seq, iTrack)) { qtractorMidiEvent *pEvent = seq.events().first(); while (pEvent) { if (pEvent->type() == qtractorMidiEvent::SYSEX) { items.append(new qtractorMidiSysexItem( new qtractorMidiSysex( info.baseName() + '-' + QString::number(iTrack) + '.' + QString::number(++iSysex), pEvent->sysex(), pEvent->sysex_len()))); } pEvent = pEvent->next(); } } } midifile.close(); return (iSysex > 0); } // Should be a SysEx file then... QFile file(sFilename); if (!file.open(QIODevice::ReadOnly)) return false; unsigned short iBuff = 0; unsigned char *pBuff = nullptr; unsigned short i = 0; // Read the file.... while (!file.atEnd()) { // (Re)allocate buffer... if (i >= iBuff) { unsigned char *pTemp = pBuff; iBuff += 1024; pBuff = new unsigned char [iBuff]; if (pTemp) { ::memcpy(pBuff, pTemp, i); delete [] pTemp; } } // Read the next chunk... unsigned short iRead = file.read((char *) pBuff + i, iBuff - i) + i; while (i < iRead) { if (pBuff[i++] == 0xf7) { QString sName = info.baseName(); if (++iSysex > 1 || i < iRead) { sName += '-'; sName += QString::number(iSysex); } items.append(new qtractorMidiSysexItem( new qtractorMidiSysex(sName, pBuff, i))); if (i < iRead) { ::memmove(pBuff, pBuff + i, iRead -= i); i = 0; } } } } // Cleanup... if (pBuff) delete [] pBuff; file.close(); return (iSysex > 0); } bool qtractorMidiSysexForm::saveSysexItems ( const QList& items, const QString& sFilename ) const { QFile file(sFilename); if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) return false; QListIterator iter(items); while (iter.hasNext()) { qtractorMidiSysexItem *pSysexItem = static_cast (iter.next()); if (pSysexItem) { qtractorMidiSysex *pSysex = pSysexItem->sysex(); file.write((const char *) pSysex->data(), pSysex->size()); } } file.close(); return true; } // Preset file handlers. void qtractorMidiSysexForm::loadSysexFile ( const QString& sFilename ) { // Open the source file... QFile file(sFilename); if (!file.open(QIODevice::ReadOnly)) return; // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); bool bResult = false; unsigned short iBuff = 0; unsigned char *pBuff = nullptr; unsigned short i = 0; // Read the file.... while (!file.atEnd()) { // (Re)allocate buffer... if (i >= iBuff) { unsigned char *pTemp = pBuff; iBuff += 1024; pBuff = new unsigned char [iBuff]; if (pTemp) { ::memcpy(pBuff, pTemp, i); delete [] pTemp; } } // Read the next chunk... unsigned short iRead = file.read((char *) pBuff + i, iBuff - i) + i; while (i < iRead) { if (pBuff[i++] == 0xf7) { QFileInfo info(sFilename); qtractorMidiSysex sysex(info.baseName(), pBuff, i); m_ui.NameComboBox->setEditText(sysex.name()); m_ui.SysexTextEdit->setText(sysex.text()); bResult = true; break; } } } // Cleanup... if (pBuff) delete [] pBuff; file.close(); // We're formerly done. QApplication::restoreOverrideCursor(); // Any late warning? if (!bResult) { // Failure (maybe wrong preset)... QMessageBox::critical(this, tr("Error"), tr("SysEx could not be loaded:\n\n" "\"%1\".\n\n" "Sorry.").arg(sFilename), QMessageBox::Cancel); } } void qtractorMidiSysexForm::saveSysexFile ( const QString& sFilename ) { // Open the target file... QFile file(sFilename); if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) return; // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Parse the data... qtractorMidiSysex sysex( m_ui.NameComboBox->currentText(), m_ui.SysexTextEdit->toPlainText()); file.write((const char *) sysex.data(), sysex.size()); file.close(); // We're formerly done. QApplication::restoreOverrideCursor(); } // Preset group path name. QString qtractorMidiSysexForm::sysexGroup (void) { return "/Midi/Sysex/"; } // end of qtractorMidiSysexForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorPluginSelectForm.ui0000644000000000000000000000013215124701674020653 xustar0030 mtime=1767080892.799263428 30 atime=1767080892.799263428 30 ctime=1767080892.799263428 qtractor-1.5.11/src/qtractorPluginSelectForm.ui0000644000175000001440000001735215124701674020653 0ustar00rncbcusers rncbc aka Rui Nuno Capela qtractor - An Audio/MIDI multi-track sequencer. Copyright (C) 2005-2025, 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. qtractorPluginSelectForm 0 0 480 320 Plugins true 4 4 0 4 22 22 22 22 Qt::TabFocus Reset filter X 320 0 Plugin search string (regular expression) true 80 22 Plugin type false 420 0 Available plugins true QAbstractItemView::ExtendedSelection false true false true true Name Audio MIDI Control Modes Path Index Instances Type 0 4 Rescan for available plugins (refresh) &Rescan Qt::Horizontal QSizePolicy::Minimum 20 20 1 0 240 0 Plugin scanning in progress... Qt::Horizontal QSizePolicy::Minimum 20 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok PluginResetToolButton PluginSearchComboBox PluginTypeComboBox PluginListView PluginRescanPushButton DialogButtonBox qtractor-1.5.11/src/PaxHeaders/qtractorMidiControlTypeGroup.h0000644000000000000000000000013215124701674021345 xustar0030 mtime=1767080892.790263466 30 atime=1767080892.790263466 30 ctime=1767080892.790263466 qtractor-1.5.11/src/qtractorMidiControlTypeGroup.h0000644000175000001440000000522615124701674021342 0ustar00rncbcusers// qtractorMidiControlTypeGroup.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorMidiControlTypeGroup_h #define __qtractorMidiControlTypeGroup_h #include "qtractorMidiControl.h" #include "qtractorMidiEditor.h" // Forwrad decls. class QComboBox; class QLabel; //---------------------------------------------------------------------------- // qtractorMidiControlTypeGroup - MIDI control type/param widget group. class qtractorMidiControlTypeGroup : public QObject { Q_OBJECT public: // Constructor. qtractorMidiControlTypeGroup( qtractorMidiEditor *pMidiEditor, QComboBox *pControlTypeComboBox, QComboBox *pControlParamComboBox, QLabel *pControlParamTextLabel = nullptr); // Accessors. void setControlType(qtractorMidiControl::ControlType ctype); qtractorMidiControl::ControlType controlType() const; qtractorMidiControl::ControlType controlTypeFromIndex(int iIndex) const; void setControlParam(unsigned short iParam); unsigned short controlParam() const; unsigned short controlParamFromIndex(int iIndex) const; // Stabilizers. void updateControlType(int iControlType = -1); signals: void controlTypeChanged(int); void controlParamChanged(int); protected slots: void activateControlType(int); void activateControlParam(int); void editControlParamFinished(); protected: // Find combo-box indexes. int indexFromControlType(qtractorMidiControl::ControlType ctype) const; int indexFromControlParam(unsigned short iParam) const; private: // Instance member variables. qtractorMidiEditor *m_pMidiEditor; QComboBox *m_pControlTypeComboBox; QComboBox *m_pControlParamComboBox; QLabel *m_pControlParamTextLabel; unsigned int m_iControlParamUpdate; }; #endif // __qtractorMidiControlTypeGroup_h // end of qtractorMidiControlTypeGroup.h qtractor-1.5.11/src/PaxHeaders/qtractorCommand.cpp0000644000000000000000000000012415124701674017155 xustar0028 mtime=1767080892.7822635 28 atime=1767080892.7822635 28 ctime=1767080892.7822635 qtractor-1.5.11/src/qtractorCommand.cpp0000644000175000001440000001110515124701674017142 0ustar00rncbcusers// qtractorCommand.cpp // /**************************************************************************** Copyright (C) 2005-2022, 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 "qtractorCommand.h" #include #include //---------------------------------------------------------------------- // class qtractorCommandList - declaration. // // Constructor. qtractorCommandList::qtractorCommandList (void) { m_pLastCommand = nullptr; m_commands.setAutoDelete(true); } // Destructor. qtractorCommandList::~qtractorCommandList (void) { clear(); } // Command stack cleaner. void qtractorCommandList::clear (void) { m_commands.clear(); m_pLastCommand = nullptr; } // Command chain accessors. qtractorCommand *qtractorCommandList::lastCommand (void) const { return m_pLastCommand; } qtractorCommand *qtractorCommandList::nextCommand (void) const { if (m_pLastCommand) { return m_pLastCommand->next(); } else { return m_commands.first(); } } // Remove last command from command chain. void qtractorCommandList::removeLastCommand (void) { if (m_pLastCommand) { qtractorCommand *pPrevCommand = m_pLastCommand->prev(); m_commands.remove(m_pLastCommand); m_pLastCommand = pPrevCommand; } } // Special backout method. void qtractorCommandList::backout ( qtractorCommand *pCommand ) { int iUpdate = 0; unsigned int flags = qtractorCommand::None; while (m_pLastCommand && m_pLastCommand != pCommand) { flags |= m_pLastCommand->flags(); m_pLastCommand->undo(); removeLastCommand(); ++iUpdate; } if (iUpdate > 0) emit updateNotifySignal(flags); } // Cannonical command methods. bool qtractorCommandList::push ( qtractorCommand *pCommand ) { // Trim the command list from current last command... qtractorCommand *pNextCommand = nextCommand(); while (pNextCommand) { qtractorCommand *pLateCommand = pNextCommand->next(); m_commands.remove(pNextCommand); pNextCommand = pLateCommand; } if (pCommand == nullptr) return false; // It must be this last one... m_commands.append(pCommand); m_pLastCommand = m_commands.last(); return (m_pLastCommand != nullptr); } bool qtractorCommandList::exec ( qtractorCommand *pCommand ) { bool bResult = false; // Append command... if (push(pCommand)) { // Execute operation... bResult = m_pLastCommand->redo(); // Notify commanders... emit updateNotifySignal(m_pLastCommand->flags()); } return bResult; } bool qtractorCommandList::undo (void) { bool bResult = false; if (m_pLastCommand) { // Undo operation... bResult = m_pLastCommand->undo(); // Backward one command... const unsigned int flags = m_pLastCommand->flags(); m_pLastCommand = m_pLastCommand->prev(); // Notify commanders... emit updateNotifySignal(flags); } return bResult; } bool qtractorCommandList::redo (void) { bool bResult = false; // Forward one command... m_pLastCommand = nextCommand(); if (m_pLastCommand) { // Redo operation... bResult = m_pLastCommand->redo(); // Notify commanders... emit updateNotifySignal(m_pLastCommand->flags()); } return bResult; } // Command action update helper. void qtractorCommandList::updateAction ( QAction *pAction, qtractorCommand *pCommand ) const { const QRegularExpression rxBrackets("[\\s]+\\([^\\)]+\\)$"); pAction->setText(pAction->text().remove(rxBrackets)); pAction->setStatusTip(pAction->statusTip().remove(rxBrackets)); pAction->setToolTip(pAction->toolTip().remove(rxBrackets)); if (pCommand) { const QString sCommand = QString(pCommand->name()).remove(rxBrackets); const QString sBrackets = QString(" (%1)").arg(sCommand); pAction->setText(pAction->text() + sBrackets); pAction->setStatusTip(pAction->statusTip() + sBrackets); pAction->setToolTip(pAction->toolTip() + sBrackets); } pAction->setEnabled(pCommand != nullptr); } // end of qtractorCommand.cpp qtractor-1.5.11/src/PaxHeaders/qtractorBusForm.cpp0000644000000000000000000000013215124701674017153 xustar0030 mtime=1767080892.780263508 30 atime=1767080892.780263508 30 ctime=1767080892.780263508 qtractor-1.5.11/src/qtractorBusForm.cpp0000644000175000001440000006751615124701674017162 0ustar00rncbcusers// qtractorBusForm.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorBusForm.h" #include "qtractorAbout.h" #include "qtractorOptions.h" #include "qtractorEngineCommand.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorMidiManager.h" #include "qtractorSession.h" #include "qtractorMidiSysexForm.h" #include "qtractorMidiSysex.h" #include "qtractorInstrument.h" #include "qtractorPlugin.h" #include #include #include //---------------------------------------------------------------------- // class qtractorBusListItem -- Custom bus listview item. // class qtractorBusListItem : public QTreeWidgetItem { public: // Constructor. qtractorBusListItem(qtractorBus *pBus) : QTreeWidgetItem(), m_pBus(pBus) { QTreeWidgetItem::setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); switch (m_pBus->busType()) { case qtractorTrack::Audio: QTreeWidgetItem::setIcon(0, QIcon::fromTheme("trackAudio")); QTreeWidgetItem::setText(1, QString::number( static_cast (m_pBus)->channels())); break; case qtractorTrack::Midi: QTreeWidgetItem::setIcon(0, QIcon::fromTheme("trackMidi")); QTreeWidgetItem::setText(1, QString::number(16)); break; case qtractorTrack::None: default: break; } QTreeWidgetItem::setText(0, m_pBus->busName()); switch (m_pBus->busMode()) { case qtractorBus::Duplex: QTreeWidgetItem::setText(2, QObject::tr("Duplex")); break; case qtractorBus::Output: QTreeWidgetItem::setText(2, QObject::tr("Output")); break; case qtractorBus::Input: QTreeWidgetItem::setText(2, QObject::tr("Input")); break; case qtractorBus::None: default: QTreeWidgetItem::setText(2, QObject::tr("None")); break; } } // Bus accessors. qtractorBus *bus() const { return m_pBus; } private: // Instance variables. qtractorBus *m_pBus; }; //---------------------------------------------------------------------------- // qtractorBusForm -- UI wrapper form. // Constructor. qtractorBusForm::qtractorBusForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); // Initialize locals. m_pBus = nullptr; m_pAudioRoot = nullptr; m_pMidiRoot = nullptr; m_iDirtySetup = 0; m_iDirtyCount = 0; m_iDirtyTotal = 0; QHeaderView *pHeader = m_ui.BusListView->header(); pHeader->setDefaultAlignment(Qt::AlignLeft); pHeader->resizeSection(0, 140); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // pHeader->setSectionResizeMode(QHeaderView::Custom); pHeader->setSectionResizeMode(1, QHeaderView::ResizeToContents); pHeader->setSectionResizeMode(2, QHeaderView::ResizeToContents); pHeader->setSectionsMovable(false); #else // pHeader->setResizeMode(QHeaderView::Custom); pHeader->setResizeMode(1, QHeaderView::ResizeToContents); pHeader->setResizeMode(2, QHeaderView::ResizeToContents); pHeader->setMovable(false); #endif m_ui.BusListView->setContextMenuPolicy(Qt::CustomContextMenu); const QColor& rgbDark = palette().dark().color().darker(150); m_ui.BusTitleTextLabel->setPalette(QPalette(rgbDark)); m_ui.BusTitleTextLabel->setAutoFillBackground(true); // (Re)initial contents. refreshBuses(); // Try to restore normal window positioning. adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.BusListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(selectBus())); QObject::connect(m_ui.BusListView, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(contextMenu(const QPoint&))); QObject::connect(m_ui.BusNameLineEdit, SIGNAL(textChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.BusModeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MonitorCheckBox, SIGNAL(clicked()), SLOT(changed())); QObject::connect(m_ui.AudioChannelsSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.AudioAutoConnectCheckBox, SIGNAL(clicked()), SLOT(changed())); QObject::connect(m_ui.MidiInstrumentComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.MidiSysexPushButton, SIGNAL(clicked()), SLOT(midiSysex())); QObject::connect(m_ui.InputPluginListView, SIGNAL(currentRowChanged(int)), SLOT(stabilizeForm())); QObject::connect(m_ui.InputPluginListView, SIGNAL(contentsChanged()), SLOT(stabilizeForm())); QObject::connect(m_ui.AddInputPluginToolButton, SIGNAL(clicked()), SLOT(addInputPlugin())); QObject::connect(m_ui.RemoveInputPluginToolButton, SIGNAL(clicked()), SLOT(removeInputPlugin())); QObject::connect(m_ui.MoveUpInputPluginToolButton, SIGNAL(clicked()), SLOT(moveUpInputPlugin())); QObject::connect(m_ui.MoveDownInputPluginToolButton, SIGNAL(clicked()), SLOT(moveDownInputPlugin())); QObject::connect(m_ui.OutputPluginListView, SIGNAL(currentRowChanged(int)), SLOT(stabilizeForm())); QObject::connect(m_ui.OutputPluginListView, SIGNAL(contentsChanged()), SLOT(stabilizeForm())); QObject::connect(m_ui.AddOutputPluginToolButton, SIGNAL(clicked()), SLOT(addOutputPlugin())); QObject::connect(m_ui.RemoveOutputPluginToolButton, SIGNAL(clicked()), SLOT(removeOutputPlugin())); QObject::connect(m_ui.MoveUpOutputPluginToolButton, SIGNAL(clicked()), SLOT(moveUpOutputPlugin())); QObject::connect(m_ui.MoveDownOutputPluginToolButton, SIGNAL(clicked()), SLOT(moveDownOutputPlugin())); QObject::connect(m_ui.MoveUpPushButton, SIGNAL(clicked()), SLOT(moveUpBus())); QObject::connect(m_ui.MoveDownPushButton, SIGNAL(clicked()), SLOT(moveDownBus())); QObject::connect(m_ui.CreatePushButton, SIGNAL(clicked()), SLOT(createBus())); QObject::connect(m_ui.UpdatePushButton, SIGNAL(clicked()), SLOT(updateBus())); QObject::connect(m_ui.DeletePushButton, SIGNAL(clicked()), SLOT(deleteBus())); QObject::connect(m_ui.ClosePushButton, SIGNAL(clicked()), SLOT(reject())); stabilizeForm(); } // Set current bus. void qtractorBusForm::setBus ( qtractorBus *pBus ) { if (pBus == nullptr) return; // Get the device view root item... QTreeWidgetItem *pRootItem = nullptr; if (pBus) { switch (pBus->busType()) { case qtractorTrack::Audio: pRootItem = m_pAudioRoot; break; case qtractorTrack::Midi: pRootItem = m_pMidiRoot; break; default: break; } } // Is the root present? if (pRootItem == nullptr) { stabilizeForm(); return; } // For each child, test for identity... const int iChildCount = pRootItem->childCount(); for (int i = 0; i < iChildCount; ++i) { QTreeWidgetItem *pItem = pRootItem->child(i); // If identities match, select as current device item. qtractorBusListItem *pBusItem = static_cast (pItem); if (pBusItem && pBusItem->bus() == pBus) { m_ui.BusListView->setCurrentItem(pItem); break; } } } // Current bus accessor. qtractorBus *qtractorBusForm::bus (void) { return m_pBus; } // Current bus accessor. bool qtractorBusForm::isDirty (void) { return (m_iDirtyTotal > 0); } // Show current selected bus. void qtractorBusForm::showBus ( qtractorBus *pBus ) { ++m_iDirtySetup; // Reset plugin lists... resetPluginLists(); // Settle current bus reference... m_pBus = pBus; // Update some dependable specifics... updateMidiInstruments(); updateMidiSysex(); // Show bus properties into view pane... if (pBus) { QString sBusTitle = pBus->busName(); if (!sBusTitle.isEmpty()) sBusTitle += " - "; switch (pBus->busType()) { case qtractorTrack::Audio: { sBusTitle += tr("Audio"); qtractorAudioBus *pAudioBus = static_cast (pBus); if (pAudioBus) { // Audio bus specifics... m_ui.AudioChannelsSpinBox->setValue( pAudioBus->channels()); m_ui.AudioAutoConnectCheckBox->setChecked( pAudioBus->isAutoConnect()); // Set plugin lists... if (pAudioBus->busMode() & qtractorBus::Input) m_ui.InputPluginListView->setPluginList( pAudioBus->pluginList_in()); if (pAudioBus->busMode() & qtractorBus::Output) m_ui.OutputPluginListView->setPluginList( pAudioBus->pluginList_out()); } break; } case qtractorTrack::Midi: { sBusTitle += tr("MIDI"); qtractorMidiBus *pMidiBus = static_cast (pBus); if (pMidiBus) { // MIDI bus specifics... const int iInstrumentIndex = m_ui.MidiInstrumentComboBox->findText( pMidiBus->instrumentName()); m_ui.MidiInstrumentComboBox->setCurrentIndex( iInstrumentIndex > 0 ? iInstrumentIndex : 0); // Set plugin lists... if (pMidiBus->busMode() & qtractorBus::Input) m_ui.InputPluginListView->setPluginList( pMidiBus->pluginList_in()); if (pMidiBus->busMode() & qtractorBus::Output) m_ui.OutputPluginListView->setPluginList( pMidiBus->pluginList_out()); } break; } case qtractorTrack::None: default: break; } if (!sBusTitle.isEmpty()) sBusTitle += ' '; m_ui.BusTitleTextLabel->setText(sBusTitle + tr("Bus")); m_ui.BusNameLineEdit->setText(pBus->busName()); m_ui.BusModeComboBox->setCurrentIndex(int(pBus->busMode()) - 1); m_ui.MonitorCheckBox->setChecked(pBus->isMonitor()); } // Reset dirty flag... m_iDirtyCount = 0; --m_iDirtySetup; // Done. stabilizeForm(); } // Refresh all buses list and views. void qtractorBusForm::refreshBuses (void) { // // (Re)Load complete bus listing ... // m_pAudioRoot = nullptr; m_pMidiRoot = nullptr; m_ui.BusListView->clear(); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Audio buses... qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) { m_pAudioRoot = new QTreeWidgetItem(); m_pAudioRoot->setText(0, ' ' + tr("Audio")); m_pAudioRoot->setFlags(Qt::ItemIsEnabled); // but not selectable... QListIterator iter(pAudioEngine->buses2()); while (iter.hasNext()) m_pAudioRoot->addChild(new qtractorBusListItem(iter.next())); m_ui.BusListView->addTopLevelItem(m_pAudioRoot); m_pAudioRoot->setExpanded(true); } // MIDI buses... qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine) { m_pMidiRoot = new QTreeWidgetItem(); m_pMidiRoot->setText(0, ' ' + tr("MIDI")); m_pMidiRoot->setFlags(Qt::ItemIsEnabled); // but not selectable... QListIterator iter(pMidiEngine->buses2()); while (iter.hasNext()) m_pMidiRoot->addChild(new qtractorBusListItem(iter.next())); m_ui.BusListView->addTopLevelItem(m_pMidiRoot); m_pMidiRoot->setExpanded(true); } } // Bus selection slot. void qtractorBusForm::selectBus (void) { if (m_iDirtySetup > 0) return; // Get current selected item, must not be a root one... QTreeWidgetItem *pItem = m_ui.BusListView->currentItem(); if (pItem == nullptr) return; if (pItem->parent() == nullptr) return; // Just make it in current view... qtractorBusListItem *pBusItem = static_cast (pItem); if (pBusItem == nullptr) return; // Check if we need an update?... bool bUpdate = false; qtractorBus *pBus = pBusItem->bus(); if (m_pBus && m_pBus != pBus && m_iDirtyCount > 0) { QMessageBox::StandardButtons buttons = QMessageBox::Discard | QMessageBox::Cancel; if (m_ui.UpdatePushButton->isEnabled()) buttons |= QMessageBox::Apply; switch (QMessageBox::warning(this, tr("Warning"), tr("Some settings have been changed.\n\n" "Do you want to apply the changes?"), buttons)) { case QMessageBox::Apply: bUpdate = updateBus(m_pBus); // Fall thru... case QMessageBox::Discard: break; default: // Cancel. return; } } // Get new one into view... showBus(pBus); // Reselect as current (only on apply/update) if (bUpdate) { ++m_iDirtyTotal; refreshBuses(); setBus(m_pBus); } } // Check whether the current view is elligible for action. unsigned int qtractorBusForm::flags (void) const { unsigned int iFlags = 0; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return iFlags; if (m_pBus == nullptr) return iFlags; qtractorEngine *pEngine = m_pBus->engine(); if (pEngine == nullptr) return iFlags; const int iBus2 = pEngine->buses2().indexOf(m_pBus); if (iBus2 < 0) return iFlags; const int iNumBuses2 = pEngine->buses2().count(); if (iBus2 > 0) { iFlags |= Delete; if (iBus2 > 1) iFlags |= MoveUp; if (iBus2 < iNumBuses2 - 1) iFlags |= MoveDown; } if (m_iDirtyCount == 0) return iFlags; const QString sBusName = m_ui.BusNameLineEdit->text().simplified(); if (sBusName.isEmpty()) return iFlags; // Is there one already? qtractorBus *pBus = pEngine->findBus(sBusName); qtractorBus *pBusEx = pEngine->findBusEx(sBusName); if (pBus == nullptr && pBusEx == nullptr) iFlags |= Create; if ((pBus == nullptr || pBus == m_pBus) && (pBusEx == nullptr) && (iBus2 > 0 || m_ui.BusModeComboBox->currentIndex() == 2)) iFlags |= Update; return iFlags; } // Update bus method. bool qtractorBusForm::updateBus ( qtractorBus *pBus ) { if (pBus == nullptr) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; const QString sBusName = m_ui.BusNameLineEdit->text().simplified(); if (sBusName.isEmpty()) return false; // Reset plugin lists... resetPluginLists(); const qtractorBus::BusMode busMode = qtractorBus::BusMode(m_ui.BusModeComboBox->currentIndex() + 1); // Make it as an unduable command... qtractorUpdateBusCommand *pUpdateBusCommand = new qtractorUpdateBusCommand(pBus); // Set all updated properties... qtractorTrack::TrackType busType = pBus->busType(); pUpdateBusCommand->setBusType(busType); pUpdateBusCommand->setBusName(sBusName); pUpdateBusCommand->setBusMode(busMode); pUpdateBusCommand->setMonitor( ((busMode & qtractorBus::Duplex) == qtractorBus::Duplex) && m_ui.MonitorCheckBox->isChecked()); // Specialties for bus types... switch (busType) { case qtractorTrack::Audio: pUpdateBusCommand->setChannels( m_ui.AudioChannelsSpinBox->value()); pUpdateBusCommand->setAutoConnect( m_ui.AudioAutoConnectCheckBox->isChecked()); break; case qtractorTrack::Midi: pUpdateBusCommand->setInstrumentName( m_ui.MidiInstrumentComboBox->currentIndex() > 0 ? m_ui.MidiInstrumentComboBox->currentText() : QString()); // Fall thru... case qtractorTrack::None: default: break; } // Execute and refresh form... return pSession->execute(pUpdateBusCommand); } // Move current bus up towards the list top. void qtractorBusForm::moveUpBus (void) { if (m_pBus == nullptr) return; // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession && pSession->execute(new qtractorMoveBusCommand(m_pBus, -1))) { ++m_iDirtyTotal; refreshBuses(); } // Reselect current bus... setBus(m_pBus); } // Move current bus down towards the list bottom. void qtractorBusForm::moveDownBus (void) { if (m_pBus == nullptr) return; // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession && pSession->execute(new qtractorMoveBusCommand(m_pBus, +1))) { ++m_iDirtyTotal; refreshBuses(); } // Reselect current bus... setBus(m_pBus); } // Create a new bus from current view. void qtractorBusForm::createBus (void) { if (m_pBus == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const QString sBusName = m_ui.BusNameLineEdit->text().simplified(); if (sBusName.isEmpty()) return; const qtractorBus::BusMode busMode = qtractorBus::BusMode(m_ui.BusModeComboBox->currentIndex() + 1); // Make it as an unduable command... qtractorCreateBusCommand *pCreateBusCommand = new qtractorCreateBusCommand(m_pBus); // Set all creational properties... qtractorTrack::TrackType busType = m_pBus->busType(); pCreateBusCommand->setBusType(busType); pCreateBusCommand->setBusName(sBusName); pCreateBusCommand->setBusMode(busMode); pCreateBusCommand->setMonitor( (busMode & qtractorBus::Duplex) == qtractorBus::Duplex && m_ui.MonitorCheckBox->isChecked()); // Specialties for bus types... switch (busType) { case qtractorTrack::Audio: pCreateBusCommand->setChannels( m_ui.AudioChannelsSpinBox->value()); pCreateBusCommand->setAutoConnect( m_ui.AudioAutoConnectCheckBox->isChecked()); break; case qtractorTrack::Midi: pCreateBusCommand->setInstrumentName( m_ui.MidiInstrumentComboBox->currentIndex() > 0 ? m_ui.MidiInstrumentComboBox->currentText() : QString()); // Fall thru... case qtractorTrack::None: default: break; } // Execute and refresh form... if (pSession->execute(pCreateBusCommand)) { ++m_iDirtyTotal; refreshBuses(); } // Get new one into view, // usually created after the cuurent one... showBus(m_pBus->next()); // Select the new bus... setBus(m_pBus); } // Update current bus in view. void qtractorBusForm::updateBus (void) { // That's it... if (updateBus(m_pBus)) { ++m_iDirtyTotal; refreshBuses(); } // Reselect current bus... setBus(m_pBus); } // Delete current bus in view. void qtractorBusForm::deleteBus (void) { if (m_pBus == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Prompt user if he/she's sure about this... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bConfirmRemove) { // Get some textual type... QString sBusType; switch (m_pBus->busType()) { case qtractorTrack::Audio: sBusType = tr("Audio"); break; case qtractorTrack::Midi: sBusType = tr("MIDI"); break; default: break; } // Show the warning... if (QMessageBox::warning(this, tr("Warning"), tr("About to remove bus:\n\n" "\"%1\" (%2)\n\n" "Are you sure?") .arg(m_pBus->busName()) .arg(sBusType), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } // Reset plugin lists... resetPluginLists(); // Make it as an unduable command... qtractorDeleteBusCommand *pDeleteBusCommand = new qtractorDeleteBusCommand(m_pBus); // Invalidade current bus... m_pBus = nullptr; // Execute and refresh form... if (pSession->execute(pDeleteBusCommand)) { ++m_iDirtyTotal; refreshBuses(); } // Done. stabilizeForm(); } // Reset (stabilize) plugin lists... void qtractorBusForm::resetPluginLists (void) { m_ui.InputPluginListView->setPluginList(nullptr); m_ui.OutputPluginListView->setPluginList(nullptr); } // Make changes due. void qtractorBusForm::changed (void) { if (m_iDirtySetup > 0) return; ++m_iDirtyCount; stabilizeForm(); } // Reject settings (Close button slot). void qtractorBusForm::reject (void) { bool bReject = true; // Check if there's any pending changes... if (m_iDirtyCount > 0) { switch (QMessageBox::warning(this, tr("Warning"), tr("Some settings have been changed.\n\n" "Do you want to discard the changes?"), QMessageBox::Discard | QMessageBox::Cancel)) { case QMessageBox::Discard: break; default: // Cancel. bReject = false; break; } } if (bReject) QDialog::reject(); } // Stabilize current form state. void qtractorBusForm::stabilizeForm (void) { const qtractorBus::BusMode busMode = qtractorBus::BusMode(m_ui.BusModeComboBox->currentIndex() + 1); bool bEnabled = (m_pBus != nullptr); m_ui.BusTabWidget->setEnabled(bEnabled); m_ui.CommonBusGroup->setEnabled(bEnabled); if (m_pBus && m_pBus->busType() == qtractorTrack::Audio) { m_ui.AudioBusGroup->setEnabled(true); m_ui.AudioBusGroup->setVisible(true); m_ui.MidiBusGroup->setEnabled(false); m_ui.MidiBusGroup->setVisible(false); } else if (m_pBus && m_pBus->busType() == qtractorTrack::Midi) { m_ui.AudioBusGroup->setEnabled(false); m_ui.AudioBusGroup->setVisible(false); m_ui.MidiBusGroup->setEnabled(true); m_ui.MidiBusGroup->setVisible(true); bEnabled = (busMode & qtractorBus::Output); m_ui.MidiInstrumentComboBox->setEnabled(bEnabled); m_ui.MidiSysexPushButton->setEnabled(bEnabled); m_ui.MidiSysexTextLabel->setEnabled(bEnabled); } else { m_ui.AudioBusGroup->setEnabled(false); m_ui.AudioBusGroup->setVisible(true); m_ui.MidiBusGroup->setEnabled(false); m_ui.MidiBusGroup->setVisible(false); } m_ui.MonitorCheckBox->setEnabled(busMode == qtractorBus::Duplex); const unsigned int iFlags = flags(); m_ui.MoveUpPushButton->setEnabled(iFlags & MoveUp); m_ui.MoveDownPushButton->setEnabled(iFlags & MoveDown); m_ui.CreatePushButton->setEnabled(iFlags & Create); m_ui.UpdatePushButton->setEnabled(iFlags & Update); m_ui.DeletePushButton->setEnabled(iFlags & Delete); // Stabilize current plugin lists state. int iItem, iItemCount; qtractorPlugin *pPlugin = nullptr; qtractorPluginListItem *pItem = nullptr; // Input plugin list... bEnabled = (busMode & qtractorBus::Input) && (m_pBus && (m_pBus->busMode() & qtractorBus::Input)) && (m_ui.InputPluginListView->pluginList() != nullptr); m_ui.BusTabWidget->setTabEnabled(1, bEnabled); if (bEnabled) { iItemCount = m_ui.InputPluginListView->count(); iItem = -1; pPlugin = nullptr; pItem = static_cast ( m_ui.InputPluginListView->currentItem()); if (pItem) { iItem = m_ui.InputPluginListView->row(pItem); pPlugin = pItem->plugin(); } // m_ui.AddInputPluginToolButton->setEnabled(true); m_ui.RemoveInputPluginToolButton->setEnabled(pPlugin != nullptr); m_ui.MoveUpInputPluginToolButton->setEnabled(pItem && iItem > 0); m_ui.MoveDownInputPluginToolButton->setEnabled( pItem && iItem < iItemCount - 1); } // Output plugin list... bEnabled = (busMode & qtractorBus::Output) && (m_pBus && (m_pBus->busMode() & qtractorBus::Output)) && (m_ui.OutputPluginListView->pluginList() != nullptr); m_ui.BusTabWidget->setTabEnabled(2, bEnabled); if (bEnabled) { iItemCount = m_ui.OutputPluginListView->count(); iItem = -1; pPlugin = nullptr; pItem = static_cast ( m_ui.OutputPluginListView->currentItem()); if (pItem) { iItem = m_ui.OutputPluginListView->row(pItem); pPlugin = pItem->plugin(); } // m_ui.AddOutputPluginToolButton->setEnabled(true); m_ui.RemoveOutputPluginToolButton->setEnabled(pPlugin != nullptr); m_ui.MoveUpOutputPluginToolButton->setEnabled(pItem && iItem > 0); m_ui.MoveDownOutputPluginToolButton->setEnabled( pItem && iItem < iItemCount - 1); } } // Bus list view context menu handler. void qtractorBusForm::contextMenu ( const QPoint& /*pos*/ ) { // Build the device context menu... QMenu menu(this); QAction *pAction; const unsigned int iFlags = flags(); pAction = menu.addAction( QIcon::fromTheme("formCreate"), tr("&Create"), this, SLOT(createBus())); pAction->setEnabled(iFlags & Create); pAction = menu.addAction( QIcon::fromTheme("formAccept"), tr("&Update"), this, SLOT(updateBus())); pAction->setEnabled(iFlags & Update); pAction = menu.addAction( QIcon::fromTheme("formRemove"), tr("&Delete"), this, SLOT(deleteBus())); pAction->setEnabled(iFlags & Delete); menu.addSeparator(); pAction = menu.addAction( QIcon::fromTheme("formMoveUp"), tr("Move &Up"), this, SLOT(moveUpBus())); pAction->setEnabled(iFlags & MoveUp); pAction = menu.addAction( QIcon::fromTheme("formMoveDown"), tr("Move &Down"), this, SLOT(moveDownBus())); pAction->setEnabled(iFlags & MoveDown); // menu.exec(m_ui.BusListView->mapToGlobal(pos)); menu.exec(QCursor::pos()); } // Show MIDI SysEx bank manager dialog... void qtractorBusForm::midiSysex (void) { // Care of MIDI output bus... if (m_pBus == nullptr) return; if (m_pBus->busType() != qtractorTrack::Midi) return; if ((m_pBus->busMode() & qtractorBus::Output) == 0) return; qtractorMidiBus *pMidiBus = static_cast (m_pBus); if (pMidiBus == nullptr) return; if (pMidiBus->sysexList() == nullptr) return; qtractorMidiSysexForm form(this); form.setSysexList(pMidiBus->sysexList()); form.exec(); updateMidiSysex(); } // Refresh instrument list. void qtractorBusForm::updateMidiInstruments (void) { m_ui.MidiInstrumentComboBox->clear(); m_ui.MidiInstrumentComboBox->addItem(tr("(No instrument)")); // Care of MIDI output bus... if (m_pBus == nullptr) return; if (m_pBus->busType() != qtractorTrack::Midi) return; if ((m_pBus->busMode() & qtractorBus::Output) == 0) return; qtractorMidiBus *pMidiBus = static_cast (m_pBus); if (pMidiBus == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorInstrumentList *pInstruments = pSession->instruments(); if (pInstruments == nullptr) return; // Avoid superfluous change notifications... ++m_iDirtySetup; const QIcon& icon = QIcon::fromTheme("itemInstrument"); if (pMidiBus->pluginList_out()) { qtractorMidiManager *pMidiManager = (pMidiBus->pluginList_out())->midiManager(); if (pMidiManager) { pMidiManager->updateInstruments(); const qtractorInstrumentList& instruments = pMidiManager->instruments(); qtractorInstrumentList::ConstIterator iter = instruments.constBegin(); const qtractorInstrumentList::ConstIterator& iter_end = instruments.constEnd(); for ( ; iter != iter_end; ++iter) m_ui.MidiInstrumentComboBox->addItem(icon, iter.value().instrumentName()); } } // Regular instrument names... qtractorInstrumentList::ConstIterator iter = pInstruments->constBegin(); const qtractorInstrumentList::ConstIterator& iter_end = pInstruments->constEnd(); for ( ; iter != iter_end; ++iter) m_ui.MidiInstrumentComboBox->addItem(icon, iter.value().instrumentName()); // Done. --m_iDirtySetup; } // Update SysEx status. void qtractorBusForm::updateMidiSysex (void) { m_ui.MidiSysexTextLabel->clear(); // Care of MIDI output bus... if (m_pBus == nullptr) return; if (m_pBus->busType() != qtractorTrack::Midi) return; if ((m_pBus->busMode() & qtractorBus::Output) == 0) return; qtractorMidiBus *pMidiBus = static_cast (m_pBus); if (pMidiBus == nullptr) return; if (pMidiBus->sysexList() == nullptr) return; // Show proper count status... const int iSysexCount = (pMidiBus->sysexList())->count(); switch (iSysexCount) { case 0: m_ui.MidiSysexTextLabel->setText(tr("(none)")); break; case 1: m_ui.MidiSysexTextLabel->setText(tr("(1 item)")); break; default: m_ui.MidiSysexTextLabel->setText(tr("(%1 items)").arg(iSysexCount)); break; } } // Input plugin list slots. void qtractorBusForm::addInputPlugin (void) { m_ui.InputPluginListView->addPlugin(); } void qtractorBusForm::removeInputPlugin (void) { m_ui.InputPluginListView->removePlugin(); } void qtractorBusForm::moveUpInputPlugin (void) { m_ui.InputPluginListView->moveUpPlugin(); } void qtractorBusForm::moveDownInputPlugin (void) { m_ui.InputPluginListView->moveDownPlugin(); } // Output plugin list slots. void qtractorBusForm::addOutputPlugin (void) { m_ui.OutputPluginListView->addPlugin(); } void qtractorBusForm::removeOutputPlugin (void) { m_ui.OutputPluginListView->removePlugin(); } void qtractorBusForm::moveUpOutputPlugin (void) { m_ui.OutputPluginListView->moveUpPlugin(); } void qtractorBusForm::moveDownOutputPlugin (void) { m_ui.OutputPluginListView->moveDownPlugin(); } // end of qtractorBusForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorClapPlugin.h0000644000000000000000000000013215124701674017301 xustar0030 mtime=1767080892.781263504 30 atime=1767080892.781263504 30 ctime=1767080892.781263504 qtractor-1.5.11/src/qtractorClapPlugin.h0000644000175000001440000001471615124701674017302 0ustar00rncbcusers// qtractorClapPlugin.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorClapPlugin_h #define __qtractorClapPlugin_h #include "qtractorPlugin.h" #include #include #include // Forward decls. class qtractorAudioEngine; class QWidget; //---------------------------------------------------------------------- // class qtractorClapPluginType -- CLAP plugin meta-interface decl. // class qtractorClapPluginType : public qtractorPluginType { public: // Constructor. qtractorClapPluginType(qtractorPluginFile *pFile, unsigned long iIndex); // Destructor. ~qtractorClapPluginType(); // Factory method (static) static qtractorClapPluginType *createType ( qtractorPluginFile *pFile, unsigned long iIndex); // Executive methods. bool open(); void close(); // It can be only one... unsigned short instances ( unsigned short iChannels, bool /*bMidi*/) const { return (iChannels > 0 ? 1 : 0); } // Instance cached-deferred accessors. const QString& aboutText() { return m_sAboutText; } int midiDialectIns() const { return m_iMidiDialectIns; } int midiDialectOuts() const { return m_iMidiDialectOuts; } // Forward decls. class Impl; Impl *impl() const { return m_pImpl; } protected: // Instance cached-deferred properties. QString m_sAboutText; private: // Instance variables. Impl *m_pImpl; int m_iMidiDialectIns; int m_iMidiDialectOuts; }; //---------------------------------------------------------------------- // class qtractorClapPlugin -- CLAP plugin instance interface decl. // class qtractorClapPlugin : public qtractorPlugin { public: // Constructor. qtractorClapPlugin(qtractorPluginList *pList, qtractorClapPluginType *pType); // Destructor. ~qtractorClapPlugin(); // Forward decl. class Param; // Channel/instance number accessors. void setChannels(unsigned short iChannels); // Do the actual (de)activation. void activate(); void deactivate(); // Instance parameters (de)initializers. void addParams(); void clearParams(); // Clear a specific parameter. void clearParam(qtractorPlugin::Param *pParam); // Parameter update methods. void updateParam(qtractorPlugin::Param *pParam, float fValue, bool bUpdate); // Parameters update methods. void updateParamValues(bool bUpdate); // Parameter finder (by id). qtractorPlugin::Param *findParamId(int id) const; // Configuration state stuff. void configure(const QString& sKey, const QString& sValue); // Plugin configuration/state snapshot. void freezeConfigs(); void releaseConfigs(); // Provisional note name accessor. bool getNoteName(int iIndex, NoteName& note) const; // Update instrument/note names cache. void updateNoteNames(); // Open/close editor widget. void openEditor(QWidget *pParent = nullptr); void closeEditor(); // Parameters post-update methods. void idleEditor(); // GUI editor visibility state. void setEditorVisible(bool bVisible); bool isEditorVisible() const; // Update editor widget caption. void setEditorTitle(const QString& sTitle); // GUI editor widget handle (if not floating). QWidget *editorWidget() const; // GUI editor created/active state. bool isEditorCreated() const; // Processor stuff... // void process_midi_in(unsigned char *data, unsigned int size, unsigned long offset, unsigned short port); void process(float **ppIBuffer, float **ppOBuffer, unsigned int nframes); // Plugin current latency (in frames); unsigned long latency() const; // Plugin preset i/o (configuration from/to state files). bool loadPresetFile(const QString& sFilename); bool savePresetFile(const QString& sFilename); // Reinitialize the plugin instance. void request_restart(); void restart(); // Idle editor. static void idleEditorAll(); // Common host-time keeper (static) static void updateTime(qtractorAudioEngine *pAudioEngine); // Host cleanup (static). static void clearAll(); // Forward decls. class Impl; Impl *impl() const { return m_pImpl; } protected: // Forward decls. class EditorWidget; // Plugin instance (de)initializers. void initialize(); void deinitialize(); // Clear instrument/note names cache. void clearNoteNames(); // Make up some others dirty... void updateDirtyCount(); private: // Instance variables. qtractorClapPluginType *m_pType; Impl *m_pImpl; // GUI Editor stuff... bool m_bEditorCreated; bool m_bEditorVisible; EditorWidget *m_pEditorWidget; // Audio I/O buffer pointers. float **m_ppIBuffer; float **m_ppOBuffer; // Dummy I/O buffers. float *m_pfIDummy; float *m_pfODummy; // MIDI Event decoder. snd_midi_event_t *m_pMidiParser; // Identififier-parameters map. QHash m_paramIds; QHash m_paramValues; // Note-names cache. QList m_noteNames; }; //---------------------------------------------------------------------------- // qtractorClapPlugin::Param -- CLAP plugin parameter interface decl. // class qtractorClapPlugin::Param : public qtractorPlugin::Param { public: // Constructors. Param(qtractorClapPlugin *pPlugin, unsigned long iIndex); // Destructor. ~Param(); // Port range hints predicate methods. bool isBoundedBelow() const; bool isBoundedAbove() const; bool isDefaultValue() const; bool isLogarithmic() const; bool isSampleRate() const; bool isInteger() const; bool isToggled() const; bool isDisplay() const; // Current display value. QString display() const; // Forward decls. class Impl; Impl *impl() const { return m_pImpl; } private: // Instance variables. Impl *m_pImpl; }; #endif // __qtractorClapPlugin_h // end of qtractorClapPlugin.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiEditList.cpp0000644000000000000000000000013215124701674020122 xustar0030 mtime=1767080892.791263462 30 atime=1767080892.791263462 30 ctime=1767080892.791263462 qtractor-1.5.11/src/qtractorMidiEditList.cpp0000644000175000001440000003550615124701674020123 0ustar00rncbcusers// qtractorMidiEditList.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorMidiEditList.h" #include "qtractorMidiEditor.h" #include "qtractorMidiEditView.h" #include #include #include #include #include #include #ifdef CONFIG_GRADIENT #include #endif //---------------------------------------------------------------------------- // qtractorMidiEditList -- MIDI sequence key scale widget. // Constructor. qtractorMidiEditList::qtractorMidiEditList ( qtractorMidiEditor *pEditor, QWidget *pParent ) : qtractorScrollView(pParent) { m_pEditor = pEditor; m_iItemHeight = ItemHeightBase; m_dragState = DragNone; m_iNoteOn = -1; m_iNoteVel = -1; qtractorScrollView::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); qtractorScrollView::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); qtractorScrollView::setFocusPolicy(Qt::NoFocus); // qtractorScrollView::viewport()->setFocusPolicy(Qt::ClickFocus); // qtractorScrollView::viewport()->setFocusProxy(this); // qtractorScrollView::viewport()->setAcceptDrops(true); // qtractorScrollView::setDragAutoScroll(false); qtractorScrollView::setMouseTracking(true); const QFont& font = qtractorScrollView::font(); qtractorScrollView::setFont(QFont(font.family(), font.pointSize() - 3)); // QObject::connect(this, SIGNAL(contentsMoving(int,int)), // this, SLOT(updatePixmap(int,int))); // Trap for help/tool-tips and leave events. qtractorScrollView::viewport()->installEventFilter(this); } // Destructor. qtractorMidiEditList::~qtractorMidiEditList (void) { } // Item height methods. void qtractorMidiEditList::setItemHeight ( unsigned short iItemHeight ) { if (iItemHeight > ItemHeightMax) iItemHeight = ItemHeightMax; else if (iItemHeight < ItemHeightMin) iItemHeight = ItemHeightMin; if (iItemHeight == m_iItemHeight) return; m_iItemHeight = iItemHeight; updateContentsHeight(); } unsigned short qtractorMidiEditList::itemHeight (void) const { return m_iItemHeight; } // Update key-list content height. void qtractorMidiEditList::updateContentsHeight (void) { const int iContentsHeight = 128 * m_iItemHeight; qtractorScrollView::resizeContents( qtractorScrollView::contentsWidth(), iContentsHeight); // Force an update on other views too... m_pEditor->editView()->resizeContents( m_pEditor->editView()->contentsWidth(), iContentsHeight); // m_pEditor->editView()->updateContents(); } // Rectangular contents update. void qtractorMidiEditList::updateContents ( const QRect& rect ) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(rect); } // Overall contents update. void qtractorMidiEditList::updateContents (void) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(); } // Resize event handler. void qtractorMidiEditList::resizeEvent ( QResizeEvent *pResizeEvent ) { qtractorScrollView::resizeEvent(pResizeEvent); updateContents(); } // (Re)create the complete view pixmap. void qtractorMidiEditList::updatePixmap ( int /*cx*/, int cy ) { QWidget *pViewport = qtractorScrollView::viewport(); const int w = pViewport->width(); const int h = pViewport->height(); if (w < 1 || h < 1) return; const QPalette& pal = qtractorScrollView::palette(); const bool bDark = (pal.base().color().value() < 128); const QColor& rgbLine = pal.mid().color(); const QColor& rgbLight = QColor(Qt::white).darker(bDark ? 240 : 160); const QColor& rgbDark = QColor(Qt::black).lighter(bDark ? 120 : 180); m_pixmap = QPixmap(w, h); m_pixmap.fill(pal.window().color()); // m_pixmap.fill(m_pEditor->background().darker(140)); QPainter painter(&m_pixmap); // painter.initFrom(this); painter.setFont(qtractorScrollView::font()); const int ch = qtractorScrollView::contentsHeight() - cy; const float hk = (12.0f * m_iItemHeight) / 7.0f; // Key height. const int wk = (w << 1) / 3; // Key width. const int w3 = (wk * 6) / 10; const int q0 = (cy / m_iItemHeight); const int n0 = 127 - q0; const int y0 = q0 * m_iItemHeight - cy; int n, k, y, x = w - wk; // Draw horizontal key-lines... painter.setPen(rgbLine); painter.setBrush(rgbDark); #ifdef CONFIG_GRADIENT QLinearGradient gradLight(x, 0, w, 0); gradLight.setColorAt(0.0f, rgbLight); gradLight.setColorAt(0.1f, rgbLight.lighter()); painter.fillRect(x, 0, wk, h, gradLight); // painter.setBrush(gradLight); #else // painter.setBrush(rgbLight.lighter()); painter.fillRect(x, 0, wk, h, rgbLight.lighter()); #endif y = y0; n = n0; while (y < h && y < ch) { k = (n % 12); if (k >= 5) ++k; if ((k & 1) == 0) { int y1 = ch - int(hk * ((n / 12) * 7 + (k >> 1))); painter.drawLine(x, y1, w, y1); if (k == 0) { painter.setPen(Qt::darkGray); y1 = y + m_iItemHeight; painter.drawText(2, y1 - 2, tr("C%1").arg((n / 12) - 1)); painter.setPen(rgbLine); // p.drawLine(0, y1, x, y1); } } y += m_iItemHeight; --n; } #ifdef CONFIG_GRADIENT QLinearGradient gradDark(x, 0, x + w3, 0); gradDark.setColorAt(0.0, rgbLight); gradDark.setColorAt(0.4, rgbDark); gradDark.setColorAt(0.92, rgbDark); gradDark.setColorAt(0.96, rgbLight); gradDark.setColorAt(1.0, rgbDark); painter.setBrush(gradDark); #else painter.setBrush(rgbDark); #endif y = y0; n = n0; while (y < h && y < ch) { k = (n % 12); if (k >= 5) ++k; if (k & 1) painter.drawRect(x, y, w3, m_iItemHeight); y += m_iItemHeight; --n; } painter.drawLine(x, 0, x, h); if (y > ch) painter.fillRect(0, ch, w, h - ch, pal.dark().color()); } // Draw the piano keyboard. void qtractorMidiEditList::drawContents ( QPainter *pPainter, const QRect& rect ) { pPainter->drawPixmap(rect, m_pixmap, rect); // Are we sticking in some note? if (m_iNoteOn >= 0) { pPainter->fillPath(m_pathNote, m_iNoteVel > 0 ? QColor(255, 0, 120, 120) : QColor(120, 120, 255, 120)); } } // To have keyline in v-sync with main view. void qtractorMidiEditList::contentsYMovingSlot ( int /*cx*/, int cy ) { dragNoteOff(); if (qtractorScrollView::contentsY() != cy) qtractorScrollView::setContentsPos(qtractorScrollView::contentsX(), cy); } // Piano keyboard note-on position handler. void qtractorMidiEditList::dragNoteOn ( const QPoint& pos, int iVelocity ) { dragNoteOn(noteAt(pos), iVelocity, true); } // Piano keyboard note descriminator. int qtractorMidiEditList::noteAt ( const QPoint& pos ) const { // Compute new key cordinates... const int ch = qtractorScrollView::contentsHeight(); QWidget *pViewport = qtractorScrollView::viewport(); const int xk = (pViewport->width() << 1) / 3; int iNote = (ch - pos.y()) / m_iItemHeight; if (pos.x() >= xk) { int k = (iNote % 12); if (k >= 5) ++k; if (k & 1) { const int yk = ch - (12 * iNote * m_iItemHeight / 7); if (pos.y() >= yk) ++iNote; else --iNote; } } return iNote; } // Piano keyboard note-on handler. void qtractorMidiEditList::dragNoteOn ( int iNote, int iVelocity, bool bForce ) { // If it ain't changed we won't change it ;) if (iNote == m_iNoteOn && m_iNoteVel >= iVelocity) return; // Were we pending on some sounding note? dragNoteOff(); // Are we allowed to preview this? if (!m_pEditor->isSendNotesEx() && !bForce) iVelocity = -1; // Now for the sounding new one... if (iNote >= 0) { // This is the new note on... m_iNoteOn = iNote; m_iNoteVel = iVelocity; m_pathNote = notePath(iNote); if (m_iNoteVel > 0) m_pEditor->sendNote(m_iNoteOn, m_iNoteVel, bForce); // Otherwise, reset any pending note... const QRect& rect = m_pathNote.boundingRect().toRect(); qtractorScrollView::viewport()->update(rect); // Propagate this to the proper piano-roll... m_pEditor->editView()->dragNoteOn(iNote, iVelocity); } } // Piano keyboard note-on handler. void qtractorMidiEditList::dragNoteOff (void) { if (m_iNoteOn < 0) return; // Turn off old note... if (m_iNoteVel > 0) m_pEditor->sendNote(m_iNoteOn, 0, true); m_iNoteOn = m_iNoteVel = -1; const QRect& rect = m_pathNote.boundingRect().toRect(); qtractorScrollView::viewport()->update(rect); m_pEditor->editView()->dragNoteOff(); } // Piano keyboard note-key shaper. QPainterPath qtractorMidiEditList::notePath ( int iNote ) const { QPainterPath path; // This stands for the keyboard area... QWidget *pViewport = qtractorScrollView::viewport(); const int w = pViewport->width(); const float wk = float(w << 1) / 3.0f; const float xk = float(w - wk) + 2.0f; float yk, hk; int k = (iNote % 12); if (k >= 5) ++k; hk = float(m_iItemHeight); yk = float(127 - iNote) * hk + 1.0f; QPainterPath path1; path1.addRect(xk, yk, (wk * 6.0f) / 10.0f, hk); #if 1 if (k & 1) { path = path1; } else { const int ch = (128 * m_iItemHeight); hk = (12.0f * m_iItemHeight) / 7.0f; yk = float(ch) - (hk * ((iNote / 12) * 7 + (k >> 1) + 1)); path.addRect(xk, yk, wk, hk); if (k == 0 || k == 2 || k == 6 || k == 8 || k == 10) { path = path.subtracted( path1.translated(0.0f, - 0.5f * hk - 1.5f)); } if (k == 2 || k == 4 || k == 8 || k == 10 || k == 12) { path = path.subtracted( path1.translated(0.0f, + 0.5f * hk + 1.5f)); } } #else if (k & 1) { path = path1; } else { path.addRect(xk, yk, wk, hk); } #endif const QRect& rect = path.boundingRect().toRect(); const QPoint& cpos = rect.topLeft(); const QPoint& vpos = contentsToViewport(cpos); path.translate(vpos - cpos); return path; } // Handle item selection/dragging -- mouse button press. void qtractorMidiEditList::mousePressEvent ( QMouseEvent *pMouseEvent ) { // Force null state. m_dragState = DragNone; // Which mouse state? const bool bModifier = (pMouseEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)); // Make sure we'll reset selection... if (!bModifier) m_pEditor->selectAll(m_pEditor->editView(), false); // Direct snap positioning... const QPoint& pos = viewportToContents(pMouseEvent->pos()); switch (pMouseEvent->button()) { case Qt::LeftButton: // Remember what and where we'll be dragging/selecting... m_dragState = DragStart; m_posDrag = pos; // Are we keying in some keyboard? dragNoteOn(pos); break; default: break; } // qtractorScrollView::mousePressEvent(pMouseEvent); } // Handle item selection/dragging -- mouse pointer move. void qtractorMidiEditList::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { // Are we already moving/dragging something? const QPoint& pos = viewportToContents(pMouseEvent->pos()); const int x = m_pEditor->editView()->contentsX(); switch (m_dragState) { case DragSelect: // Rubber-band selection... m_rectDrag.setBottom(pos.y()); m_pEditor->editView()->ensureVisible(x, pos.y(), 0, 16); m_pEditor->selectRect(m_pEditor->editView(), m_rectDrag, pMouseEvent->modifiers() & Qt::ControlModifier, false); // Are we keying in some keyboard? dragNoteOn(pos); break; case DragStart: // Rubber-band starting... if ((m_posDrag - pos).manhattanLength() > QApplication::startDragDistance()) { // We'll start dragging alright... const int w = m_pEditor->editView()->contentsWidth(); m_rectDrag.setTop(m_posDrag.y()); m_rectDrag.setLeft(0); m_rectDrag.setRight(w); m_rectDrag.setBottom(pos.y()); m_dragState = DragSelect; qtractorScrollView::setCursor(QCursor(Qt::SizeVerCursor)); } // Fall thru... case DragNone: default: // Are we hovering in some keyboard? dragNoteOn(pos, -1); break; } // qtractorScrollView::mouseMoveEvent(pMouseEvent); } // Handle item selection/dragging -- mouse button release. void qtractorMidiEditList::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { // qtractorScrollView::mouseReleaseEvent(pMouseEvent); // Direct snap positioning... switch (m_dragState) { case DragSelect: // Do the final range selection... m_pEditor->selectRect(m_pEditor->editView(), m_rectDrag, pMouseEvent->modifiers() & Qt::ControlModifier, true); // Keyboard notes are reset later anyway... break; case DragStart: case DragNone: default: break; } // Clean up. resetDragState(); } // Handle zoom with mouse wheel. void qtractorMidiEditList::wheelEvent ( QWheelEvent *pWheelEvent ) { if (pWheelEvent->modifiers() & Qt::ControlModifier) { const int delta = pWheelEvent->angleDelta().y(); if (delta > 0) m_pEditor->zoomIn(); else m_pEditor->zoomOut(); } else qtractorScrollView::wheelEvent(pWheelEvent); } // Keyboard event handler. void qtractorMidiEditList::keyPressEvent ( QKeyEvent *pKeyEvent ) { if (pKeyEvent->key() == Qt::Key_Escape) resetDragState(); if (!m_pEditor->keyPress(this, pKeyEvent->key(), pKeyEvent->modifiers())) qtractorScrollView::keyPressEvent(pKeyEvent); } // Reset drag/select/move state. void qtractorMidiEditList::resetDragState (void) { // Were we stuck on some keyboard note? dragNoteOff(); // Cancel any dragging out there... switch (m_dragState) { case DragSelect: qtractorScrollView::updateContents(); // Fall thru... qtractorScrollView::unsetCursor(); // Fall thru again... case DragNone: default: break; } // Also get rid of any meta-breadcrumbs... m_pEditor->resetDragState(this); // Force null state. m_dragState = DragNone; // HACK: give focus to track-view... m_pEditor->editView()->setFocus(); } // Trap for help/tool-tip events. bool qtractorMidiEditList::eventFilter ( QObject *pObject, QEvent *pEvent ) { if (static_cast (pObject) == qtractorScrollView::viewport()) { if (pEvent->type() == QEvent::ToolTip) { QHelpEvent *pHelpEvent = static_cast (pEvent); if (pHelpEvent) { const QPoint& pos = qtractorScrollView::viewportToContents(pHelpEvent->pos()); const QString sToolTip("%1 (%2)"); const int iNote = noteAt(pos); QToolTip::showText(pHelpEvent->globalPos(), sToolTip.arg(m_pEditor->noteName(iNote)).arg(iNote)); return true; } } else if (pEvent->type() == QEvent::Leave) { dragNoteOff(); return true; } } // Not handled here. return qtractorScrollView::eventFilter(pObject, pEvent); } // end of qtractorMidiEditList.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMeter.cpp0000644000000000000000000000013215124701674016652 xustar0030 mtime=1767080892.788263475 30 atime=1767080892.788263475 30 ctime=1767080892.788263475 qtractor-1.5.11/src/qtractorMeter.cpp0000644000175000001440000002601715124701674016650 0ustar00rncbcusers// qtractorMeter.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorMeter.h" #include "qtractorMonitor.h" #include "qtractorMidiControlObserver.h" #include "qtractorMidiControlObserverForm.h" #include "qtractorObserverWidget.h" #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) #define horizontalAdvance width #endif //---------------------------------------------------------------------------- // qtractorMeterScale -- Meter bridge scale widget. // Constructor. qtractorMeterScale::qtractorMeterScale ( qtractorMeter *pMeter ) : QFrame(pMeter) { m_pMeter = pMeter; m_iLastY = 0; QFrame::setFrameShape(QFrame::Panel); QFrame::setFrameShadow(QFrame::Sunken); QFrame::setMinimumWidth(20); QFrame::setMaximumWidth(24); // QFrame::setBackgroundRole(QPalette::Mid); const QFont& font = QFrame::font(); QFrame::setFont(QFont(font.family(), font.pointSize() - 3)); } // Meter accessor. qtractorMeter *qtractorMeterScale::meter (void) const { return m_pMeter; } // Draw scale line and label; assumes labels drawn from top to bottom. void qtractorMeterScale::drawLineLabel ( QPainter *p, int y, const QString& sLabel ) { const int iCurrY = QWidget::height() - y; const int iWidth = QWidget::width(); const QFontMetrics& fm = p->fontMetrics(); const int iMidHeight = (fm.height() >> 1); if (iCurrY < iMidHeight || iCurrY > (m_iLastY + iMidHeight)) { if (fm.horizontalAdvance(sLabel) < iWidth - 5) p->drawLine(iWidth - 3, iCurrY, iWidth - 1, iCurrY); p->drawText(0, iCurrY - iMidHeight, iWidth - 3, fm.height(), Qt::AlignHCenter | Qt::AlignVCenter, sLabel); m_iLastY = iCurrY + 1; } } // Paint event handler. void qtractorMeterScale::paintEvent ( QPaintEvent * ) { QPainter painter(this); m_iLastY = 0; #ifndef CONFIG_GRADIENT painter.setPen(Qt::darkGray); #endif paintScale(&painter); } //---------------------------------------------------------------------------- // qtractorMeterValue -- Meter bridge value widget. // List of meter-values (global obviously) QList qtractorMeterValue::g_values; unsigned long qtractorMeterValue::g_iStamp = 0; // Constructor. qtractorMeterValue::qtractorMeterValue ( qtractorMeter *pMeter ) : QWidget(pMeter), m_pMeter(pMeter) { g_values.append(this); } // Destructor (virtual). qtractorMeterValue::~qtractorMeterValue (void) { g_values.removeAll(this); } // Global refreshment (static). void qtractorMeterValue::refreshAll (void) { ++g_iStamp; QListIterator iter(g_values); while (iter.hasNext()) iter.next()->refresh(g_iStamp); } // Global update (static). void qtractorMeterValue::updateAll (void) { QListIterator iter(g_values); while (iter.hasNext()) iter.next()->update(); } //---------------------------------------------------------------------------- // qtractorMeter -- Meter bridge slot widget. // Constructor. qtractorMeter::qtractorMeter ( QWidget *pParent ) : QWidget(pParent) { m_pBoxLayout = new QHBoxLayout(); m_pBoxLayout->setContentsMargins(0, 0, 0, 0); m_pBoxLayout->setSpacing(2); QWidget::setLayout(m_pBoxLayout); m_fScale = 0.0f; m_iPeakFalloff = 0; // QWidget::setMinimumHeight(160); // QWidget::setMaximumHeight(480); QWidget::setSizePolicy( QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); } // Default destructor. qtractorMeter::~qtractorMeter (void) { // No need to delete child widgets, Qt does it all for us } //---------------------------------------------------------------------- // class qtractorMixerMeter::PanSliderInterface -- Observer interface. // // Local converter interface. class qtractorMixerMeter::PanSliderInterface : public qtractorObserverSlider::Interface { public: // Formerly Pure virtuals. float scaleFromValue ( float fValue ) const { return 100.0f * fValue; } float valueFromScale ( float fScale ) const { return 0.01f * fScale; } }; //---------------------------------------------------------------------------- // qtractorMixerMeter::PanObserver -- Local dedicated observer. class qtractorMixerMeter::PanObserver : public qtractorObserver { public: // Constructor. PanObserver(qtractorMixerMeter *pMixerMeter) : qtractorObserver(nullptr), m_pMixerMeter(pMixerMeter) {} protected: // Update feedback. void update(bool bUpdate) { if (bUpdate) m_pMixerMeter->updatePanning(); } private: // Members. qtractorMixerMeter *m_pMixerMeter; }; //---------------------------------------------------------------------------- // qtractorMixerMeter::GainObserver -- Local dedicated observer. class qtractorMixerMeter::GainObserver : public qtractorObserver { public: // Constructor. GainObserver(qtractorMixerMeter *pMixerMeter) : qtractorObserver(nullptr), m_pMixerMeter(pMixerMeter) {} protected: // Update feedback. void update(bool bUpdate) { if (bUpdate) m_pMixerMeter->updateGain(); } private: // Members. qtractorMixerMeter *m_pMixerMeter; }; //---------------------------------------------------------------------------- // qtractorMixerMeter -- Mixer-strip meter bridge widget. // Constructor. qtractorMixerMeter::qtractorMixerMeter ( QWidget *pParent ) : QWidget(pParent) { const QFont& font = QWidget::font(); // const QFont font2(font.family(), font.pointSize() - 2); const int iFixedHeight = QFontMetrics(font).lineSpacing() + 4; // QWidget::setFont(font2); QVBoxLayout *pVBoxLayout = new QVBoxLayout(); pVBoxLayout->setContentsMargins(0, 0, 0, 0); pVBoxLayout->setSpacing(2); QWidget::setLayout(pVBoxLayout); m_pPanSlider = new qtractorObserverSlider(/*this*/); m_pPanSlider->setOrientation(Qt::Horizontal); m_pPanSlider->setFixedHeight(20); pVBoxLayout->addWidget(m_pPanSlider); m_pPanSpinBox = new qtractorObserverSpinBox(/*this*/); // m_pPanSpinBox->setFont(font2); m_pPanSpinBox->setFixedHeight(iFixedHeight); m_pPanSpinBox->setKeyboardTracking(false); pVBoxLayout->addWidget(m_pPanSpinBox); m_pTopWidget = new QWidget(/*this*/); m_pTopLayout = new QHBoxLayout(); m_pTopLayout->setContentsMargins(2, 2, 2, 2); m_pTopLayout->setSpacing(0); m_pTopWidget->setLayout(m_pTopLayout); pVBoxLayout->addWidget(m_pTopWidget); m_pBoxLayout = new QHBoxLayout(); m_pBoxLayout->setContentsMargins(2, 2, 2, 2); m_pBoxLayout->setSpacing(2); pVBoxLayout->addLayout(m_pBoxLayout); m_pGainSlider = new qtractorObserverSlider(/*, m_pBoxWidget*/); m_pGainSlider->setOrientation(Qt::Vertical); m_pGainSlider->setFixedWidth(20); m_pBoxLayout->addWidget(m_pGainSlider); m_pGainSpinBox = new qtractorObserverSpinBox(/*this*/); // m_pGainSpinBox->setFont(font2); m_pGainSpinBox->setFixedHeight(iFixedHeight); m_pGainSpinBox->setKeyboardTracking(false); pVBoxLayout->addWidget(m_pGainSpinBox); m_pPanObserver = new PanObserver(this); m_pGainObserver = new GainObserver(this); m_pPanSlider->setInterface(new PanSliderInterface()); m_pPanSlider->setTickPosition(QSlider::NoTicks); m_pPanSlider->setMinimum(-100); m_pPanSlider->setMaximum(+100); m_pPanSlider->setPageStep(10); m_pPanSlider->setSingleStep(1); m_pPanSpinBox->setDecimals(1); m_pPanSpinBox->setMinimum(-1.0f); m_pPanSpinBox->setMaximum(+1.0f); m_pPanSpinBox->setSingleStep(0.1f); m_pPanSpinBox->setAlignment(Qt::AlignHCenter); m_pPanSpinBox->setAccelerated(true); m_pPanSpinBox->setToolTip(tr("Pan")); m_pGainSlider->setTickPosition(QSlider::NoTicks); m_pGainSlider->setMinimum(0); m_pGainSlider->setMaximum(10000); m_pGainSlider->setPageStep(250); m_pGainSlider->setSingleStep(50); m_pGainSpinBox->setDecimals(1); // m_pGainSpinBox->setSingleStep(0.1f); m_pGainSpinBox->setAlignment(Qt::AlignHCenter); m_pGainSpinBox->setAccelerated(true); // m_pGainSpinBox->setToolTip(tr("Gain")); QWidget::setMinimumHeight(160); // QWidget::setMaximumHeight(480); QWidget::setSizePolicy( QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); } // Default destructor. qtractorMixerMeter::~qtractorMixerMeter (void) { delete m_pGainObserver; delete m_pPanObserver; // No need to delete child widgets, Qt does it all for us } // Panning subject accessors. void qtractorMixerMeter::setPanningSubject ( qtractorSubject *pSubject ) { m_pPanSlider->setSubject(pSubject); m_pPanSpinBox->setSubject(pSubject); m_pPanObserver->setSubject(pSubject); m_pPanSpinBox->observer()->update(true); m_pPanSlider->observer()->update(true); } qtractorSubject *qtractorMixerMeter::panningSubject (void) const { return m_pPanObserver->subject(); } // Stereo panning accessors. void qtractorMixerMeter::setPanning ( float fPanning ) { m_pPanObserver->setValue(fPanning); monitor()->update(); updatePanning(); } float qtractorMixerMeter::panning (void) const { return m_pPanObserver->value(); } float qtractorMixerMeter::prevPanning (void) const { return m_pPanObserver->prevValue(); } // Gain subject accessors. void qtractorMixerMeter::setGainSubject ( qtractorSubject *pSubject ) { m_pGainSlider->setSubject(pSubject); m_pGainSpinBox->setSubject(pSubject); m_pGainObserver->setSubject(pSubject); m_pGainSpinBox->observer()->update(true); m_pGainSlider->observer()->update(true); } qtractorSubject *qtractorMixerMeter::gainSubject (void) const { return m_pGainObserver->subject(); } // Gain accessors. void qtractorMixerMeter::setGain ( float fGain ) { m_pGainObserver->setValue(fGain); monitor()->update(); updateGain(); } float qtractorMixerMeter::gain (void) const { return m_pGainObserver->value(); } float qtractorMixerMeter::prevGain (void) const { return m_pGainObserver->prevValue(); } // MIDI controller/observer attachment (context menu) activator. // void qtractorMixerMeter::addMidiControlAction ( QWidget *pWidget, qtractorMidiControlObserver *pMidiObserver ) { qtractorMidiControlObserverForm::addMidiControlAction( this, pWidget, pMidiObserver); } void qtractorMixerMeter::midiControlActionSlot (void) { qtractorMidiControlObserverForm::midiControlAction( this, qobject_cast (sender())); } void qtractorMixerMeter::midiControlMenuSlot ( const QPoint& pos ) { qtractorMidiControlObserverForm::midiControlMenu( qobject_cast (sender()), pos); } // end of qtractorMeter.cpp qtractor-1.5.11/src/PaxHeaders/qtractorPluginFactory.cpp0000644000000000000000000000013215124701674020364 xustar0030 mtime=1767080892.798263432 30 atime=1767080892.798263432 30 ctime=1767080892.798263432 qtractor-1.5.11/src/qtractorPluginFactory.cpp0000644000175000001440000010133215124701674020354 0ustar00rncbcusers// qtractorPluginFactory.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorPluginFactory.h" #ifdef CONFIG_LADSPA #include "qtractorLadspaPlugin.h" #endif #ifdef CONFIG_DSSI #include "qtractorDssiPlugin.h" #endif #ifdef CONFIG_VST2 #include "qtractorVst2Plugin.h" #endif #ifdef CONFIG_VST3 #include "qtractorVst3Plugin.h" #endif #ifdef CONFIG_CLAP #include "qtractorClapPlugin.h" #endif #ifdef CONFIG_LV2 #include "qtractorLv2Plugin.h" #endif #include "qtractorInsertPlugin.h" #include "qtractorMidiControlPlugin.h" #include "qtractorOptions.h" #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include #else #include #endif // Deprecated QTextStreamFunctions/Qt namespaces workaround. #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) #define endl Qt::endl #endif //---------------------------------------------------------------------------- // qtractorPluginFactory -- Plugin path helper. // // Singleton instance pointer. qtractorPluginFactory *qtractorPluginFactory::g_pPluginFactory = nullptr; // Singleton instance accessor (static). qtractorPluginFactory *qtractorPluginFactory::getInstance (void) { return g_pPluginFactory; } // Contructor. qtractorPluginFactory::qtractorPluginFactory ( QObject *pParent ) : QObject(pParent), m_typeHint(qtractorPluginType::Any), m_bRescan(false) { // Register cache file paths... //m_clearFilePaths.clear(); #ifdef CONFIG_LADSPA addCacheFilePath(qtractorPluginType::Ladspa); #endif #ifdef CONFIG_DSSI addCacheFilePath(qtractorPluginType::Dssi); #endif #ifdef CONFIG_VST2 addCacheFilePath(qtractorPluginType::Vst2); #endif #ifdef CONFIG_VST3 addCacheFilePath(qtractorPluginType::Vst3); #endif #ifdef CONFIG_CLAP addCacheFilePath(qtractorPluginType::Clap); #endif #ifdef CONFIG_LV2 addCacheFilePath(qtractorPluginType::Lv2); #endif // Load persistent blacklist... //m_blacklist.clear(); QFile data_file(blacklistDataFilePath()); if (data_file.exists()) readBlacklist(data_file); QFile temp_file(blacklistTempFilePath()); if (temp_file.exists()) { readBlacklist(temp_file); temp_file.remove(); } g_pPluginFactory = this; } // Destructor. qtractorPluginFactory::~qtractorPluginFactory (void) { g_pPluginFactory = nullptr; reset(); clear(); m_paths.clear(); // Save persistent blacklist... QFile data_file(blacklistDataFilePath()); if (!m_blacklist.isEmpty()) writeBlacklist(data_file, m_blacklist); else if (data_file.exists()) data_file.remove(); m_blacklist.clear(); m_cacheFilePaths.clear(); } // A common scheme for (a default) plugin serach paths... // #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) #define PATH_SEP ";" #else #define PATH_SEP ":" #endif static QStringList default_paths ( const QString& suffix ) { const QString& sep = QDir::separator(); const QString& home = QDir::homePath(); const QString& pre1 = QDir::rootPath() + "usr"; const QString& pre2 = pre1 + sep + "local"; const QString& lib0 = "lib"; const QString& lib1 = pre1 + sep + lib0; const QString& lib2 = pre2 + sep + lib0; #if defined(__x86_64__) const QString& x64 = "64"; const QString& lib3 = lib1 + x64; const QString& lib4 = lib2 + x64; #endif QStringList paths; paths << home + sep + '.' + suffix; #if defined(__x86_64__) // paths << home + sep + lib0 + x64 + sep + suffix; paths << lib4 + sep + suffix; paths << lib3 + sep + suffix; #endif // paths << home + sep + lib0 + sep + suffix; paths << lib2 + sep + suffix; paths << lib1 + sep + suffix; // Get rid of duplicate symlinks (canonical paths)... QStringList ret; QStringListIterator iter(paths); while (iter.hasNext()) { const QFileInfo info(iter.next()); if (info.exists() && info.isDir()) { const QString& path = info.canonicalFilePath(); if (!ret.contains(path)) ret.append(path); } } return ret; } void qtractorPluginFactory::updatePluginPaths ( qtractorPluginType::Hint typeHint ) { qtractorOptions *pOptions = qtractorOptions::getInstance(); #ifdef CONFIG_LADSPA if (typeHint == qtractorPluginType::Any || typeHint == qtractorPluginType::Ladspa) { // LADSPA default paths... QStringList ladspa_paths; if (pOptions) ladspa_paths = pOptions->ladspaPaths; if (ladspa_paths.isEmpty()) { QStringList sLadspaPaths; const char *ladspa_path = ::getenv("LADSPA_PATH"); if (ladspa_path) sLadspaPaths << QString::fromUtf8(ladspa_path).split(PATH_SEP); if (sLadspaPaths.isEmpty()) ladspa_paths << default_paths("ladspa"); else ladspa_paths << sLadspaPaths; } m_paths.insert(qtractorPluginType::Ladspa, ladspa_paths); } #endif #ifdef CONFIG_DSSI if (typeHint == qtractorPluginType::Any || typeHint == qtractorPluginType::Dssi) { // DSSI default paths... QStringList dssi_paths; if (pOptions) dssi_paths = pOptions->dssiPaths; if (dssi_paths.isEmpty()) { QStringList sDssiPaths; const char *dssi_path = ::getenv("DSSI_PATH"); if (dssi_path) sDssiPaths << QString::fromUtf8(dssi_path).split(PATH_SEP); if (sDssiPaths.isEmpty()) dssi_paths << default_paths("dssi"); else dssi_paths << sDssiPaths; } m_paths.insert(qtractorPluginType::Dssi, dssi_paths); } #endif #ifdef CONFIG_VST2 if (typeHint == qtractorPluginType::Any || typeHint == qtractorPluginType::Vst2) { // VST2 default paths... QStringList vst2_paths; if (pOptions) vst2_paths = pOptions->vst2Paths; if (vst2_paths.isEmpty()) { QStringList sVst2Paths; const char *vst2_path = ::getenv("VST2_PATH"); if (vst2_path) sVst2Paths << QString::fromUtf8(vst2_path).split(PATH_SEP); if (sVst2Paths.isEmpty()) vst2_paths << default_paths("vst2"); else vst2_paths << sVst2Paths; // LXVST_PATH... sVst2Paths.clear(); vst2_path = ::getenv("LXVST_PATH"); if (vst2_path) sVst2Paths << QString::fromUtf8(vst2_path).split(PATH_SEP); if (sVst2Paths.isEmpty()) vst2_paths << default_paths("lxvst"); else vst2_paths << sVst2Paths; // VST_PATH... sVst2Paths.clear(); vst2_path = ::getenv("VST_PATH"); if (vst2_path) sVst2Paths << QString::fromUtf8(vst2_path).split(PATH_SEP); if (sVst2Paths.isEmpty()) vst2_paths << default_paths("vst"); else vst2_paths << sVst2Paths; } m_paths.insert(qtractorPluginType::Vst2, vst2_paths); } #endif #ifdef CONFIG_VST3 if (typeHint == qtractorPluginType::Any || typeHint == qtractorPluginType::Vst3) { // VST3 default paths... QStringList vst3_paths; if (pOptions) vst3_paths = pOptions->vst3Paths; if (vst3_paths.isEmpty()) { QStringList sVst3Paths; const char *vst3_path = ::getenv("VST3_PATH"); if (vst3_path) sVst3Paths << QString::fromUtf8(vst3_path).split(PATH_SEP); if (sVst3Paths.isEmpty()) vst3_paths << default_paths("vst3"); else vst3_paths << sVst3Paths; } m_paths.insert(qtractorPluginType::Vst3, vst3_paths); } #endif #ifdef CONFIG_CLAP if (typeHint == qtractorPluginType::Any || typeHint == qtractorPluginType::Clap) { // CLAP default paths... QStringList clap_paths; if (pOptions) clap_paths = pOptions->clapPaths; if (clap_paths.isEmpty()) { QStringList sClapPaths; const char *clap_path = ::getenv("CLAP_PATH"); if (clap_path) sClapPaths << QString::fromUtf8(clap_path).split(PATH_SEP); if (sClapPaths.isEmpty()) clap_paths << default_paths("clap"); else clap_paths << sClapPaths; } m_paths.insert(qtractorPluginType::Clap, clap_paths); } #endif #ifdef CONFIG_LV2 if (typeHint == qtractorPluginType::Any || typeHint == qtractorPluginType::Lv2) { // LV2 default paths... QStringList lv2_paths; if (pOptions) lv2_paths = pOptions->lv2Paths; if (lv2_paths.isEmpty()) { QStringList sLv2Paths; const char *lv2_path = ::getenv("LV2_PATH"); if (lv2_path) sLv2Paths << QString::fromUtf8(lv2_path).split(PATH_SEP); if (sLv2Paths.isEmpty()) lv2_paths << default_paths("lv2"); else lv2_paths << sLv2Paths; } #ifdef CONFIG_LV2_PRESETS QString sLv2PresetDir; if (pOptions) sLv2PresetDir = pOptions->sLv2PresetDir; if (sLv2PresetDir.isEmpty()) sLv2PresetDir = QDir::homePath() + QDir::separator() + ".lv2"; if (!lv2_paths.contains(sLv2PresetDir)) lv2_paths.append(sLv2PresetDir); #endif m_paths.insert(qtractorPluginType::Lv2, lv2_paths); // HACK: set special environment for LV2... ::setenv("LV2_PATH", lv2_paths.join(PATH_SEP).toUtf8().constData(), 1); } #endif } QStringList qtractorPluginFactory::pluginPaths ( qtractorPluginType::Hint typeHint ) { if (m_paths.isEmpty()) updatePluginPaths(qtractorPluginType::Any); return m_paths.value(typeHint); } // Blacklist accessors. void qtractorPluginFactory::setBlacklist ( const QStringList& blacklist ) { m_blacklist = blacklist; } const QStringList& qtractorPluginFactory::blacklist (void) const { return m_blacklist; } // Absolute cache file path registration. void qtractorPluginFactory::addCacheFilePath ( qtractorPluginType::Hint typeHint ) { const QString& sCacheName = "qtractor_" + qtractorPluginType::textFromHint(typeHint).toLower() + "_scan.cache"; const QString& sCacheDir #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) = QDesktopServices::storageLocation(QDesktopServices::CacheLocation); #else = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); #endif const QFileInfo fi(sCacheDir, sCacheName); const QDir& dir = fi.absoluteDir(); if (!dir.exists()) dir.mkpath(dir.path()); m_cacheFilePaths.insert(typeHint, fi.absoluteFilePath()); } // Absolute temporary blacklist file path. QString qtractorPluginFactory::blacklistTempFilePath (void) const { const QString& sTempName = "qtractor_plugin_blacklist.temp"; const QString& sTempDir #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) = QDesktopServices::storageLocation(QDesktopServices::TempLocation); #else = QStandardPaths::writableLocation(QStandardPaths::TempLocation); #endif const QFileInfo fi(sTempDir, sTempName); const QDir& dir = fi.absoluteDir(); if (!dir.exists()) dir.mkpath(dir.path()); return fi.absoluteFilePath(); } // Absolute persistent blacklist file path. QString qtractorPluginFactory::blacklistDataFilePath (void) const { const QString& sDataName = "qtractor_plugin_blacklist.data"; const QString& sDataDir #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) = QDesktopServices::storageLocation(QDesktopServices::DataLocation); #else = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); #endif const QFileInfo fi(sDataDir, sDataName); const QDir& dir = fi.absoluteDir(); if (!dir.exists()) dir.mkpath(dir.absolutePath()); return fi.absoluteFilePath(); } // Read from file and append to blacklist. bool qtractorPluginFactory::readBlacklist ( QFile& file ) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return false; QTextStream sin(&file); while (!sin.atEnd()) { const QString& line = sin.readLine(); if (line.isEmpty()) continue; m_blacklist.append(line); } file.close(); return true; } // Write/append blacklist to file. bool qtractorPluginFactory::writeBlacklist ( QFile& file, const QStringList& blacklist ) const { if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) return false; QTextStream sout(&file); QStringListIterator iter(blacklist); while (iter.hasNext()) sout << iter.next() << endl; file.close(); return true; } // Generic plugin-scan factory method. int qtractorPluginFactory::startScan ( qtractorPluginType::Hint typeHint ) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return 0; int iDummyPluginHash = 0; switch (typeHint) { case qtractorPluginType::Ladspa: iDummyPluginHash = pOptions->iDummyLadspaHash; break; case qtractorPluginType::Dssi: iDummyPluginHash = pOptions->iDummyDssiHash; break; case qtractorPluginType::Vst2: iDummyPluginHash = pOptions->iDummyVst2Hash; break; case qtractorPluginType::Vst3: iDummyPluginHash = pOptions->iDummyVst3Hash; break; case qtractorPluginType::Clap: iDummyPluginHash = pOptions->iDummyClapHash; break; case qtractorPluginType::Lv2: iDummyPluginHash = pOptions->iDummyLv2Hash; break; default: break; } const QString& sCacheFilePath = m_cacheFilePaths.value(typeHint); if (sCacheFilePath.isEmpty()) return 0; Scanner *pScanner = new Scanner(typeHint, this); if (!pScanner->open(sCacheFilePath, iDummyPluginHash)) return 0; m_scanners.insert(typeHint, pScanner); QStringList& files = m_files[typeHint]; files.append(pScanner->files()); #ifdef CONFIG_LV2 if (typeHint == qtractorPluginType::Lv2) files.clear(); #endif int iFileCount = files.count(); if (m_bRescan || iFileCount == 0) { m_bRescan = false; iFileCount += addFiles(typeHint, pluginPaths(typeHint)); } return iFileCount; } // Executive methods. void qtractorPluginFactory::scan (void) { // Start clean. reset(); // Get paths based on hints... int iFileCount = 0; #ifdef CONFIG_LADSPA // LADSPA default path... if (m_typeHint == qtractorPluginType::Any || m_typeHint == qtractorPluginType::Ladspa) { iFileCount += startScan(qtractorPluginType::Ladspa); } #endif #ifdef CONFIG_DSSI // DSSI default path... if (m_typeHint == qtractorPluginType::Any || m_typeHint == qtractorPluginType::Dssi) { iFileCount += startScan(qtractorPluginType::Dssi); } #endif #ifdef CONFIG_VST2 // VST default path... if (m_typeHint == qtractorPluginType::Any || m_typeHint == qtractorPluginType::Vst2) { iFileCount += startScan(qtractorPluginType::Vst2); } #endif #ifdef CONFIG_VST3 // VST3 default path... if (m_typeHint == qtractorPluginType::Any || m_typeHint == qtractorPluginType::Vst3) { iFileCount += startScan(qtractorPluginType::Vst3); } #endif #ifdef CONFIG_CLAP // CLAP default path... if (m_typeHint == qtractorPluginType::Any || m_typeHint == qtractorPluginType::Clap) { iFileCount += startScan(qtractorPluginType::Clap); } #endif #ifdef CONFIG_LV2 // LV2 default path... if (m_typeHint == qtractorPluginType::Any || m_typeHint == qtractorPluginType::Lv2) { iFileCount += startScan(qtractorPluginType::Lv2); } #endif // Do the real scan... int iFile = 0; Paths::ConstIterator files_iter = m_files.constBegin(); const Paths::ConstIterator& files_end = m_files.constEnd(); for ( ; files_iter != files_end; ++files_iter) { const qtractorPluginType::Hint typeHint = files_iter.key(); QStringListIterator file_iter(files_iter.value()); while (file_iter.hasNext()) { addTypes(typeHint, file_iter.next()); emit scanned((++iFile * 100) / iFileCount); QApplication::processEvents( QEventLoop::ExcludeUserInputEvents); } } // Done. reset(); } void qtractorPluginFactory::reset (void) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // Check the proxy (out-of-process) client closure... Scanners::ConstIterator iter = m_scanners.constBegin(); const Scanners::ConstIterator& iter_end = m_scanners.constEnd(); for ( ; iter != iter_end; ++iter) { const qtractorPluginType::Hint typeHint = iter.key(); Scanner *pScanner = iter.value(); if (pScanner) { pScanner->close(); const int iDummyPluginHash = pScanner->dummyPluginHash(); switch (typeHint) { case qtractorPluginType::Ladspa: pOptions->iDummyLadspaHash = iDummyPluginHash; break; case qtractorPluginType::Dssi: pOptions->iDummyDssiHash = iDummyPluginHash; break; case qtractorPluginType::Vst2: pOptions->iDummyVst2Hash = iDummyPluginHash; break; case qtractorPluginType::Vst3: pOptions->iDummyVst3Hash = iDummyPluginHash; break; case qtractorPluginType::Clap: pOptions->iDummyClapHash = iDummyPluginHash; break; case qtractorPluginType::Lv2: pOptions->iDummyLv2Hash = iDummyPluginHash; break; default: break; } } } qDeleteAll(m_scanners); m_scanners.clear(); m_files.clear(); } void qtractorPluginFactory::clear (void) { qDeleteAll(m_types); m_types.clear(); } void qtractorPluginFactory::clearAll ( qtractorPluginType::Hint typeHint ) { if (typeHint == qtractorPluginType::Any) { CacheFilePaths::ConstIterator iter = m_cacheFilePaths.constBegin(); const CacheFilePaths::ConstIterator& iter_end = m_cacheFilePaths.constEnd(); for ( ; iter != iter_end; ++iter) { const QString& sCacheFilePath = iter.value(); if (!sCacheFilePath.isEmpty()) QFile::remove(sCacheFilePath); } } else { const QString& sCacheFilePath = m_cacheFilePaths.value(typeHint); if (!sCacheFilePath.isEmpty()) QFile::remove(sCacheFilePath); } clear(); } // Recursive plugin file/path inventory method. int qtractorPluginFactory::addFiles ( qtractorPluginType::Hint typeHint, const QStringList& paths ) { #ifdef CONFIG_LV2 if (typeHint == qtractorPluginType::Lv2) { QStringList& files = m_files[typeHint]; files.append(qtractorLv2PluginType::lv2_plugins()); return files.count(); } #endif int iFileCount = 0; QStringListIterator path_iter(paths); while (path_iter.hasNext()) iFileCount += addFiles(typeHint, path_iter.next()); return iFileCount; } int qtractorPluginFactory::addFiles ( qtractorPluginType::Hint typeHint, const QString& sPath ) { int iFileCount = 0; QStringList& files = m_files[typeHint]; const QDir dir(sPath); QDir::Filters filters = QDir::Files; if (typeHint == qtractorPluginType::Vst2 || typeHint == qtractorPluginType::Vst3 || typeHint == qtractorPluginType::Clap) filters = filters | QDir::AllDirs | QDir::NoDotAndDotDot; const QFileInfoList& info_list = dir.entryInfoList(filters); QListIterator info_iter(info_list); while (info_iter.hasNext()) { const QFileInfo& info = info_iter.next(); const QString& sFilename = info.absoluteFilePath(); if (info.isDir() && info.isReadable()) iFileCount += addFiles(typeHint, sFilename); else if (!files.contains(sFilename) && (QLibrary::isLibrary(sFilename) #ifdef CONFIG_CLAP || (typeHint == qtractorPluginType::Clap && info.suffix() == "clap") #endif )) { files.append(sFilename); ++iFileCount; } } return iFileCount; } // Plugin factory method (static). qtractorPlugin *qtractorPluginFactory::createPlugin ( qtractorPluginList *pList, const QString& sFilename, unsigned long iIndex, qtractorPluginType::Hint typeHint ) { #ifdef CONFIG_DEBUG qDebug("qtractorPluginFactory::createPlugin(%p, \"%s\", %lu, %d)", pList, sFilename.toUtf8().constData(), iIndex, int(typeHint)); #endif // Attend to insert pseudo-plugin hints... if (sFilename.isEmpty()) { if (typeHint == qtractorPluginType::Insert) return qtractorInsertPluginType::createPlugin(pList, iIndex); else if (typeHint == qtractorPluginType::AuxSend) return qtractorAuxSendPluginType::createPlugin(pList, iIndex); else if (typeHint == qtractorPluginType::Control) return qtractorMidiControlPluginType::createPlugin(pList); else // Don't bother with anything else. return nullptr; } #ifdef CONFIG_LV2 // Try LV2 plugins hints before anything else... if (typeHint == qtractorPluginType::Lv2) { qtractorLv2PluginType *pLv2Type = qtractorLv2PluginType::createType(sFilename); if (pLv2Type) { if (pLv2Type->open()) return new qtractorLv2Plugin(pList, pLv2Type); delete pLv2Type; } // Bail out. return nullptr; } #endif // Try to fill the types list at this moment... qtractorPluginFile *pFile = qtractorPluginFile::addFile(sFilename); if (pFile == nullptr) return nullptr; #ifdef CONFIG_DSSI // Try DSSI plugin types first... if (typeHint == qtractorPluginType::Dssi) { qtractorDssiPluginType *pDssiType = qtractorDssiPluginType::createType(pFile, iIndex); if (pDssiType) { pFile->addRef(); if (pDssiType->open()) return new qtractorDssiPlugin(pList, pDssiType); delete pDssiType; } } #endif #ifdef CONFIG_LADSPA // Try LADSPA plugin types... if (typeHint == qtractorPluginType::Ladspa) { qtractorLadspaPluginType *pLadspaType = qtractorLadspaPluginType::createType(pFile, iIndex); if (pLadspaType) { pFile->addRef(); if (pLadspaType->open()) return new qtractorLadspaPlugin(pList, pLadspaType); delete pLadspaType; } } #endif #ifdef CONFIG_VST2 // Try VST2 plugin types... if (typeHint == qtractorPluginType::Vst2) { qtractorVst2PluginType *pVst2Type = qtractorVst2PluginType::createType(pFile, iIndex); if (pVst2Type) { pFile->addRef(); if (pVst2Type->open()) return new qtractorVst2Plugin(pList, pVst2Type); delete pVst2Type; } } #endif #ifdef CONFIG_VST3 // Try VST3 plugin types... if (typeHint == qtractorPluginType::Vst3) { qtractorVst3PluginType *pVst3Type = qtractorVst3PluginType::createType(pFile, iIndex); if (pVst3Type) { pFile->addRef(); if (pVst3Type->open()) return new qtractorVst3Plugin(pList, pVst3Type); delete pVst3Type; } } #endif #ifdef CONFIG_CLAP // Try CLAP plugin types... if (typeHint == qtractorPluginType::Clap) { qtractorClapPluginType *pClapType = qtractorClapPluginType::createType(pFile, iIndex); if (pClapType) { pFile->addRef(); if (pClapType->open()) return new qtractorClapPlugin(pList, pClapType); delete pClapType; } } #endif // Bad luck, no valid plugin found... qtractorPluginFile::removeFile(pFile); return nullptr; } // Plugin type listing methods. bool qtractorPluginFactory::addTypes ( qtractorPluginType::Hint typeHint, const QString& sFilename ) { // Check if already blacklisted... if (m_blacklist.contains(sFilename)) return false; // Try first out-of-process scans, if any... Scanner *pScanner = m_scanners.value(typeHint, nullptr); if (pScanner) return pScanner->addTypes(typeHint, sFilename); else return false; } // Plugin type listing method. bool qtractorPluginFactory::addTypes ( qtractorPluginType::Hint typeHint, qtractorPluginFile *pFile, unsigned long iIndex ) { qtractorPluginType *pType = nullptr; switch (typeHint) { #ifdef CONFIG_LADSPA case qtractorPluginType::Ladspa: // Try LADSPA plugin type... pType = qtractorLadspaPluginType::createType(pFile, iIndex); break; #endif #ifdef CONFIG_DSSI case qtractorPluginType::Dssi: // Try DSSI plugin type... pType = qtractorDssiPluginType::createType(pFile, iIndex); break; #endif #ifdef CONFIG_VST2 case qtractorPluginType::Vst2: // Try VST2 plugin type... pType = qtractorVst2PluginType::createType(pFile, iIndex); break; #endif #ifdef CONFIG_VST3 case qtractorPluginType::Vst3: // Try VST3 plugin type... pType = qtractorVst3PluginType::createType(pFile, iIndex); break; #endif #ifdef CONFIG_CLAP case qtractorPluginType::Clap: // Try CLAP plugin type... pType = qtractorClapPluginType::createType(pFile, iIndex); break; #endif default: break; } if (pType == nullptr) return false; if (pType->open()) { pFile->addRef(); addType(pType); pType->close(); return true; } else { delete pType; return false; } } //---------------------------------------------------------------------------- // qtractorPluginFactory::Scanner -- Plugin path proxy (out-of-process client). // // Constructor. qtractorPluginFactory::Scanner::Scanner ( qtractorPluginType::Hint typeHint, QObject *pParent ) : QProcess(pParent), m_typeHint(typeHint), m_iExitStatus(-1), m_iDummyPluginHash(0) { QObject::connect(this, SIGNAL(readyReadStandardOutput()), SLOT(stdout_slot())); QObject::connect(this, SIGNAL(readyReadStandardError()), SLOT(stderr_slot())); QObject::connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(exit_slot(int, QProcess::ExitStatus))); } // Open/start method. bool qtractorPluginFactory::Scanner::open ( const QString& sCacheFilePath, int iDummyPluginHash ) { // Cache file setup... m_file.setFileName(sCacheFilePath); m_list.clear(); // Open and read cache file, whether applicable... m_iDummyPluginHash = 0; if (m_file.open(QIODevice::ReadOnly | QIODevice::Text)) { // Read from cache... QTextStream sin(&m_file); while (!sin.atEnd()) { const QString& sText = sin.readLine(); if (sText.isEmpty()) continue; const QStringList& props = sText.split('|'); if (props.count() >= 6) { // get filename... m_list[props.at(6)].append(sText); ++m_iDummyPluginHash; } } // May close the file. m_file.close(); } if (iDummyPluginHash > 0 && iDummyPluginHash == m_iDummyPluginHash) return true; // Re-open cache file for update... if (!m_file.open(QIODevice::Append | QIODevice::Text)) { // Make sure cache file location do exists... const QFileInfo fi(m_file); if (!fi.dir().mkpath(fi.absolutePath())) return false; // Open cache file for writing... if (!m_file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) return false; } // LV2 plugins are dang special, // need no out-of-process scanning whatsoever... if (m_typeHint == qtractorPluginType::Lv2) { m_iExitStatus = 0; return true; } // Go go go... return start(); } // Close/stop method. void qtractorPluginFactory::Scanner::close (void) { // We're we scanning hard?... if (QProcess::state() != QProcess::NotRunning || m_iExitStatus < 0) { QProcess::closeWriteChannel(); while (QProcess::state() != QProcess::NotRunning) QProcess::waitForFinished(200); } // Done anyway. QProcess::terminate(); // Close cache file... if (m_file.isOpen()) m_file.close(); // Cleanup cache... m_list.clear(); } // Scan start method. bool qtractorPluginFactory::Scanner::start (void) { // Maybe we're still running, doh! if (QProcess::state() != QProcess::NotRunning) return false; // Start from scratch... m_iExitStatus = -1; // Get the main scanner executable... const QString sName("qtractor_plugin_scan"); QString sLibPath = QApplication::applicationDirPath(); QFileInfo fi(sLibPath, sName); if (!fi.isExecutable()) { sLibPath.remove(CONFIG_BINDIR); sLibPath.append(CONFIG_LIBDIR); sLibPath.append(QDir::separator()); sLibPath.append(PROJECT_NAME); fi = QFileInfo(sLibPath, sName); } if (!fi.isExecutable()) return false; // Go go go! QProcess::start(fi.filePath(), QStringList()); return true; } // Service slots. void qtractorPluginFactory::Scanner::stdout_slot (void) { qtractorPluginFactory *pPluginFactory = static_cast (QObject::parent()); if (pPluginFactory == nullptr) return; const QString sData(QProcess::readAllStandardOutput()); addTypes(sData.split('\n'), true); } void qtractorPluginFactory::Scanner::stderr_slot (void) { QTextStream(stderr) << QProcess::readAllStandardError(); } void qtractorPluginFactory::Scanner::exit_slot ( int exitCode, QProcess::ExitStatus exitStatus ) { if (m_iExitStatus < 0) m_iExitStatus = 0; if (exitCode || exitStatus != QProcess::NormalExit) ++m_iExitStatus; } // Service methods. bool qtractorPluginFactory::Scanner::addTypes ( qtractorPluginType::Hint typeHint, const QString& sFilename ) { // See if it's already cached in... const QStringList& list = m_list.value(sFilename); if (!list.isEmpty() && ( #ifdef CONFIG_LV2 m_typeHint == qtractorPluginType::Lv2 || #endif QFileInfo(sFilename).exists())) { return addTypes(list, false); } // If cache file isn't open for update // there's no use to run any further... if (!m_file.isOpen()) return false; qtractorPluginFactory *pPluginFactory = static_cast (QObject::parent()); if (pPluginFactory == nullptr) return false; #ifdef CONFIG_LV2 // LV2 plugins are dang special... if (typeHint == qtractorPluginType::Lv2) { qtractorPluginType *pType = qtractorLv2PluginType::createType(sFilename); if (pType == nullptr) return false; if (pType->open()) { pPluginFactory->addType(pType); pType->close(); // Cache out... if (m_file.isOpen()) { QTextStream sout(&m_file); sout << "LV2|"; sout << pType->name() << '|'; sout << pType->audioIns() << ':' << pType->audioOuts() << '|'; sout << pType->midiIns() << ':' << pType->midiOuts() << '|'; sout << pType->controlIns() << ':' << pType->controlOuts() << '|'; QStringList flags; if (pType->isEditor()) flags.append("GUI"); if (pType->isConfigure()) flags.append("EXT"); if (pType->isRealtime()) flags.append("RT"); sout << flags.join(",") << '|'; sout << sFilename << '|' << 0 << '|'; sout << "0x" << QString::number(pType->uniqueID(), 16) << endl; ++m_iDummyPluginHash; } // Success. return true; } else { // Fail. delete pType; return false; } } #endif // Add to temporary blacklist... QFile temp_file(pPluginFactory->blacklistTempFilePath()); if (temp_file.exists()) pPluginFactory->readBlacklist(temp_file); pPluginFactory->writeBlacklist(temp_file, QStringList() << sFilename); // Not cached, yet... const QString& sHint = qtractorPluginType::textFromHint(typeHint); const QString& sLine = sHint + ':' + sFilename + '\n'; const QByteArray& data = sLine.toUtf8(); bool bResult = (QProcess::write(data) == data.size()); // Check for hideous scan crashes... if (!QProcess::waitForReadyRead(3000)) { if (m_iExitStatus > 0) { QProcess::waitForFinished(200); start(); // Restart the crashed scan... QProcess::waitForStarted(200); bResult = false; } } // If it reaches here safely, then there's // no use to temporary blacklist anymore... if (bResult) temp_file.remove(); return bResult; } bool qtractorPluginFactory::Scanner::addTypes ( const QStringList& list, bool bDummyPluginType ) { qtractorPluginFactory *pPluginFactory = static_cast (QObject::parent()); if (pPluginFactory == nullptr) return false; QStringListIterator iter(list); while (iter.hasNext()) { const QString& sText = iter.next().simplified(); if (sText.isEmpty()) continue; qtractorPluginType *pType = qtractorDummyPluginType::createType(sText); if (pType) { // Brand new type, add to inventory... pPluginFactory->addType(pType); // Cache in... if (bDummyPluginType && m_file.isOpen()) { QTextStream(&m_file) << sText << endl; ++m_iDummyPluginHash; } // Done. } else { // Possibly some mistake occurred... QTextStream(stderr) << sText << endl; } } return true; } // Cached files accessor. QStringList qtractorPluginFactory::Scanner::files (void) const { return m_list.keys(); } // Cache hash result. int qtractorPluginFactory::Scanner::dummyPluginHash (void) const { return m_iDummyPluginHash; } //---------------------------------------------------------------------------- // qtractorDummyPluginType -- Dummy plugin type instance. // // Constructor. qtractorDummyPluginType::qtractorDummyPluginType ( const QString& sText, unsigned long iIndex, Hint typeHint ) : qtractorPluginType(nullptr, iIndex, typeHint) { const QStringList& props = sText.split('|'); m_sName = props.at(1); m_sLabel = m_sName.simplified().replace(QRegularExpression("[\\s|\\.|\\-]+"), "_"); const QStringList& audios = props.at(2).split(':'); m_iAudioIns = audios.at(0).toUShort(); m_iAudioOuts = audios.at(1).toUShort(); const QStringList& midis = props.at(3).split(':'); m_iMidiIns = midis.at(0).toUShort(); m_iMidiOuts = midis.at(1).toUShort(); const QStringList& controls = props.at(4).split(':'); m_iControlIns = controls.at(0).toUShort(); m_iControlOuts = controls.at(1).toUShort(); const QStringList& flags = props.at(5).split(','); m_bEditor = flags.contains("GUI"); m_bConfigure = flags.contains("EXT"); m_bRealtime = flags.contains("RT"); m_sFilename = props.at(6); bool bOk = false; QString sUniqueID = props.at(8); m_iUniqueID = qHash(sUniqueID.remove("0x").toULong(&bOk, 16)); } // Must be overridden methods. bool qtractorDummyPluginType::open (void) { return true; } void qtractorDummyPluginType::close (void) { } // Factory method (static) qtractorDummyPluginType *qtractorDummyPluginType::createType ( const QString& sText ) { // Sanity checks... const QStringList& props = sText.split('|'); if (props.count() < 9) return nullptr; const Hint typeHint = qtractorPluginType::hintFromText(props.at(0)); if (typeHint == Any) return nullptr; const unsigned long iIndex = props.at(7).toULong(); return new qtractorDummyPluginType(sText, iIndex, typeHint); } // end of qtractorPluginFactory.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMessageList.h0000644000000000000000000000013215124701674017463 xustar0030 mtime=1767080892.788263475 30 atime=1767080892.788263475 30 ctime=1767080892.788263475 qtractor-1.5.11/src/qtractorMessageList.h0000644000175000001440000000320315124701674017451 0ustar00rncbcusers// qtractorMessageList.h // /**************************************************************************** Copyright (C) 2005-2019, 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 __qtractorMessageList_h #define __qtractorMessageList_h #include //---------------------------------------------------------------------- // class qtractorMessageList -- message string list buffer. // class qtractorMessageList { public: // Constructor. qtractorMessageList(); // Destructor. ~qtractorMessageList(); // Forfeit methods. static void append(const QString& sText); static bool isEmpty(); static QStringList items(); static void clear(); private: // Instance variable. QStringList m_items; // Pseudo-singleton instance. static qtractorMessageList *g_pInstance; }; #endif // __qtractorMessageList_h // end of qtractorMessageList.h qtractor-1.5.11/src/PaxHeaders/qtractorObserverWidget.h0000644000000000000000000000013215124701674020176 xustar0030 mtime=1767080892.796263441 30 atime=1767080892.796263441 30 ctime=1767080892.796263441 qtractor-1.5.11/src/qtractorObserverWidget.h0000644000175000001440000001120615124701674020166 0ustar00rncbcusers// qtractorObserverWidget.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorObserverWidget_h #define __qtractorObserverWidget_h #include "qtractorObserver.h" //---------------------------------------------------------------------- // class qtractorObserverWidget -- Template widget observer/visitor. // #include "qtractorSpinBox.h" #include #include template class qtractorObserverWidget : public Widget { public: // Local interface converter. class Interface { public: // Virtual destructor. virtual ~Interface() {} // Pure virtuals. virtual float scaleFromValue(float fValue) const = 0; virtual float valueFromScale(float fScale) const = 0; }; // Local observer. class Observer : public qtractorObserver { public: // Constructor. Observer(qtractorSubject *pSubject, qtractorObserverWidget *pWidget) : qtractorObserver(pSubject), m_pWidget(pWidget) {} // Observer updater. void update(bool bUpdate) { if (bUpdate) m_pWidget->updateValue(value()); } private: // Members. qtractorObserverWidget *m_pWidget; }; // Constructor. qtractorObserverWidget(QWidget *pParent = 0) : Widget(pParent), m_pInterface(nullptr), m_observer(nullptr, this) {} // Destructor. ~qtractorObserverWidget() { setInterface(nullptr); } // Setup. void setSubject(qtractorSubject *pSubject) { m_observer.setSubject(pSubject); } qtractorSubject *subject() const { return m_observer.subject(); } // Observer accessor. Observer *observer() { return &m_observer; } // Interface setup. void setInterface(Interface *pInterface) { if (m_pInterface) delete m_pInterface; m_pInterface = pInterface; } // Interface methods. float scaleFromValue(float fValue) const { return (m_pInterface ? m_pInterface->scaleFromValue(fValue) : fValue); } float valueFromScale(float fScale) const { return (m_pInterface ? m_pInterface->valueFromScale(fScale) : fScale); } protected: // Pure virtual visitor. virtual void updateValue(float fValue) = 0; private: // Members. Interface *m_pInterface; Observer m_observer; }; //---------------------------------------------------------------------- // class qtractorObserverCheckBox -- Concrete widget observer. // class qtractorObserverCheckBox : public qtractorObserverWidget { Q_OBJECT public: // Constructor. qtractorObserverCheckBox(QWidget *pParent = nullptr); protected: // Visitors overload. void updateValue(float fValue); protected slots: void checkBoxChanged(bool bValue); signals: void valueChanged(float); }; //---------------------------------------------------------------------- // class qtractorObserverSpinBox -- Concrete widget observer. // class qtractorObserverSpinBox : public qtractorObserverWidget { Q_OBJECT public: // Constructor. qtractorObserverSpinBox(QWidget *pParent = nullptr); protected: // Visitors overload. void updateValue(float fValue); protected slots: void spinBoxChanged(double value); signals: void valueChanged(float); }; //---------------------------------------------------------------------- // class qtractorObserverSlider -- Concrete widget observer. // class qtractorObserverSlider : public qtractorObserverWidget { Q_OBJECT public: // Constructor. qtractorObserverSlider(QWidget *pParent = nullptr); protected: // Alternate mouse behavior event handlers. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseDoubleClickEvent(QMouseEvent *pMouseEvent); void wheelEvent(QWheelEvent *pWheelEvent); // Visitors overload. void updateValue(float fValue); protected slots: void sliderChanged(int iValue); signals: void valueChanged(float); }; #endif // __qtractorObserverWidget_h // end of qtractorObserverWidget.h qtractor-1.5.11/src/PaxHeaders/qtractorTrackList.cpp0000644000000000000000000000013215124701674017476 xustar0030 mtime=1767080892.803263411 30 atime=1767080892.802263416 30 ctime=1767080892.803263411 qtractor-1.5.11/src/qtractorTrackList.cpp0000644000175000001440000015430015124701674017471 0ustar00rncbcusers// qtractorTrackList.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorTrackList.h" #include "qtractorTrack.h" #include "qtractorTracks.h" #include "qtractorTrackView.h" #include "qtractorTrackCommand.h" #include "qtractorTrackButton.h" #include "qtractorInstrument.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorMidiManager.h" #include "qtractorPluginListView.h" #include "qtractorRubberBand.h" #include "qtractorMainForm.h" #include "qtractorThumbView.h" #include "qtractorMixer.h" #include "qtractorCurve.h" #include "qtractorAudioMonitor.h" #include "qtractorMidiMonitor.h" #include "qtractorAudioMeter.h" #include "qtractorMidiMeter.h" #include "qtractorOptions.h" #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #endif #ifdef CONFIG_GRADIENT #include #endif //---------------------------------------------------------------------------- // qtractorCurveButton -- Track automation curve menu button. class qtractorCurveButton : public QPushButton { // Q_OBJECT public: // Constructor. qtractorCurveButton(qtractorTrack *pTrack, QWidget *pParent) : QPushButton(pParent), m_pTrack(pTrack) { QPushButton::setFocusPolicy(Qt::NoFocus); } // Button state updater. void updateTrack() { qtractorCurveList *pCurveList = m_pTrack->curveList(); if (pCurveList) { QPalette pal; if (pCurveList->isCapture()) { pal.setColor(QPalette::Button, Qt::darkRed); pal.setColor(QPalette::ButtonText, Qt::red); } else if (pCurveList->isProcess()) { pal.setColor(QPalette::Button, Qt::darkGreen); pal.setColor(QPalette::ButtonText, Qt::green); } QPushButton::setPalette(pal); QString sToolTip(QObject::tr("Automation (%1)")); qtractorSubject *pSubject = nullptr; qtractorCurve *pCurrentCurve = pCurveList->currentCurve(); if (pCurrentCurve) pSubject = pCurrentCurve->subject(); if (pSubject) QPushButton::setToolTip(sToolTip.arg(pSubject->name())); else QPushButton::setToolTip(sToolTip.arg(QObject::tr("none"))); } } protected: // Virtual trap to set current track // before showing the automation menu... bool hitButton(const QPoint& pos) const { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) pTracks->trackList()->setCurrentTrack(m_pTrack); } return QPushButton::hitButton(pos); } private: // Instance variables. qtractorTrack *m_pTrack; }; //---------------------------------------------------------------------------- // qtractorTrackList::HeaderModel -- Track-list header model. class qtractorTrackList::HeaderModel : public QAbstractListModel { public: // Constructor. HeaderModel(QObject *pParent = 0); QVariant headerData(int section, Qt::Orientation orient, int role) const; int rowCount(const QModelIndex&) const { return 0; } int columnCount(const QModelIndex&) const { return m_headerText.count(); } QVariant data(const QModelIndex&, int) const { return QVariant(); } private: // Model variables. QStringList m_headerText; }; // Constructor. qtractorTrackList::HeaderModel::HeaderModel ( QObject *pParent ) : QAbstractListModel(pParent) { m_headerText << tr("Nr") << tr("Track Name") << tr("Bus") << tr("Ch") << tr("Patch") << tr("Instrument"); }; // Header model data. QVariant qtractorTrackList::HeaderModel::headerData ( int section, Qt::Orientation orient, int role ) const { if (orient == Qt::Horizontal) { switch (role) { case Qt::DisplayRole: return m_headerText.at(section); case Qt::TextAlignmentRole: if (section == qtractorTrackList::Number || section == qtractorTrackList::Channel) return int(Qt::AlignHCenter | Qt::AlignVCenter); else return int(Qt::AlignLeft | Qt::AlignVCenter); case Qt::SizeHintRole: if (section == qtractorTrackList::Number || section == qtractorTrackList::Channel) return QSize(24, 24); else if (section == qtractorTrackList::Name) return QSize(120, 24); else return QSize(100, 24); } } return QVariant(); } //---------------------------------------------------------------------------- // qtractorTrackListButtons -- Track button layout widget. // Constructor. qtractorTrackListButtons::qtractorTrackListButtons ( qtractorTrack *pTrack, QWidget *pParent ) : QWidget(pParent) { QWidget::setBackgroundRole(QPalette::Window); QHBoxLayout *pHBoxLayout = new QHBoxLayout(); pHBoxLayout->setContentsMargins(2, 2, 2, 2); pHBoxLayout->setSpacing(2); const QFont& font = QWidget::font(); const QFont font2(font.family(), font.pointSize() - 2); const int iFixedHeight = QFontMetrics(font).lineSpacing() + 4; const QSize buttonSize(22, iFixedHeight); m_pRecordButton = new qtractorTrackButton(pTrack, qtractorTrack::Record, this); m_pRecordButton->setFixedSize(buttonSize); m_pRecordButton->setFont(font2); m_pMuteButton = new qtractorTrackButton(pTrack, qtractorTrack::Mute, this); m_pMuteButton->setFixedSize(buttonSize); m_pMuteButton->setFont(font2); m_pSoloButton = new qtractorTrackButton(pTrack, qtractorTrack::Solo, this); m_pSoloButton->setFixedSize(buttonSize); m_pSoloButton->setFont(font2); m_pCurveButton = new qtractorCurveButton(pTrack, this); m_pCurveButton->setFixedSize(QSize(32, iFixedHeight)); m_pCurveButton->setFont(font2); m_pCurveButton->setText("A"); m_pCurveButton->setToolTip(QObject::tr("Automation")); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) m_pCurveButton->setMenu(pMainForm->trackCurveMenu()); pHBoxLayout->addStretch(); pHBoxLayout->addWidget(m_pRecordButton); pHBoxLayout->addWidget(m_pMuteButton); pHBoxLayout->addWidget(m_pSoloButton); pHBoxLayout->addWidget(m_pCurveButton); QWidget::setLayout(pHBoxLayout); } // Refresh color (palette) state buttons void qtractorTrackListButtons::updateTrackButtons (void) { m_pRecordButton->updateTrackButton(); m_pMuteButton->updateTrackButton(); m_pSoloButton->updateTrackButton(); } //---------------------------------------------------------------------------- // qtractorTrackList -- Track list widget. // Local constants. static const char *TracksGroup = "/Tracks"; static const char *TrackListHeaderViewKey = "/TrackListHeaderView"; // Constructor. qtractorTrackList::qtractorTrackList ( qtractorTracks *pTracks, QWidget *pParent ) : qtractorScrollView(pParent) { m_pTracks = pTracks; m_iCurrentTrack = -1; m_dragState = DragNone; m_iDragTrack = -1; m_iDragY = 0; m_pRubberBand = nullptr; m_iUpdateContents = 0; m_pPixmap[IconAudio] = new QPixmap(QIcon::fromTheme("trackAudio").pixmap(16, 16)); m_pPixmap[IconMidi] = new QPixmap(QIcon::fromTheme("trackMidi").pixmap(16, 16)); // Allocate local header. m_pHeader = new QHeaderView(Qt::Horizontal, qtractorScrollView::viewport()); m_pHeader->setModel(new HeaderModel(this)); m_pHeader->setHighlightSections(false); m_pHeader->setStretchLastSection(true); m_pHeader->setSortIndicatorShown(false); // Default section sizes... const int iColCount = m_pHeader->count() - 1; for (int iCol = 0; iCol < iColCount; ++iCol) m_pHeader->resizeSection(iCol, m_pHeader->sectionSizeHint(iCol)); // qtractorScrollView::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); qtractorScrollView::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); qtractorScrollView::viewport()->setFocusPolicy(Qt::ClickFocus); // qtractorScrollView::viewport()->setFocusProxy(this); qtractorScrollView::viewport()->setAcceptDrops(true); // qtractorScrollView::setDragAutoScroll(false); qtractorScrollView::setMouseTracking(true); const QFont& font = qtractorScrollView::font(); qtractorScrollView::setFont(QFont(font.family(), font.pointSize() - 1)); // Load header section (column) sizes... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { QSettings& settings = pOptions->settings(); settings.beginGroup(TracksGroup); const QByteArray& aHeaderView = settings.value(TrackListHeaderViewKey).toByteArray(); if (!aHeaderView.isEmpty()) m_pHeader->restoreState(aHeaderView); settings.endGroup(); } // Enforce this no matter what... m_pHeader->setMinimumSectionSize(24); QObject::connect(m_pHeader, SIGNAL(sectionResized(int,int,int)), SLOT(updateHeaderSize(int,int,int))); QObject::connect(m_pHeader, SIGNAL(sectionHandleDoubleClicked(int)), SLOT(resetHeaderSize(int))); // QObject::connect(this, SIGNAL(contentsMoving(int,int)), // this, SLOT(updatePixmap(int,int))); } // Destructor. qtractorTrackList::~qtractorTrackList (void) { // Save header section (column) sizes... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { QSettings& settings = pOptions->settings(); settings.beginGroup(TracksGroup); settings.setValue(TrackListHeaderViewKey, m_pHeader->saveState()); settings.endGroup(); } delete m_pPixmap[IconAudio]; delete m_pPixmap[IconMidi]; } // Main tracks widget accessor. qtractorTracks *qtractorTrackList::tracks (void) const { return m_pTracks; } // Local header view accessor. QHeaderView *qtractorTrackList::header (void) const { return m_pHeader; } // Track-list model item constructor qtractorTrackList::Item::Item ( qtractorTrack *pTrack ) : track(pTrack), flags(0), buttons(nullptr), plugins(nullptr), meters(nullptr) { const QString s; text << track->trackName() << s << s << s << s; } // Track-list model item destructor qtractorTrackList::Item::~Item (void) { if (meters) delete meters; if (plugins) delete plugins; if (buttons) delete buttons; } // Track-list model item bank/program names helper. bool qtractorTrackList::Item::updateBankProgNames ( qtractorMidiManager *pMidiManager, const QString& sInstrumentName, QString& sBankName, QString& sProgName ) const { if (pMidiManager == nullptr) return false; const qtractorInstrumentList& instruments = pMidiManager->instruments(); if (!instruments.contains(sInstrumentName)) return false; const int iBank = track->midiBank(); const int iProg = track->midiProg(); const qtractorInstrument& instr = instruments.value(sInstrumentName); const qtractorInstrumentData& bank = instr.patch(iBank); if (bank.contains(iProg)) { sBankName = bank.name(); sProgName = QString("%1 - %2").arg(iProg).arg(bank[iProg]); } return true; } // Track-list model item cache updater. void qtractorTrackList::Item::updateItem ( qtractorTrackList *pTrackList ) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; text.clear(); // Default initialization? if (track == nullptr) return; updateIcon(pTrackList); text << track->trackName(); const QString s = " - -"; QString sBusText = track->inputBusName(); if (track->inputBusName() != track->outputBusName()) sBusText += '/' + track->outputBusName(); switch (track->trackType()) { case qtractorTrack::Audio: { // Audio Bus name... text << sBusText; // + '\n' + QObject::tr("Audio"); // Audio channels... qtractorAudioBus *pAudioBus = static_cast (track->outputBus()); text << (pAudioBus ? QString::number(pAudioBus->channels()) : s.right(1)); // Fillers... text << s << s; ribbon = qtractorAudioMeter::color(qtractorAudioMeter::Color10dB); break; } case qtractorTrack::Midi: { // MIDI Bus name... if (track->pluginList()) { qtractorMidiManager *pMidiManager = (track->pluginList())->midiManager(); if (pMidiManager && pMidiManager->isAudioOutputMonitor() && pMidiManager->audioOutputBus()) { sBusText += '\n'; sBusText += (pMidiManager->audioOutputBus())->busName(); } } text << sBusText; // + '\n' + QObject::tr("MIDI"); qtractorMidiBus *pMidiBus = static_cast (track->outputBus()); // MIDI channels... QString sOmni; if (track->isMidiOmni()) sOmni += '*'; const unsigned short iChannel = track->midiChannel(); text << sOmni + QString::number(iChannel + 1); // Care of MIDI instrument, program and bank numbers vs.names... QString sInstrumentName; QString sProgName; QString sBankName; const int iProg = track->midiProg(); if (iProg >= 0) sProgName = QString::number(iProg + 1); sProgName += s; const int iBank = track->midiBank(); if (iBank >= 0) sBankName = QString::number(iBank); if (pMidiBus) { const qtractorMidiBus::Patch& patch = pMidiBus->patch(iChannel); if (patch.instrumentName.isEmpty()) sInstrumentName = pMidiBus->instrumentName(); else sInstrumentName = patch.instrumentName; if (!sInstrumentName.isEmpty()) { bool bMidiManager = updateBankProgNames( (track->pluginList())->midiManager(), sInstrumentName, sBankName, sProgName); if (!bMidiManager && pMidiBus->pluginList_out()) { bMidiManager = updateBankProgNames( (pMidiBus->pluginList_out())->midiManager(), sInstrumentName, sBankName, sProgName); } if (!bMidiManager) { qtractorInstrumentList *pInstruments = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pInstruments = pSession->instruments(); if (pInstruments && pInstruments->contains(sInstrumentName)) { const qtractorInstrument& instr = pInstruments->value(sInstrumentName); const qtractorInstrumentData& bank = instr.patch(iBank); if (bank.contains(iProg)) { sProgName = bank[iProg]; sBankName = bank.name(); } } } } } // Trade (No instrument) for dashes... if (sInstrumentName.isEmpty()) sInstrumentName = s; // This is it, MIDI Patch/Bank... text << sProgName + '\n' + sBankName << sInstrumentName; ribbon = qtractorMidiMeter::color(qtractorMidiMeter::ColorOver); break; } case qtractorTrack::None: default: { text << s + '\n' + QObject::tr("Unknown") << s << s << s; break; } } if (buttons) buttons->curveButton()->updateTrack(); } // Track-list model item buttons updater. void qtractorTrackList::Item::updateButtons ( qtractorTrackList *pTrackList, bool bVisible ) { if (bVisible && buttons == nullptr) { buttons = new qtractorTrackListButtons(track, pTrackList->viewport()); buttons->curveButton()->updateTrack(); buttons->lower(); } else if (!bVisible && buttons) { // buttons->hide(); delete buttons; buttons = nullptr; } } // Track-list model item plugins updater. void qtractorTrackList::Item::updatePlugins ( qtractorTrackList *pTrackList, bool bVisible ) { if (bVisible && plugins == nullptr) { const QFont& font = pTrackList->font(); plugins = new qtractorPluginListView(pTrackList->viewport()); plugins->setFont(QFont(font.family(), font.pointSize() - 2)); plugins->setTinyScrollBar(true); plugins->setPluginList(track->pluginList()); plugins->lower(); } else if (!bVisible && plugins) { // plugins->hide(); delete plugins; plugins = nullptr; } } // Track-list model item meters updater. void qtractorTrackList::Item::updateMeters ( qtractorTrackList *pTrackList, bool bVisible ) { if (bVisible && meters == nullptr) { switch (track->trackType()) { case qtractorTrack::Audio: { // Re-create the audio meters... qtractorAudioMonitor *pAudioMonitor = static_cast (track->monitor()); if (pAudioMonitor) { const int iAudioChannels = pAudioMonitor->channels(); if (iAudioChannels > 0) { const int iColWidth = pTrackList->header()->sectionSize(Channel); const int iMinWidth = iColWidth / iAudioChannels - 1; if (iMinWidth > 3) { meters = new qtractorAudioMeter( pAudioMonitor, pTrackList->viewport()); meters->lower(); } } } break; } case qtractorTrack::Midi: { // Re-create the MIDI meters... qtractorMidiMonitor *pMidiMonitor = static_cast (track->monitor()); if (pMidiMonitor) { qtractorMidiComboMeter *pMidiComboMeter = new qtractorMidiComboMeter( pMidiMonitor, pTrackList->viewport()); qtractorMidiManager *pMidiManager = (track->pluginList())->midiManager(); if (pMidiManager && pMidiManager->isAudioOutputMonitor()) { pMidiComboMeter->setAudioOutputMonitor( pMidiManager->audioOutputMonitor()); } meters = pMidiComboMeter; meters->lower(); } break; } case qtractorTrack::None: default: break; } } else if (!bVisible && meters) { // meters->hide(); delete meters; meters = nullptr; } } // Track-list model item icon updater. void qtractorTrackList::Item::updateIcon ( qtractorTrackList *pTrackList ) { const QString& sTrackIcon = track->trackIcon(); QIcon trackIcon = QIcon::fromTheme(sTrackIcon); if (trackIcon.isNull()) trackIcon = QIcon(sTrackIcon); if (!trackIcon.isNull()) { const int h0 = track->zoomHeight() - 4; // Account for track nr. const int h1 = (h0 < qtractorTrack::HeightMin ? h0 : h0 - 12); const int w0 = pTrackList->header()->sectionSize(Number) - 4; const int w1 = (w0 < h1 ? w0 : h1); icon = trackIcon.pixmap(w1); } else { icon = QPixmap(); // null pixmap. } } // Find the list view item from track pointer reference. int qtractorTrackList::trackRow ( qtractorTrack *pTrack ) const { int iTrack = 0; QListIterator iter(m_items); while (iter.hasNext()) { if (iter.next()->track == pTrack) return iTrack; ++iTrack; } return -1; } // Find track row of given contents point... int qtractorTrackList::trackRowAt ( const QPoint& pos ) { const int y = pos.y() - m_pHeader->sizeHint().height(); int y1, y2; y1 = y2 = 0; int iTrack = 0; QListIterator iter(m_items); while (iter.hasNext() && y2 < qtractorScrollView::contentsHeight()) { y1 = y2; y2 += (iter.next()->track)->zoomHeight(); if (y >= y1 && y < y2) return iTrack; ++iTrack; } return -1; } // Find track column of given contents point... int qtractorTrackList::trackColumnAt ( const QPoint& pos ) { const int y = pos.y() - m_pHeader->sizeHint().height(); if (y > 0 && y < qtractorScrollView::contentsHeight()) { const int x = pos.x(); int x1, x2; x1 = x2 = 0; const int iColCount = m_pHeader->count(); for (int iCol = 0; iCol < iColCount; ++iCol) { x1 = x2; x2 += m_pHeader->sectionSize(iCol); if (x > x1 && x < x2) return iCol; } } return -1; } // Find the track pointer reference from list view item row. qtractorTrack *qtractorTrackList::track ( int iTrack ) const { if (iTrack < 0 || iTrack >= m_items.count()) return nullptr; return m_items.at(iTrack)->track; } // Retrive the given track row rectangular (in contents coordinates). QRect qtractorTrackList::trackRect ( int iTrack ) const { QRect rect; if (iTrack >= 0 && iTrack < m_items.count()) { rect.setX(0); rect.setWidth(qtractorScrollView::viewport()->width()); int y1, y2; y1 = y2 = m_pHeader->sizeHint().height(); QListIterator iter(m_items); while (iter.hasNext()) { y1 = y2; y2 += (iter.next()->track)->zoomHeight(); if (iTrack == 0) { rect.setY(y1); rect.setHeight(y2 - y1); break; } --iTrack; } } return rect; } // Insert a track item; return actual track row added. int qtractorTrackList::insertTrack ( int iTrack, qtractorTrack *pTrack ) { clearSelect(); if (iTrack < 0) iTrack = m_items.count(); Item *pItem = new Item(pTrack); m_items.insert(iTrack, pItem); m_tracks.insert(pTrack, pItem); return iTrack; } // Remove a track item; return remaining track row. int qtractorTrackList::removeTrack ( int iTrack ) { if (iTrack < 0 || iTrack >= m_items.count()) return -1; clearSelect(); if (m_select.contains(iTrack)) m_select.remove(iTrack); Item *pItem = m_items.at(iTrack); m_tracks.remove(pItem->track); m_items.removeAt(iTrack); delete pItem; m_iCurrentTrack = -1; const int iTrackCount = m_items.count(); return (iTrack < iTrackCount ? iTrack : iTrackCount - 1); } // Manage current track row by index. void qtractorTrackList::setCurrentTrackRow ( int iTrack ) { int iCurrentTrack = m_iCurrentTrack; if (iTrack < 0 || iTrack >= m_items.count()) iCurrentTrack = -1; else iCurrentTrack = iTrack; if (iCurrentTrack == m_iCurrentTrack) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorTrackList::setCurrentTrackRow(%d)", iCurrentTrack); #endif m_iCurrentTrack = iCurrentTrack; if (m_iCurrentTrack < 0) clearSelect(); // Make sure the new current track is visible... if (!ensureVisibleRect(trackRect(m_iCurrentTrack))) updateContents(); emit selectionChanged(); } int qtractorTrackList::currentTrackRow (void) const { return m_iCurrentTrack; } int qtractorTrackList::trackRowCount (void) const { return m_items.count(); } // Current selected track reference. void qtractorTrackList::setCurrentTrack ( qtractorTrack *pTrack ) { setCurrentTrackRow(trackRow(pTrack)); } qtractorTrack *qtractorTrackList::currentTrack (void) const { if (m_iCurrentTrack < 0 || m_iCurrentTrack >= m_items.count()) return nullptr; return m_items.at(m_iCurrentTrack)->track; } // Find the list view item from track pointer reference. void qtractorTrackList::updateTrack ( qtractorTrack *pTrack ) { Item *pItem = m_tracks.value(pTrack, nullptr); if (pItem) { // pItem->updateButtons(this, false); pItem->updatePlugins(this, false); pItem->updateMeters(this, false); pItem->updateItem(this); } updateContents(); } // Update the list view item from MIDI manager pointer reference. void qtractorTrackList::updateMidiTrackItem ( qtractorMidiManager *pMidiManager ) { QListIterator iter(m_items); while (iter.hasNext()) { Item *pItem = iter.next(); qtractorTrack *pTrack = pItem->track; if (pTrack && pTrack->trackType() == qtractorTrack::Midi) { qtractorPluginList *pPluginList = pTrack->pluginList(); if (pPluginList && pPluginList->midiManager() == pMidiManager) { qtractorMidiComboMeter *pMidiComboMeter = static_cast (pItem->meters); if (pMidiComboMeter) { if (pMidiManager->isAudioOutputMonitor()) { pMidiComboMeter->setAudioOutputMonitor( pMidiManager->audioOutputMonitor()); } else { pMidiComboMeter->setAudioOutputMonitor(nullptr); } } pItem->updateItem(this); updateContents(); break; } } } } // Track-button colors (palette) update. void qtractorTrackList::updateTrackButtons (void) { QListIterator iter(m_items); while (iter.hasNext()) { const Item *pItem = iter.next(); if (pItem->buttons) pItem->buttons->updateTrackButtons(); } } // Update all track-items/icons methods. void qtractorTrackList::updateItems (void) { QListIterator iter(m_items); while (iter.hasNext()) iter.next()->updateItem(this); // updateContents(); } void qtractorTrackList::updateIcons (void) { QListIterator iter(m_items); while (iter.hasNext()) iter.next()->updateIcon(this); // updateContents(); } // Main table cleaner. void qtractorTrackList::clear (void) { m_select.clear(); m_iCurrentTrack = -1; m_dragState = DragNone; m_iDragTrack = -1; m_iDragY = 0; if (m_pRubberBand) delete m_pRubberBand; m_pRubberBand = nullptr; qDeleteAll(m_items); m_items.clear(); m_tracks.clear(); } // Update all tracks item height. void qtractorTrackList::updateContentsHeight (void) { // Remember to give some room to drop something at the bottom... int iContentsHeight = m_pHeader->sizeHint().height() + (qtractorTrack::HeightMin << 2); QListIterator iter(m_items); while (iter.hasNext()) { qtractorTrack *pTrack = iter.next()->track; pTrack->updateZoomHeight(); iContentsHeight += pTrack->zoomHeight(); } qtractorScrollView::resizeContents( qtractorScrollView::contentsWidth(), iContentsHeight); // Update track view total contents height... qtractorTrackView *pTrackView = m_pTracks->trackView(); pTrackView->updateContentsHeight(); pTrackView->updateContents(); updateContents(); } // Rectangular contents update. void qtractorTrackList::updateContents ( const QRect& rect ) { if (m_iUpdateContents > 0) return; ++m_iUpdateContents; updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(rect); --m_iUpdateContents; } // Overall contents update. void qtractorTrackList::updateContents (void) { if (m_iUpdateContents > 0) return; ++m_iUpdateContents; updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(); --m_iUpdateContents; } // Resize event handler. void qtractorTrackList::resizeEvent ( QResizeEvent *pResizeEvent ) { qtractorScrollView::resizeEvent(pResizeEvent); updateHeader(); } // Check/update header resize. void qtractorTrackList::updateHeaderSize ( int iCol, int, int iColSize ) { const bool bBlockSignals = m_pHeader->blockSignals(true); if (iCol == Name && iColSize < 110) { // Make sure this column stays legible... m_pHeader->resizeSection(iCol, 110); } else if (iCol == Number) { // Resize all icons anyway... updateIcons(); } else if (iCol == Channel) { // Reset all meter sizes anyway... updateItems(); } m_pHeader->blockSignals(bBlockSignals); updateHeader(); } // Reset header extents. void qtractorTrackList::resetHeaderSize ( int iCol ) { const bool bBlockSignals = m_pHeader->blockSignals(true); const int iColSize = m_pHeader->sectionSizeHint(iCol); // = m_pHeader->model()->headerData(iCol, // Qt::Horizontal, Qt::SizeHintRole).toSize().width(); m_pHeader->resizeSection(iCol, iColSize); if (iCol == Number) { // Resize all icons anyway... QListIterator iter(m_items); while (iter.hasNext()) iter.next()->updateIcon(this); } m_pHeader->blockSignals(bBlockSignals); updateHeader(); } // Update header extents. void qtractorTrackList::updateHeader (void) { // Find out wich is the largest header width // and enforce to let it know it like so... const int iColCount = m_pHeader->count() - 1; int iContentsWidth = (qtractorScrollView::viewport()->width() >> 1); for (int iCol = 0; iCol < iColCount; ++iCol) iContentsWidth += m_pHeader->sectionSize(iCol); m_pHeader->setFixedWidth(iContentsWidth); qtractorScrollView::resizeContents( iContentsWidth, qtractorScrollView::contentsHeight()); updateContents(); } // Selection method. void qtractorTrackList::selectTrack ( int iTrack, bool bSelect, bool bToggle ) { Item *pItem = m_items.at(iTrack); if (m_select.contains(iTrack)) { const unsigned int flags = pItem->flags; if ( (!bSelect && (flags & 2) == 0) || (( bSelect && (flags & 3) == 3) && bToggle)) pItem->flags &= ~1; else if ( ( bSelect && (flags & 2) == 0) || ((!bSelect && (flags & 3) == 2) && bToggle)) pItem->flags |= 1; } else if (bSelect) { pItem->flags = 1; m_select.insert(iTrack, pItem); } } // Selection commit method. void qtractorTrackList::updateSelect ( bool bCommit ) { // Remove unselected... QHash::Iterator iter = m_select.begin(); const QHash::Iterator& iter_end = m_select.end(); while (iter != iter_end) { Item *pItem = iter.value(); if (bCommit) { if (pItem->flags & 1) pItem->flags |= 2; else pItem->flags &= ~2; } if ((pItem->flags & 3) == 0) iter = m_select.erase(iter); else ++iter; } updateContents(); } // Selection clear method. void qtractorTrackList::clearSelect (void) { int iUpdate = 0; // Clear all selected... QHash::ConstIterator iter = m_select.constBegin(); const QHash::ConstIterator& iter_end = m_select.constEnd(); for ( ; iter != iter_end; ++iter) { iter.value()->flags = 0; ++iUpdate; } m_select.clear(); if (iUpdate > 0) updateContents(); } // Retrieve all current seleceted tracks but one. QList qtractorTrackList::selectedTracks ( qtractorTrack *pTrackEx, bool bAllTracks ) const { QList tracks; // Grab current selected tracks; also check // whether given track is currently selected... bool bSelected = false; QHash::ConstIterator iter = m_select.constBegin(); const QHash::ConstIterator& iter_end = m_select.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorTrack *pTrack = iter.value()->track; if (pTrackEx == pTrack) bSelected = true; else tracks.append(pTrack); } // Current selection is either empty or // given track is not currently selected... if (!bSelected) { tracks.clear(); // Optionally apply to all tracks if (bAllTracks) { QListIterator iter(m_items); while (iter.hasNext()) { qtractorTrack *pTrack = iter.next()->track; if (pTrackEx != pTrack) tracks.append(pTrack); } } } return tracks; } // Draw table cell. void qtractorTrackList::drawCell ( QPainter *pPainter, int iRow, int iCol, const QRect& rect ) const { const QPalette& pal = qtractorScrollView::palette(); const Item *pItem = m_items.at(iRow); QColor bg, fg; if (iCol == Number) { bg = (pItem->track)->foreground().lighter(); fg = (pItem->track)->background().lighter(); if (qAbs(bg.value() - fg.value()) < 0x33) fg.setHsv(fg.hue(), fg.saturation(), (255 - fg.value()), 200); } else if (pItem->flags & 1) { bg = pal.highlight().color(); fg = pal.highlightedText().color(); if (m_iCurrentTrack == iRow) bg = bg.darker(140); } else if (m_iCurrentTrack == iRow) { bg = pal.midlight().color().darker(160); fg = pal.highlightedText().color(); } else { bg = pal.window().color(); fg = pal.windowText().color(); } // Draw text and decorations if any... QRect rectText(rect.topLeft() + QPoint(4, 4), rect.size() - QSize(8, 8)); #ifdef CONFIG_GRADIENT QLinearGradient grad(0, rect.top(), 0, rect.bottom()); grad.setColorAt(0.4, bg); grad.setColorAt(1.0, bg.darker(120)); pPainter->fillRect(rect, grad); #else pPainter->fillRect(rect, bg); #endif pPainter->setPen(fg); if (iCol == Number) { if (pItem->icon.isNull() || rect.height() > qtractorTrack::HeightMin + 4) { pPainter->drawText(rectText, Qt::AlignHCenter | Qt::AlignTop, QString::number(iRow + 1)); } if (!pItem->icon.isNull()) { const int x = rect.left() + ((rect.width() - pItem->icon.width()) >> 1); const int y = rect.bottom() - (pItem->icon.height() + 2); pPainter->drawPixmap(x, y, pItem->icon) ; } } else if (iCol == Channel) { if ((pItem->track)->trackType() == qtractorTrack::Midi || rect.height() < qtractorTrack::HeightMin + 4) { pPainter->drawText(rectText, Qt::AlignHCenter | Qt::AlignTop, pItem->text.at(iCol - 1)); } } else if (iCol == Bus) { pPainter->fillRect( // ribbon filler... rect.x() + 2, rect.y() + 2, 4, rect.height() - 4, pItem->ribbon); rectText.setX(rectText.x() + 6); // ribbon spacing... const QPixmap *pPixmap = nullptr; switch ((pItem->track)->trackType()) { case qtractorTrack::Audio: pPixmap = m_pPixmap[IconAudio]; break; case qtractorTrack::Midi: pPixmap = m_pPixmap[IconMidi]; break; case qtractorTrack::None: default: break; } if (pPixmap) { pPainter->drawPixmap(rectText.x(), rectText.y(), *pPixmap); if ((pItem->track)->trackType() == qtractorTrack::Midi && (pItem->track)->pluginList()) { qtractorMidiManager *pMidiManager = ((pItem->track)->pluginList())->midiManager(); if (pMidiManager && pMidiManager->isAudioOutputMonitor()) { const int h = QFontMetrics(pPainter->font()).height(); pPainter->drawPixmap( rectText.x(), rectText.y() + h, *m_pPixmap[IconAudio]); } } rectText.setLeft(rectText.left() + pPixmap->width() + 4); } if (pItem->plugins) { QPalette pal2(pal); if (m_iCurrentTrack == iRow) { const QColor& rgbBase = pal2.midlight().color(); pal2.setColor(QPalette::WindowText, pal2.highlightedText().color()); pal2.setColor(QPalette::Window, rgbBase.darker(150)); } else { const QColor& rgbBase = pal2.window().color(); pal2.setColor(QPalette::WindowText, pal2.windowText().color()); pal2.setColor(QPalette::Window, rgbBase); } pItem->plugins->setPalette(pal2); } pPainter->drawText(rectText, Qt::AlignLeft | Qt::AlignTop, pItem->text.at(iCol - 1)); } else { pPainter->drawText(rectText, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, pItem->text.at(iCol - 1)); } // Do some simple embossing... pPainter->setPen(bg.lighter(120)); pPainter->drawLine(rect.left(), rect.top(), rect.left(), rect.bottom()); pPainter->drawLine(rect.left(), rect.top(), rect.right(), rect.top()); pPainter->setPen(bg.darker(120)); pPainter->drawLine(rect.right(), rect.top(), rect.right(), rect.bottom()); pPainter->drawLine(rect.left(), rect.bottom(), rect.right(), rect.bottom()); } // (Re)create the complete view pixmap. void qtractorTrackList::updatePixmap ( int cx, int cy ) { QWidget *pViewport = qtractorScrollView::viewport(); const int w = pViewport->width(); const int h = pViewport->height(); if (w < 1 || h < 1) return; const QPalette& pal = qtractorScrollView::palette(); m_pixmap = QPixmap(w, h); m_pixmap.fill(pal.window().color()); QPainter painter(&m_pixmap); // painter.initFrom(pViewport); painter.setFont(qtractorScrollView::font()); // Update actual contents size... m_pHeader->setOffset(cx); // Draw all cells... const int iColCount = m_pHeader->count(); const int hh = m_pHeader->sizeHint().height(); // Account for the item dropping headroom... const int ch = qtractorScrollView::contentsHeight() - (qtractorTrack::HeightMin << 2); int x, y1, y2, h1; y1 = y2 = 0; int iTrack = 0; QListIterator iter(m_items); while (iter.hasNext()) { Item *pItem = iter.next(); h1 = (pItem->track)->zoomHeight(); y1 = y2; y2 += h1; if (y2 > cy && y1 < cy + h) { // Dispatch to paint this track... x = 0; QRect rect(x, y1 - cy + hh, w, h1); for (int iCol = 0; iCol < iColCount; ++iCol) { const int dx = m_pHeader->sectionSize(iCol); if (x + dx > cx && x < cx + w) { rect.setX(x - cx); rect.setWidth(dx); if (iCol == Name) { const int h2 = qtractorTrack::HeightMin - 4; pItem->updateButtons(this, rect.height() > h2); if (pItem->buttons) { const QSize sizeButtons = (pItem->buttons)->sizeHint(); QRect rectButtons = rect; rectButtons.setTop(rect.bottom() - sizeButtons.height()); rectButtons.setLeft(rect.right() - sizeButtons.width()); (pItem->buttons)->setGeometry(rectButtons); (pItem->buttons)->show(); } } else if (iCol == Bus) { const int h2 = (qtractorTrack::HeightMin << 2) - qtractorTrack::HeightMin - 2; pItem->updatePlugins(this, rect.height() > h2); if (pItem->plugins) { const int dy1 = (qtractorTrack::HeightMin << 1); (pItem->plugins)->setGeometry( rect.adjusted(+10, dy1, -4, -2)); (pItem->plugins)->show(); } } else if (iCol == Channel) { const int h2 = qtractorTrack::HeightMin + 4; pItem->updateMeters(this, rect.height() > h2); if (pItem->meters) { const int dy1 = ((pItem->track)->trackType() == qtractorTrack::Midi ? 20 : 4); (pItem->meters)->setGeometry( rect.adjusted(+4, dy1, -3, -2)); (pItem->meters)->show(); } } // Paint item cell... drawCell(&painter, iTrack, iCol, rect); } else if (iCol == Name) pItem->updateButtons(this, false); else if (iCol == Bus) pItem->updatePlugins(this, false); else if (iCol == Channel) pItem->updateMeters(this, false); x += dx; } } else { // Just hide all children... pItem->updateButtons(this, false); pItem->updatePlugins(this, false); pItem->updateMeters(this, false); } ++iTrack; } if (cy + h > ch) { painter.setPen(pal.mid().color()); painter.drawLine(0, ch - cy, w, ch - cy); painter.fillRect(0, ch - cy + 1, w, cy + h - ch, pal.dark().color()); } } // Draw the time scale. void qtractorTrackList::drawContents ( QPainter *pPainter, const QRect& rect ) { pPainter->drawPixmap(rect, m_pixmap, rect); } // To have keyline in v-sync with main view. void qtractorTrackList::contentsYMovingSlot ( int /*cx*/, int cy ) { if (qtractorScrollView::contentsY() != cy) qtractorScrollView::setContentsPos(qtractorScrollView::contentsX(), cy); } // Context menu event handler. void qtractorTrackList::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { // We'll need a reference for issuing commands... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { pMainForm->stabilizeForm(); pMainForm->trackMenu()->exec(pContextMenuEvent->globalPos()); } } // Handle mouse double-clicks. void qtractorTrackList::mouseDoubleClickEvent ( QMouseEvent *pMouseEvent ) { qtractorTrack *pTrack = nullptr; if (pMouseEvent->button() == Qt::LeftButton) { const QPoint& pos = qtractorScrollView::viewportToContents(pMouseEvent->pos()); const int iTrack = trackRowAt(pos); if (iTrack >= 0 && trackColumnAt(pos) == Number) { const QRect& rect = trackRect(iTrack); if (iTrack > 0 && pos.y() >= rect.top() && pos.y() < rect.top() + 4) pTrack = track(iTrack - 1); else if (pos.y() >= rect.bottom() - 4 && pos.y() < rect.bottom() + 4) pTrack = track(iTrack); } } // Avoid lingering mouse press/release states... resetDragState(); // Do we reset track height or go for its properties? if (pTrack) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { const int iZoomHeight = pTrack->zoomHeightBase(); pSession->execute( new qtractorResizeTrackCommand(pTrack, iZoomHeight)); } } else { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->trackProperties(); } } // Handle item selection/dragging -- mouse button press. void qtractorTrackList::mousePressEvent ( QMouseEvent *pMouseEvent ) { // Now set ready for drag something... const QPoint& pos = qtractorScrollView::viewportToContents(pMouseEvent->pos()); // Select current track... const int iTrack = trackRowAt(pos); // Make it current anyway... setCurrentTrackRow(iTrack); if (pMouseEvent->button() == Qt::LeftButton) { // Try for drag-move/resize/select later... m_dragState = DragStart; m_posDrag = pos; // Look for the mouse hovering around some item boundary... if (iTrack >= 0) { // Make current row always selected... const Qt::KeyboardModifiers& modifiers = pMouseEvent->modifiers(); if ((modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) == 0) { clearSelect(); } else { selectTrack(iTrack, true, (modifiers & Qt::ControlModifier)); updateSelect(true); } if (m_iDragTrack >= 0) { // Most probably DragResize is next... qtractorScrollView::setCursor(QCursor(Qt::SplitVCursor)); } else { // Most probably DragMove is next... qtractorScrollView::setCursor(QCursor(Qt::PointingHandCursor)); m_iDragTrack = iTrack; m_iDragY = 0; } } } // qtractorScrollView::mousePressEvent(pMouseEvent); } // Handle item selection/dragging -- mouse pointer move. void qtractorTrackList::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { const QPoint& pos = qtractorScrollView::viewportToContents(pMouseEvent->pos()); // We're already on some item dragging/resizing?... switch (m_dragState) { case DragMove: // Currently moving an item... if (m_iDragTrack >= 0) { const int iTrack = trackRowAt(pos); if (iTrack >= 0) { const QRect& rect = trackRect(iTrack); m_posDrag = rect.topLeft(); moveRubberBand(m_posDrag); ensureVisibleRect(rect); } else { const QRect& rect = trackRect(m_items.count() - 1); m_posDrag = rect.bottomLeft(); moveRubberBand(m_posDrag); } } break; case DragResize: // Currently resizing an item... if (m_iDragTrack >= 0) { qtractorSession *pSession = qtractorSession::getInstance(); const int iVerticalZoom = (pSession ? pSession->verticalZoom() : 100); const int iZoomHeightMin = (qtractorTrack::HeightMin * iVerticalZoom) / 100; int y = pos.y(); if (y < m_iDragY + iZoomHeightMin) y = m_iDragY + iZoomHeightMin; m_posDrag.setY(y); moveRubberBand(m_posDrag); // Go for it, immediately... qtractorTrack *pTrack = track(m_iDragTrack); if (pTrack) { const int iZoomHeight = y - m_iDragY; pTrack->setZoomHeight(iZoomHeight); Item *pItem = m_tracks.value(pTrack, nullptr); if (pItem) { pItem->updateIcon(this); pItem->updateButtons(this, true); pItem->updatePlugins(this, true); pItem->updateMeters(this, true); } m_pTracks->trackView()->updateContents(); updateContentsHeight(); } } break; case DragSelect: { // Currently selecting an group of items... if (m_iDragTrack >= 0 && m_iCurrentTrack >= 0) { const int iTrack = trackRowAt(pos); ensureVisibleRect(trackRect(iTrack)); if (iTrack >= 0 && m_iDragTrack != iTrack) { const Qt::KeyboardModifiers& modifiers = pMouseEvent->modifiers(); const bool bToggle = (modifiers & Qt::ControlModifier); if ((modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) == 0) selectTrack(m_iCurrentTrack, true, bToggle); if (iTrack < m_iCurrentTrack) { for (int i = iTrack; i < m_iCurrentTrack; ++i) selectTrack(i, true, bToggle); } else if (iTrack > m_iCurrentTrack) { for (int i = iTrack; i > m_iCurrentTrack; --i) selectTrack(i, true, bToggle); } if ((m_iDragTrack > iTrack && m_iDragTrack > m_iCurrentTrack) || (m_iDragTrack < iTrack && m_iDragTrack < m_iCurrentTrack)) { const bool bSelect = !m_select.contains(m_iDragTrack); selectTrack(m_iDragTrack, bSelect, bToggle); } // selectTrack(iTrack, true, bToggle); updateSelect(false); m_iDragTrack = iTrack; } } // Update the rubber-band anyway... moveRubberBand(QRect(m_posDrag, pos)); break; } case DragStart: // About to start dragging an item... if ((m_posDrag - pos).manhattanLength() > QApplication::startDragDistance()) { if (m_iDragTrack >= 0) { if (trackColumnAt(pos) == Number) { qtractorScrollView::setCursor(QCursor(Qt::SizeVerCursor)); const QRect& rect = trackRect(m_iDragTrack); if (m_iDragTrack > 0 && m_posDrag.y() >= rect.top() && m_posDrag.y() < rect.top() + 4) { m_dragState = DragResize; m_posDrag = rect.topLeft(); --m_iDragTrack; } else if (m_posDrag.y() >= rect.bottom() - 4 && m_posDrag.y() < rect.bottom() + 4) { m_dragState = DragResize; m_posDrag = rect.bottomLeft(); } else { m_dragState = DragMove; m_posDrag = rect.topLeft(); } moveRubberBand(m_posDrag); } else { qtractorScrollView::setCursor(QCursor(Qt::CrossCursor)); m_dragState = DragSelect; moveRubberBand(QRect(m_posDrag, pos)); } // Special case if one wishes to undo a track's height... if (m_dragState == DragResize) { qtractorTrack *pTrack = track(m_iDragTrack); qtractorSession *pSession = qtractorSession::getInstance(); if (pTrack && pSession) { const int iZoomHeight = pTrack->zoomHeight(); pSession->commands()->push( new qtractorResizeTrackCommand(pTrack, iZoomHeight)); m_pTracks->dirtyChangeNotify(); } } } } break; case DragNone: { // Look for the mouse hovering around first column item boundary... int iTrack = trackRowAt(pos); if (iTrack >= 0 && trackColumnAt(pos) == Number) { m_posDrag = pos; const QRect& rect = trackRect(iTrack); if (pos.y() >= rect.top() && pos.y() < rect.top() + 4) { if (--iTrack >= 0) { if (m_iDragTrack < 0) qtractorScrollView::setCursor(QCursor(Qt::SplitVCursor)); m_iDragTrack = iTrack; m_iDragY = trackRect(iTrack).top(); break; } } else if (pos.y() >= rect.bottom() - 4 && pos.y() < rect.bottom() + 4) { if (m_iDragTrack < 0) qtractorScrollView::setCursor(QCursor(Qt::SplitVCursor)); m_iDragTrack = iTrack; m_iDragY = rect.top(); break; } } // If something has been going on, turn it off... if (m_iDragTrack >= 0) { qtractorScrollView::unsetCursor(); m_iDragTrack = -1; m_iDragY = 0; } // Fall thru... } default: break; } // qtractorScrollView::mouseMoveEvent(pMouseEvent); } // Handle item selection/dragging -- mouse button release. void qtractorTrackList::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { // qtractorScrollView::mouseReleaseEvent(pMouseEvent); // We'll need a reference for issuing commands... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const QPoint& pos = qtractorScrollView::viewportToContents(pMouseEvent->pos()); // If we were resizing, now's time to let // things know that have changed somehow... switch (m_dragState) { case DragMove: if (m_iDragTrack >= 0) { qtractorTrack *pTrack = track(m_iDragTrack); if (pTrack) { qtractorTrack *pTrackDrop = nullptr; const int iTrack = trackRowAt(pos); if (iTrack >= 0) pTrackDrop = track(iTrack); if (pTrack != pTrackDrop && (pTrackDrop == nullptr || pTrack != pTrackDrop->prev())) { clearSelect(); pSession->execute( new qtractorMoveTrackCommand(pTrack, pTrackDrop)); } } } break; case DragSelect: if (m_iDragTrack >= 0) updateSelect(true); break; case DragStart: // Special attitude, only of interest on // the first left-most column (track-number)... if (trackColumnAt(pos) == Number) { m_pTracks->selectCurrentTrack((pMouseEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) == 0); qtractorScrollView::setFocus(); // Get focus back anyway. } break; case DragResize: { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->thumbView()->updateContents(); break; } case DragNone: default: break; } // Force null state. resetDragState(); } // Handle zoom with mouse wheel. void qtractorTrackList::wheelEvent ( QWheelEvent *pWheelEvent ) { if (pWheelEvent->modifiers() & Qt::ControlModifier) { const int delta = pWheelEvent->angleDelta().y(); if (delta > 0) m_pTracks->zoomIn(); else m_pTracks->zoomOut(); } else qtractorScrollView::wheelEvent(pWheelEvent); } // Drag-n-drop event handlers. void qtractorTrackList::dragEnterEvent ( QDragEnterEvent *pDragEnterEvent ) { const QMimeData *pMimeData = pDragEnterEvent->mimeData(); if (pMimeData && pMimeData->hasUrls()) pDragEnterEvent->accept(); else pDragEnterEvent->ignore(); } void qtractorTrackList::dragMoveEvent ( QDragMoveEvent *pDragMoveEvent ) { // Now set ready for drag something... const QPoint& pos #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) = qtractorScrollView::viewportToContents(pDragMoveEvent->position().toPoint()); #else = qtractorScrollView::viewportToContents(pDragMoveEvent->pos()); #endif // Select current track... const int iTrack = trackRowAt(pos); // Make it current anyway... setCurrentTrackRow(iTrack); } void qtractorTrackList::dropEvent ( QDropEvent *pDropEvent ) { // Can we decode it as Audio/MIDI files? const QMimeData *pMimeData = pDropEvent->mimeData(); if (pMimeData == nullptr || !pMimeData->hasUrls()) return; // Let's see how many files there are... QStringList files; QListIterator iter(pMimeData->urls()); while (iter.hasNext()) { const QString& sPath = iter.next().toLocalFile(); if (!sPath.isEmpty()) files.append(sPath); } // Depending on import type... qtractorSession *pSession = qtractorSession::getInstance(); const unsigned long iClipStart = (pSession ? pSession->editHead() : 0); qtractorTrack *pAfterTrack = currentTrack(); if (m_pTracks->addTracks(files, iClipStart, 0, 0, pAfterTrack)) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->dropEvent(pDropEvent); } } // Draw a dragging separator line. void qtractorTrackList::moveRubberBand ( const QPoint& posDrag ) { const QPoint& pos = qtractorScrollView::contentsToViewport(posDrag); // Create the rubber-band if there's none... if (m_pRubberBand == nullptr) { m_pRubberBand = new qtractorRubberBand( QRubberBand::Line, qtractorScrollView::viewport()); #if 0 QPalette pal(m_pRubberBand->palette()); pal.setColor(m_pRubberBand->foregroundRole(), pal.highlight().color()); m_pRubberBand->setPalette(pal); m_pRubberBand->setBackgroundRole(QPalette::NoRole); #endif } // Just move it m_pRubberBand->setGeometry( QRect(0, pos.y() - 1, qtractorScrollView::viewport()->width(), 3)); // Ah, and make it visible, of course... if (!m_pRubberBand->isVisible()) m_pRubberBand->show(); } // Draw a lasso rectangle. void qtractorTrackList::moveRubberBand ( const QRect& rectDrag ) { QRect rect(rectDrag.normalized()); rect.moveTopLeft(qtractorScrollView::contentsToViewport(rect.topLeft())); // Create the rubber-band if there's none... if (m_pRubberBand == nullptr) { m_pRubberBand = new qtractorRubberBand( QRubberBand::Rectangle, qtractorScrollView::viewport()); #if 0 QPalette pal(m_pRubberBand->palette()); pal.setColor(m_pRubberBand->foregroundRole(), pal.highlight().color()); m_pRubberBand->setPalette(pal); m_pRubberBand->setBackgroundRole(QPalette::NoRole); #endif } // Just move it m_pRubberBand->setGeometry(rect); // Ah, and make it visible, of course... if (!m_pRubberBand->isVisible()) m_pRubberBand->show(); } // Make sure the given (track) rectangle is visible. bool qtractorTrackList::ensureVisibleRect ( const QRect& rect ) { if (!rect.isValid()) return false; const int hh = m_pHeader->sizeHint().height(); const int cx = qtractorScrollView::contentsX(); const int cy = qtractorScrollView::contentsY(); if (rect.top() < cy + hh) { qtractorScrollView::ensureVisible(cx, rect.top(), 0, 24); return true; } if (rect.bottom() > cy + qtractorScrollView::viewport()->height()) { qtractorScrollView::ensureVisible(cx, rect.bottom() - hh, 0, 24); return true; } return false; } // Reset drag/select/move state. void qtractorTrackList::resetDragState (void) { // Cancel any dragging out there... // Just hide the rubber-band... if (m_pRubberBand) { // m_pRubberBand->hide(); delete m_pRubberBand; m_pRubberBand = nullptr; } // Should fallback mouse cursor... if (m_dragState != DragNone || m_iDragTrack >= 0) qtractorScrollView::unsetCursor(); // Not dragging anymore. m_dragState = DragNone; m_iDragTrack = -1; m_iDragY = 0; } // Keyboard event handler. void qtractorTrackList::keyPressEvent ( QKeyEvent *pKeyEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTrackList::keyPressEvent(%d)", pKeyEvent->key()); #endif const Qt::KeyboardModifiers& modifiers = pKeyEvent->modifiers(); switch (pKeyEvent->key()) { case Qt::Key_Escape: resetDragState(); clearSelect(); break; case Qt::Key_Home: if (m_iCurrentTrack > 0) { const int iTrack = 0; if (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) { const bool bToggle = (modifiers & Qt::ControlModifier); for (int i = iTrack; m_iCurrentTrack >= i; ++i) selectTrack(i, true, bToggle); updateSelect(true); } setCurrentTrackRow(iTrack); } break; case Qt::Key_End: if (m_iCurrentTrack < m_items.count() - 1) { const int iTrack = m_items.count() - 1; if (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) { const bool bToggle = (modifiers & Qt::ControlModifier); for (int i = iTrack; i >= m_iCurrentTrack; --i) selectTrack(i, true, bToggle); updateSelect(true); } setCurrentTrackRow(iTrack); } break; case Qt::Key_Left: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX() - qtractorScrollView::width(), qtractorScrollView::contentsY()); } else { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX() - 16, qtractorScrollView::contentsY()); } break; case Qt::Key_Right: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX() + qtractorScrollView::width(), qtractorScrollView::contentsY()); } else { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX() + 16, qtractorScrollView::contentsY()); } break; case Qt::Key_Up: if (m_iCurrentTrack > 0) { const int iTrack = m_iCurrentTrack - 1; if (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) { const bool bToggle = (modifiers & Qt::ControlModifier); selectTrack(m_iCurrentTrack, true, bToggle); selectTrack(iTrack, true, bToggle); updateSelect(true); } setCurrentTrackRow(iTrack); } break; case Qt::Key_Down: if (m_iCurrentTrack < m_items.count() - 1) { const int iTrack = m_iCurrentTrack + 1; if (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) { const bool bToggle = (modifiers & Qt::ControlModifier); selectTrack(m_iCurrentTrack, true, bToggle); selectTrack(iTrack, true, bToggle); updateSelect(true); } setCurrentTrackRow(iTrack); } break; case Qt::Key_PageUp: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), 0); } else { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), qtractorScrollView::contentsY() - qtractorScrollView::height()); } break; case Qt::Key_PageDown: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), qtractorScrollView::contentsHeight()); } else { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), qtractorScrollView::contentsY() + qtractorScrollView::height()); } break; default: qtractorScrollView::keyPressEvent(pKeyEvent); break; } } // end of qtractorTrackList.cpp qtractor-1.5.11/src/PaxHeaders/qtractorTimeScaleForm.h0000644000000000000000000000012715124701674017741 xustar0029 mtime=1767080892.80126342 29 atime=1767080892.80126342 29 ctime=1767080892.80126342 qtractor-1.5.11/src/qtractorTimeScaleForm.h0000644000175000001440000000577415124701674017742 0ustar00rncbcusers// qtractorTimeScaleForm.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorTimeScaleForm_h #define __qtractorTimeScaleForm_h #include "ui_qtractorTimeScaleForm.h" // Forward declarations... class qtractorTimeScaleListItem; class QElapsedTimer; //---------------------------------------------------------------------------- // qtractorTimeScaleForm -- UI wrapper form. class qtractorTimeScaleForm : public QDialog { Q_OBJECT public: // Constructor. qtractorTimeScaleForm(QWidget *pParent = nullptr); // Destructor. ~qtractorTimeScaleForm(); void setTimeScale(qtractorTimeScale *pTimeScale); qtractorTimeScale *timeScale() const; void setFrame(unsigned long iFrame); unsigned long frame() const; unsigned short bar() const; bool isDirty(); protected slots: void reject(); void refresh(); void selectItem(); void addItem(); void updateItem(); void removeItem(); void refreshItems(); void barChanged(int); void timeChanged(unsigned long); void tempoChanged(); void accidentalsChanged(int); void modeChanged(int); void changed(); void tempoTap(); void tempoFactor(); void markerColor(); void contextMenu(const QPoint&); void stabilizeForm(); protected: enum { AddNode = (1 << 0), UpdateNode = (1 << 1), RemoveNode = (1 << 2), AddMarker = (1 << 3), UpdateMarker = (1 << 4), RemoveMarker = (1 << 5), AddKeySignature = (1 << 6), UpdateKeySignature = (1 << 7) }; unsigned int flags() const; void setCurrentItem(qtractorTimeScale::Node *pNode, unsigned long iFrame); void setCurrentMarker(qtractorTimeScale::Marker *pMarker); void setCurrentKeySignature(qtractorTimeScale::Marker *pMarker); void updateKeySignatures(int iAccidentals, int iMode); void ensureVisibleFrame(unsigned long iFrame); private: // The Qt-designer UI struct... Ui::qtractorTimeScaleForm m_ui; // Instance variables... qtractorTimeScale *m_pTimeScale; QElapsedTimer *m_pTempoTap; int m_iTempoTap; float m_fTempoTap; int m_iDirtySetup; int m_iDirtyCount; int m_iDirtyTotal; }; #endif // __qtractorTimeScaleForm_h // end of qtractorTimeScaleForm.h qtractor-1.5.11/src/PaxHeaders/qtractorScrollView.h0000644000000000000000000000013215124701674017334 xustar0030 mtime=1767080892.799263428 30 atime=1767080892.799263428 30 ctime=1767080892.799263428 qtractor-1.5.11/src/qtractorScrollView.h0000644000175000001440000000551715124701674017334 0ustar00rncbcusers// qtractorScrollView.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorScrollView_h #define __qtractorScrollView_h #include #include // Forward declarations. class QResizeEvent; class QPaintEvent; //---------------------------------------------------------------------------- // qtractorScrollView -- abstract scroll view widget. class qtractorScrollView : public QAbstractScrollArea { Q_OBJECT public: // Constructor. qtractorScrollView(QWidget *pParent); // Destructor. virtual ~qtractorScrollView(); // Virtual contents extent accessors. int contentsX() const { return m_rectContents.x(); } int contentsY() const { return m_rectContents.y(); } int contentsHeight() const { return m_rectContents.height(); } int contentsWidth() const { return m_rectContents.width(); } // Virtual contents methods. void setContentsPos(int cx, int cy); void resizeContents(int cw, int ch); // Scrolls contents so that given point is visible. void ensureVisible(int cx, int cy, int mx = 50, int my = 50); // Viewport/contents position converters. QPoint viewportToContents(const QPoint& pos) const; QPoint contentsToViewport(const QPoint& pos) const; signals: // Contents moving slot. void contentsMoving(int cx, int cy); protected: // Scrollbar stabilization. void updateScrollBars(); // Scroll area updater. void scrollContentsBy(int dx, int dy); // Specialized event handlers. void resizeEvent(QResizeEvent *pResizeEvent); void paintEvent(QPaintEvent *pPaintEvent); void wheelEvent(QWheelEvent *pWheelEvent); // Draw the virtual contents. virtual void drawContents(QPainter *pPainter, const QRect& rect) = 0; // Rectangular contents update. virtual void updateContents(const QRect& rect); // Overall contents update. virtual void updateContents(); private: // The virtual contents coordinates. QRect m_rectContents; }; #endif // __qtractorScrollView_h // end of qtractorScrollView.h qtractor-1.5.11/src/PaxHeaders/qtractorAudioConnect.h0000644000000000000000000000013215124701674017616 xustar0030 mtime=1767080892.778263517 30 atime=1767080892.778263517 30 ctime=1767080892.778263517 qtractor-1.5.11/src/qtractorAudioConnect.h0000644000175000001440000001025315124701674017607 0ustar00rncbcusers// qtractorAudioConnect.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorAudioConnect_h #define __qtractorAudioConnect_h #include "qtractorConnect.h" #include // Forward declarations. class qtractorAudioPortItem; class qtractorAudioClientItem; class qtractorAudioClientListView; class qtractorAudioConnect; //---------------------------------------------------------------------- // qtractorAudioPortItem -- Jack port list item. // class qtractorAudioPortItem : public qtractorPortListItem { public: // Constructor. qtractorAudioPortItem( qtractorAudioClientItem *pClientItem, unsigned long uiPortFlags); // Default destructor. ~qtractorAudioPortItem(); // Proto-pretty/display name accessors (virtual override). void updatePortName(); }; //---------------------------------------------------------------------- // qtractorAudioClientItem -- Jack client list item. // class qtractorAudioClientItem : public qtractorClientListItem { public: // Constructor. qtractorAudioClientItem(qtractorAudioClientListView *pClientListView); // Default destructor. ~qtractorAudioClientItem(); // Proto-pretty/display name accessors (virtual override). void updateClientName(); }; //---------------------------------------------------------------------- // qtractorAudioClientListView -- Jack client list view. // class qtractorAudioClientListView : public qtractorClientListView { public: // Constructor. qtractorAudioClientListView(QWidget *pParent = nullptr); // Default destructor. ~qtractorAudioClientListView(); // Jack connect accessors. jack_client_t *jackClient() const; // Client:port refreshner (return newest item count). int updateClientPorts(); }; //---------------------------------------------------------------------------- // qtractorAudioConnect -- Connections model integrated object. // class qtractorAudioConnect : public qtractorConnect { public: // Constructor. qtractorAudioConnect( qtractorAudioClientListView *pOListView, qtractorAudioClientListView *pIListView, qtractorConnectorView *pConnectorView); // Default destructor. ~qtractorAudioConnect(); // JACK client accessors. jack_client_t *jackClient() const; // Icon-set array indexes. enum { ClientIn = 0, // Input client item icon. ClientOut = 1, // Output client item icon. PortIn = 2, // Input port item icon. PortOut = 3, // Output port item icon. PortPhysIn = 4, // Physical input port item icon. PortPhysOut = 5, // Physical output port item icon., IconCount = 6 // Number of icons in local array. }; // Common icon accessor. static const QIcon& icon (int iIcon); protected: // Virtual Connect/Disconnection primitives. bool connectPorts(qtractorPortListItem *pOPort, qtractorPortListItem *pIPort); bool disconnectPorts(qtractorPortListItem *pOPort, qtractorPortListItem *pIPort); // Update port connection references. void updateConnections(); // Update (clear) Audio-buses connect lists (non-virtual). void disconnectPortsUpdate( qtractorPortListItem *pOPort, qtractorPortListItem *pIPort); private: // Local pixmap-set janitor methods. void createIcons(); void deleteIcons(); // Local pixmap-set array. static QIcon *g_apIcons[IconCount]; static int g_iIconsRefCount; }; #endif // __qtractorAudioConnect_h // end of qtractorAudioConnect.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiControlTypeGroup.cpp0000644000000000000000000000013215124701674021700 xustar0030 mtime=1767080892.790263466 30 atime=1767080892.790263466 30 ctime=1767080892.790263466 qtractor-1.5.11/src/qtractorMidiControlTypeGroup.cpp0000644000175000001440000002765415124701674021706 0ustar00rncbcusers// qtractorMidiControltypeGroup.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorMidiControlTypeGroup.h" #include #include #include //---------------------------------------------------------------------------- // qtractorMidiControlTypeGroup - MIDI control type/param widget group. // Constructor. qtractorMidiControlTypeGroup::qtractorMidiControlTypeGroup ( qtractorMidiEditor *pMidiEditor, QComboBox *pControlTypeComboBox, QComboBox *pControlParamComboBox, QLabel *pControlParamTextLabel ) : QObject(), m_pMidiEditor(pMidiEditor), m_pControlTypeComboBox(pControlTypeComboBox), m_pControlParamComboBox(pControlParamComboBox), m_pControlParamTextLabel(pControlParamTextLabel), m_iControlParamUpdate(0) { const QIcon& iconControlType = QIcon::fromTheme("itemProperty"); m_pControlTypeComboBox->clear(); m_pControlTypeComboBox->addItem(iconControlType, qtractorMidiControl::nameFromType(qtractorMidiEvent::NOTEON), int(qtractorMidiEvent::NOTEON)); m_pControlTypeComboBox->addItem(iconControlType, qtractorMidiControl::nameFromType(qtractorMidiEvent::NOTEOFF), int(qtractorMidiEvent::NOTEOFF)); m_pControlTypeComboBox->addItem(iconControlType, qtractorMidiControl::nameFromType(qtractorMidiEvent::KEYPRESS), int(qtractorMidiEvent::KEYPRESS)); m_pControlTypeComboBox->addItem(iconControlType, qtractorMidiControl::nameFromType(qtractorMidiEvent::CONTROLLER), int(qtractorMidiEvent::CONTROLLER)); m_pControlTypeComboBox->addItem(iconControlType, qtractorMidiControl::nameFromType(qtractorMidiEvent::PGMCHANGE), int(qtractorMidiEvent::PGMCHANGE)); m_pControlTypeComboBox->addItem(iconControlType, qtractorMidiControl::nameFromType(qtractorMidiEvent::CHANPRESS), int(qtractorMidiEvent::CHANPRESS)); m_pControlTypeComboBox->addItem(iconControlType, qtractorMidiControl::nameFromType(qtractorMidiEvent::PITCHBEND), int(qtractorMidiEvent::PITCHBEND)); m_pControlTypeComboBox->addItem(iconControlType, qtractorMidiControl::nameFromType(qtractorMidiEvent::REGPARAM), int(qtractorMidiEvent::REGPARAM)); m_pControlTypeComboBox->addItem(iconControlType, qtractorMidiControl::nameFromType(qtractorMidiEvent::NONREGPARAM), int(qtractorMidiEvent::NONREGPARAM)); m_pControlTypeComboBox->addItem(iconControlType, qtractorMidiControl::nameFromType(qtractorMidiEvent::CONTROL14), int(qtractorMidiEvent::CONTROL14)); m_pControlParamComboBox->setInsertPolicy(QComboBox::NoInsert); QObject::connect(m_pControlTypeComboBox, SIGNAL(activated(int)), SLOT(activateControlType(int))); QObject::connect(m_pControlParamComboBox, SIGNAL(activated(int)), SLOT(activateControlParam(int))); } // Accessors. void qtractorMidiControlTypeGroup::setControlType ( qtractorMidiControl::ControlType ctype ) { const int iControlType = indexFromControlType(ctype); m_pControlTypeComboBox->setCurrentIndex(iControlType); activateControlType(iControlType); } qtractorMidiControl::ControlType qtractorMidiControlTypeGroup::controlType (void) const { return controlTypeFromIndex(m_pControlTypeComboBox->currentIndex()); } qtractorMidiControl::ControlType qtractorMidiControlTypeGroup::controlTypeFromIndex ( int iIndex ) const { if (iIndex < 0 || iIndex >= m_pControlTypeComboBox->count()) return qtractorMidiEvent::NOTEON; else return qtractorMidiControl::ControlType( m_pControlTypeComboBox->itemData(iIndex).toInt()); } void qtractorMidiControlTypeGroup::setControlParam ( unsigned short iParam ) { const int iControlParam = indexFromControlParam(iParam); if (iControlParam >= 0) { m_pControlParamComboBox->setCurrentIndex(iControlParam); activateControlParam(iControlParam); } else { const QString& sControlParam = QString::number(iParam); m_pControlParamComboBox->setEditText(sControlParam); editControlParamFinished(); } } unsigned short qtractorMidiControlTypeGroup::controlParam (void) const { if (m_pControlParamComboBox->isEditable()) { unsigned short iParam = 0; const QString& sControlParam = m_pControlParamComboBox->currentText(); bool bOk = false; iParam = sControlParam.toInt(&bOk); if (bOk) return iParam; } return controlParamFromIndex(m_pControlParamComboBox->currentIndex()); } unsigned short qtractorMidiControlTypeGroup::controlParamFromIndex ( int iIndex ) const { if (iIndex >= 0 && iIndex < m_pControlParamComboBox->count()) return m_pControlParamComboBox->itemData(iIndex).toInt(); else return 0; } // Stabilizers. void qtractorMidiControlTypeGroup::updateControlType ( int iControlType ) { // Just in case... if (m_pMidiEditor) m_pMidiEditor->updateInstrumentNames(); if (iControlType < 0) iControlType = m_pControlTypeComboBox->currentIndex(); const qtractorMidiControl::ControlType ctype = qtractorMidiControl::ControlType( m_pControlTypeComboBox->itemData(iControlType).toInt()); const bool bOldEditable = m_pControlParamComboBox->isEditable(); const int iOldParam = m_pControlParamComboBox->currentIndex(); const QString sOldParam = m_pControlParamComboBox->currentText(); m_pControlParamComboBox->clear(); const QString sTextMask("%1 - %2"); switch (ctype) { case qtractorMidiEvent::NOTEON: case qtractorMidiEvent::NOTEOFF: case qtractorMidiEvent::KEYPRESS: { const QIcon& iconNotes = QIcon::fromTheme("itemNotes"); if (m_pControlParamTextLabel) m_pControlParamTextLabel->setEnabled(true); m_pControlParamComboBox->setEnabled(true); m_pControlParamComboBox->setEditable(false); for (unsigned short iParam = 0; iParam < 128; ++iParam) { m_pControlParamComboBox->addItem(iconNotes, sTextMask.arg(iParam).arg( m_pMidiEditor ? m_pMidiEditor->noteName(iParam) : qtractorMidiEditor::defaultNoteName(iParam)), int(iParam)); } break; } case qtractorMidiEvent::CONTROLLER: { const QIcon& iconControllers = QIcon::fromTheme("itemControllers"); if (m_pControlParamTextLabel) m_pControlParamTextLabel->setEnabled(true); m_pControlParamComboBox->setEnabled(true); m_pControlParamComboBox->setEditable(false); for (unsigned short iParam = 0; iParam < 128; ++iParam) { m_pControlParamComboBox->addItem(iconControllers, sTextMask.arg(iParam).arg( m_pMidiEditor ? m_pMidiEditor->controllerName(iParam) : qtractorMidiEditor::defaultControllerName(iParam)), int(iParam)); } break; } case qtractorMidiEvent::PGMCHANGE: { const QIcon& iconPatches = QIcon::fromTheme("itemPatches"); if (m_pControlParamTextLabel) m_pControlParamTextLabel->setEnabled(true); m_pControlParamComboBox->setEnabled(true); m_pControlParamComboBox->setEditable(false); for (unsigned short iParam = 0; iParam < 128; ++iParam) { m_pControlParamComboBox->addItem(iconPatches, sTextMask.arg(iParam).arg('-'), int(iParam)); } break; } case qtractorMidiEvent::REGPARAM: { const QIcon& iconRpns = QIcon::fromTheme("itemRpns"); if (m_pControlParamTextLabel) m_pControlParamTextLabel->setEnabled(true); m_pControlParamComboBox->setEnabled(true); m_pControlParamComboBox->setEditable(true); const QMap& rpns = (m_pMidiEditor ? m_pMidiEditor->rpnNames() : qtractorMidiEditor::defaultRpnNames()); QMap::ConstIterator rpns_iter = rpns.constBegin(); const QMap::ConstIterator& rpns_end = rpns.constEnd(); for ( ; rpns_iter != rpns_end; ++rpns_iter) { const unsigned short iParam = rpns_iter.key(); m_pControlParamComboBox->addItem(iconRpns, sTextMask.arg(iParam).arg(rpns_iter.value()), int(iParam)); } break; } case qtractorMidiEvent::NONREGPARAM: { const QIcon& iconNrpns = QIcon::fromTheme("itemNrpns"); if (m_pControlParamTextLabel) m_pControlParamTextLabel->setEnabled(true); m_pControlParamComboBox->setEnabled(true); m_pControlParamComboBox->setEditable(true); const QMap& nrpns = (m_pMidiEditor ? m_pMidiEditor->nrpnNames() : qtractorMidiEditor::defaultNrpnNames()); QMap::ConstIterator nrpns_iter = nrpns.constBegin(); const QMap::ConstIterator& nrpns_end = nrpns.constEnd(); for ( ; nrpns_iter != nrpns_end; ++nrpns_iter) { const unsigned short iParam = nrpns_iter.key(); m_pControlParamComboBox->addItem(iconNrpns, sTextMask.arg(iParam).arg(nrpns_iter.value()), int(iParam)); } break; } case qtractorMidiEvent::CONTROL14: { const QIcon& iconControllers = QIcon::fromTheme("itemControllers"); if (m_pControlParamTextLabel) m_pControlParamTextLabel->setEnabled(true); m_pControlParamComboBox->setEnabled(true); m_pControlParamComboBox->setEditable(false); for (unsigned short iParam = 1; iParam < 32; ++iParam) { m_pControlParamComboBox->addItem(iconControllers, sTextMask.arg(iParam).arg( m_pMidiEditor ? m_pMidiEditor->control14Name(iParam) : qtractorMidiEditor::defaultControl14Name(iParam)), int(iParam)); } break; } case qtractorMidiEvent::CHANPRESS: case qtractorMidiEvent::PITCHBEND: default: if (m_pControlParamTextLabel) m_pControlParamTextLabel->setEnabled(false); m_pControlParamComboBox->setEnabled(false); m_pControlParamComboBox->setEditable(false); break; } if (iOldParam >= 0 && iOldParam < m_pControlParamComboBox->count()) m_pControlParamComboBox->setCurrentIndex(iOldParam); if (m_pControlParamComboBox->isEditable()) { QObject::connect(m_pControlParamComboBox->lineEdit(), SIGNAL(editingFinished()), SLOT(editControlParamFinished())); if (bOldEditable) m_pControlParamComboBox->setEditText(sOldParam); } } // Private slots. void qtractorMidiControlTypeGroup::activateControlType ( int iControlType ) { updateControlType(iControlType); emit controlTypeChanged(iControlType); activateControlParam(m_pControlParamComboBox->currentIndex()); } void qtractorMidiControlTypeGroup::activateControlParam ( int iControlParam ) { const unsigned short iParam = m_pControlParamComboBox->itemData(iControlParam).toInt(); emit controlParamChanged(int(iParam)); } void qtractorMidiControlTypeGroup::editControlParamFinished (void) { if (m_iControlParamUpdate > 0) return; ++m_iControlParamUpdate; const QString& sControlParam = m_pControlParamComboBox->currentText(); bool bOk = false; const unsigned short iParam = sControlParam.toInt(&bOk); if (bOk) emit controlParamChanged(int(iParam)); --m_iControlParamUpdate; } // Find combo-box index from control type. int qtractorMidiControlTypeGroup::indexFromControlType ( qtractorMidiControl::ControlType ctype ) const { const int iItemCount = m_pControlTypeComboBox->count(); for (int iIndex = 0; iIndex < iItemCount; ++iIndex) { if (qtractorMidiControl::ControlType( m_pControlTypeComboBox->itemData(iIndex).toInt()) == ctype) return iIndex; } return (-1); } // Find combo-box index from control parameter number. int qtractorMidiControlTypeGroup::indexFromControlParam ( unsigned short iParam ) const { const int iItemCount = m_pControlParamComboBox->count(); for (int iIndex = 0; iIndex < iItemCount; ++iIndex) { if (m_pControlParamComboBox->itemData(iIndex).toInt() == int(iParam)) return iIndex; } return (-1); } // end of qtractorMidiControlTypeGroup.cpp qtractor-1.5.11/src/PaxHeaders/qtractorTracks.cpp0000644000000000000000000000013215124701674017025 xustar0030 mtime=1767080892.804263407 30 atime=1767080892.804263407 30 ctime=1767080892.804263407 qtractor-1.5.11/src/qtractorTracks.cpp0000644000175000001440000027444715124701674017037 0ustar00rncbcusers// qtractorTracks.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorTracks.h" #include "qtractorTrackView.h" #include "qtractorTrackList.h" #include "qtractorTrackTime.h" #include "qtractorSession.h" #include "qtractorSessionCursor.h" #include "qtractorSessionCommand.h" #include "qtractorTrackCommand.h" #include "qtractorClipCommand.h" #include "qtractorCurveCommand.h" #include "qtractorMidiEditCommand.h" #include "qtractorTimeScaleCommand.h" #include "qtractorAudioEngine.h" #include "qtractorAudioBuffer.h" #include "qtractorAudioClip.h" #include "qtractorMidiEngine.h" #include "qtractorMidiClip.h" #include "qtractorClipSelect.h" #include "qtractorOptions.h" #include "qtractorMainForm.h" #include "qtractorTrackForm.h" #include "qtractorExportForm.h" #include "qtractorPasteRepeatForm.h" #include "qtractorTempoAdjustForm.h" #include "qtractorEditRangeForm.h" #include "qtractorMidiEditorForm.h" #include "qtractorMidiEditor.h" #include "qtractorMidiToolsForm.h" #include "qtractorMidiEditSelect.h" #include "qtractorFileList.h" #include #include #include #include #include #include #include #include //---------------------------------------------------------------------------- // qtractorTracks -- The main session track listview widget. // Constructor. qtractorTracks::qtractorTracks ( QWidget *pParent ) : QSplitter(Qt::Horizontal, pParent) { // Surely a name is crucial (e.g. for storing geometry settings) QSplitter::setObjectName("qtractorTracks"); // Create child widgets... m_pTrackList = new qtractorTrackList(this, this); QWidget *pVBox = new QWidget(this); m_pTrackTime = new qtractorTrackTime(this, pVBox); m_pTrackView = new qtractorTrackView(this, pVBox); // Zoom mode flag. m_iZoomMode = ZoomAll; // Create child box layouts... QVBoxLayout *pVBoxLayout = new QVBoxLayout(pVBox); pVBoxLayout->setContentsMargins(0, 0, 0, 0); pVBoxLayout->setSpacing(0); pVBoxLayout->addWidget(m_pTrackTime); pVBoxLayout->addWidget(m_pTrackView); pVBox->setLayout(pVBoxLayout); // QSplitter::setOpaqueResize(false); QSplitter::setStretchFactor(QSplitter::indexOf(m_pTrackList), 0); QSplitter::setHandleWidth(2); QSplitter::setWindowTitle(tr("Tracks")); QSplitter::setWindowIcon(QIcon::fromTheme("qtractorTracks")); // Get previously saved splitter sizes, // (with some fair default...) qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { QList sizes; sizes.append(160); sizes.append(480); pOptions->loadSplitterSizes(this, sizes); } // Early track list stabilization. m_pTrackTime->setFixedHeight( (m_pTrackList->header())->sizeHint().height()); // To have all views in positional sync. QObject::connect(m_pTrackList, SIGNAL(contentsMoving(int,int)), m_pTrackView, SLOT(contentsYMovingSlot(int,int))); QObject::connect(m_pTrackView, SIGNAL(contentsMoving(int,int)), m_pTrackTime, SLOT(contentsXMovingSlot(int,int))); QObject::connect(m_pTrackView, SIGNAL(contentsMoving(int,int)), m_pTrackList, SLOT(contentsYMovingSlot(int,int))); } // Destructor. qtractorTracks::~qtractorTracks (void) { // Save splitter sizes... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) pOptions->saveSplitterSizes(this); } // Child widgets accessors. qtractorTrackList *qtractorTracks::trackList (void) const { return m_pTrackList; } qtractorTrackTime *qtractorTracks::trackTime (void) const { return m_pTrackTime; } qtractorTrackView *qtractorTracks::trackView (void) const { return m_pTrackView; } // Horizontal zoom factor. void qtractorTracks::horizontalZoomStep ( int iZoomStep ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; int iHorizontalZoom = pSession->horizontalZoom() + iZoomStep; if (iHorizontalZoom < ZoomMin) iHorizontalZoom = ZoomMin; else if (iHorizontalZoom > ZoomMax) iHorizontalZoom = ZoomMax; if (iHorizontalZoom == pSession->horizontalZoom()) return; // Fix the ssession time scale zoom determinant. pSession->setHorizontalZoom(iHorizontalZoom); pSession->updateTimeScale(); pSession->updateSession(); // Update visual play-head position... m_pTrackView->setPlayHead(pSession->playHead()); } // Vertical zoom factor. void qtractorTracks::verticalZoomStep ( int iZoomStep ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; int iVerticalZoom = pSession->verticalZoom() + iZoomStep; if (iVerticalZoom < ZoomMin) iVerticalZoom = ZoomMin; else if (iVerticalZoom > ZoomMax) iVerticalZoom = ZoomMax; if (iVerticalZoom == pSession->verticalZoom()) return; // Fix the session vertical view zoom. pSession->setVerticalZoom(iVerticalZoom); } // Zoom (view) mode. void qtractorTracks::setZoomMode ( int iZoomMode ) { m_iZoomMode = iZoomMode; } int qtractorTracks::zoomMode (void) const { return m_iZoomMode; } // Zoom view actuators. void qtractorTracks::zoomIn (void) { ZoomCenter zc; zoomCenterPre(zc); if (m_iZoomMode & ZoomHorizontal) horizontalZoomStep(+ ZoomStep); if (m_iZoomMode & ZoomVertical) verticalZoomStep(+ ZoomStep); zoomCenterPost(zc); } void qtractorTracks::zoomOut (void) { ZoomCenter zc; zoomCenterPre(zc); if (m_iZoomMode & ZoomHorizontal) horizontalZoomStep(- ZoomStep); if (m_iZoomMode & ZoomVertical) verticalZoomStep(- ZoomStep); zoomCenterPost(zc); } void qtractorTracks::zoomReset (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; ZoomCenter zc; zoomCenterPre(zc); if (m_iZoomMode & ZoomHorizontal) horizontalZoomStep(ZoomBase - pSession->horizontalZoom()); if (m_iZoomMode & ZoomVertical) verticalZoomStep(ZoomBase - pSession->verticalZoom()); zoomCenterPost(zc); } // Zoom step evaluator. int qtractorTracks::zoomStep (void) const { const Qt::KeyboardModifiers& modifiers = QApplication::keyboardModifiers(); if (modifiers & Qt::ControlModifier) return ZoomMax; if (modifiers & Qt::ShiftModifier) return ZoomBase >> 1; return ZoomStep; } void qtractorTracks::horizontalZoomInSlot (void) { ZoomCenter zc; zoomCenterPre(zc); horizontalZoomStep(+ zoomStep()); zoomCenterPost(zc); } void qtractorTracks::horizontalZoomOutSlot (void) { ZoomCenter zc; zoomCenterPre(zc); horizontalZoomStep(- zoomStep()); zoomCenterPost(zc); } void qtractorTracks::verticalZoomInSlot (void) { ZoomCenter zc; zoomCenterPre(zc); verticalZoomStep(+ zoomStep()); zoomCenterPost(zc); } void qtractorTracks::verticalZoomOutSlot (void) { ZoomCenter zc; zoomCenterPre(zc); verticalZoomStep(- zoomStep()); zoomCenterPost(zc); } void qtractorTracks::viewZoomResetSlot (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; ZoomCenter zc; zoomCenterPre(zc); // All zoom base are belong to us :) verticalZoomStep(ZoomBase - pSession->verticalZoom()); horizontalZoomStep(ZoomBase - pSession->horizontalZoom()); zoomCenterPost(zc); } // Zoom centering prepare method. // (usually before zoom change) void qtractorTracks::zoomCenterPre ( ZoomCenter& zc ) const { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const int cx = m_pTrackView->contentsX(); const int cy = m_pTrackView->contentsY(); QWidget *pViewport = m_pTrackView->viewport(); const QRect& rect = pViewport->rect(); const QPoint& pos = pViewport->mapFromGlobal(QCursor::pos()); zc.x = 0; zc.y = 0; if (rect.contains(pos)) { if (m_iZoomMode & ZoomHorizontal) zc.x = pos.x(); if (m_iZoomMode & ZoomVertical) zc.y = pos.y(); } else { if (m_iZoomMode & ZoomHorizontal) { const int w2 = (rect.width() >> 1); if (cx > w2) zc.x = w2; } if (m_iZoomMode & ZoomVertical) { const int h2 = (rect.height() >> 1); if (cy > h2) zc.y = h2; } } zc.ch = m_pTrackView->contentsHeight(); zc.frame = pSession->frameFromPixel(cx + zc.x); } // Zoom centering post methods. // (usually after zoom change) void qtractorTracks::zoomCenterPost ( const ZoomCenter& zc ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; int cx = pSession->pixelFromFrame(zc.frame); int cy = m_pTrackView->contentsY(); // Update the dependent views... m_pTrackList->updateItems(); m_pTrackList->updateContentsHeight(); m_pTrackView->updateContentsWidth(); if (m_iZoomMode & ZoomHorizontal) { if (cx > zc.x) cx -= zc.x; else cx = 0; } if (m_iZoomMode & ZoomVertical) { // if (cy > zc.y) cy -= zc.y; else cy = 0; cy = (cy * m_pTrackView->contentsHeight()) / zc.ch; } // Do the centering... m_pTrackView->setContentsPos(cx, cy); m_pTrackView->updateContents(); // Make its due... selectionChangeNotify(); } // Update/sync integral contents from session tracks. void qtractorTracks::updateContents ( bool bRefresh ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorTracks::updateContents(%d)\n", int(bRefresh)); #endif // Update/sync from session tracks. int iRefresh = 0; if (bRefresh) ++iRefresh; int iTrack = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { // Check if item is already on list if (m_pTrackList->trackRow(pTrack) < 0) { m_pTrackList->insertTrack(iTrack, pTrack); ++iRefresh; } pTrack = pTrack->next(); ++iTrack; } // Update dependent views. if (iRefresh > 0) { m_pTrackList->updateContentsHeight(); m_pTrackView->updateContentsWidth(); // m_pTrackView->setFocus(); } } // Retrieves current (selected) track reference. qtractorTrack *qtractorTracks::currentTrack (void) const { return m_pTrackList->currentTrack(); } // Make current selected clip reference. void qtractorTracks::setCurrentClip ( qtractorClip *pClip ) { m_pTrackView->setCurrentClip(pClip); } // Retrieves current selected clip reference. qtractorClip *qtractorTracks::currentClip (void) const { return m_pTrackView->currentClip(); } // Edit/create a brand new clip. bool qtractorTracks::newClip (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; // Create on current track, or take the first... qtractorTrack *pTrack = currentTrack(); if (pTrack == nullptr) pTrack = pSession->tracks().first(); if (pTrack == nullptr) return false; // Create the clip prototype... qtractorClip *pClip = nullptr; switch (pTrack->trackType()) { case qtractorTrack::Audio: pClip = new qtractorAudioClip(pTrack); break; case qtractorTrack::Midi: pClip = new qtractorMidiClip(pTrack); break; case qtractorTrack::None: default: break; } // Correct so far? if (pClip == nullptr) return false; // Set initial default clip parameters... const unsigned long iClipStart = pSession->editHead(); pClip->setClipStart(iClipStart); m_pTrackView->ensureVisibleFrame(iClipStart); // Special for MIDI clips, which already have it's own editor, // we'll add and start a blank one right-away... if (pTrack->trackType() == qtractorTrack::Midi) { // Set initial clip length... if (pSession->editTail() > pSession->editHead()) { pClip->setClipLength(pSession->editTail() - pSession->editHead()); } else { // Deafult length is one bar... const unsigned long iClipStartTime = pSession->tickFromFrame(iClipStart); const unsigned long iClipLengthTime = pSession->ticksPerBeat() * pSession->beatsPerBar(); pClip->setClipLength(pSession->frameFromTickRange( iClipStartTime, iClipStartTime + iClipLengthTime)); } // Proceed to setup the MDII clip properly... qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) { // Create a clip filename from scratch... const QString& sFilename = pSession->createFilePath(pTrack->shortTrackName(), "mid"); // Create the SMF for good... if (!pMidiClip->createMidiFile(sFilename)) { delete pClip; return false; } // Add that to regular files... pMainForm->addMidiFile(pClip->filename()); // Insert the clip right away... qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("new clip")); pClipCommand->addClip(pClip, pTrack); pSession->execute(pClipCommand); } } // Just start the clip editor on it... if (pClip->startEditor(pMainForm)) return true; // Otherwise get rid of it... delete pClip; return false; } // Edit given(current) clip. bool qtractorTracks::editClip ( qtractorClip *pClip ) { if (pClip == nullptr) pClip = m_pTrackView->currentClip(); if (pClip == nullptr) return false; // Assume all else hasn't failed... return pClip->startEditor(qtractorMainForm::getInstance()); } // Mute given(current) clip. bool qtractorTracks::muteClip ( qtractorClip *pClip ) { if (pClip == nullptr) pClip = m_pTrackView->currentClip(); if (pClip == nullptr) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorClipCommand *pClipMuteCommand = new qtractorClipCommand(tr("mute clip")); // Multiple clip selection... if (isClipSelected()) { qtractorClipSelect *pClipSelect = m_pTrackView->clipSelect(); const qtractorClipSelect::ItemList& items = pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { // Make sure it's legal selection... pClip = iter.key(); if (pClip->track() && pClip->isClipSelected()) pClipMuteCommand->muteClip(pClip, !pClip->isClipMute()); } } // Single, current clip instead? else pClipMuteCommand->muteClip(pClip, !pClip->isClipMute()); return pSession->execute(pClipMuteCommand); } // Unlink given(current) clip. bool qtractorTracks::unlinkClip ( qtractorClip *pClip ) { if (pClip == nullptr) pClip = m_pTrackView->currentClip(); if (pClip == nullptr) return false; qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip == nullptr) return false; if (!pMidiClip->isHashLinked()) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorClipUnlinkCommand *pClipUnlinkCommand = new qtractorClipUnlinkCommand(); pClipUnlinkCommand->addMidiClipContext(pMidiClip); return pSession->execute(pClipUnlinkCommand); } // Split given(current) clip. bool qtractorTracks::splitClip ( qtractorClip *pClip ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; QList clips; const unsigned long iPlayHead = pSession->playHead(); // Apply to current clip or clips on current track... if (pClip == nullptr) { qtractorTrack *pTrack = m_pTrackList->currentTrack(); if (pTrack) { pClip = pTrack->clips().first(); while (pClip && iPlayHead > pClip->clipStart() + pClip->clipLength()) { pClip = pClip->next(); } if (pClip && iPlayHead > pClip->clipStart() && iPlayHead < pClip->clipStart() + pClip->clipLength()) { clips.append(pClip); } } } else if (iPlayHead > pClip->clipStart() && iPlayHead < pClip->clipStart() + pClip->clipLength()) { clips.append(pClip); } // Apply to multiple clip selection, as well... qtractorClipSelect *pClipSelect = m_pTrackView->clipSelect(); const qtractorClipSelect::ItemList& items = pClipSelect->items(); if (items.count() > 0) { qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { pClip = iter.key(); if (!clips.contains(pClip) && iPlayHead > pClip->clipStart() && iPlayHead < pClip->clipStart() + pClip->clipLength()) { clips.append(pClip); } } } if (clips.isEmpty()) return false; QListIterator clip_iter(clips); while (clip_iter.hasNext()) { pClip = clip_iter.next(); if (!pClip->queryEditor()) return false; } clip_iter.toFront(); m_pTrackView->ensureVisibleFrame(iPlayHead); qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("split clip")); while (clip_iter.hasNext()) { pClip = clip_iter.next(); // Shorten old right... const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipEnd = iClipStart + pClip->clipLength(); const unsigned long iClipOffset = pClip->clipOffset(); pClipCommand->resizeClip(pClip, iClipStart, iClipOffset, iPlayHead - iClipStart); // Add left clone... qtractorClip *pNewClip = m_pTrackView->cloneClip(pClip); if (pNewClip) { pNewClip->setClipStart(iPlayHead); pNewClip->setClipOffset(iClipOffset + iPlayHead - iClipStart); pNewClip->setClipLength(iClipEnd - iPlayHead); pNewClip->setFadeOutLength(pClip->fadeOutLength()); pClipCommand->addClip(pNewClip, pNewClip->track()); } } // That's it... return pSession->execute(pClipCommand); } // Audio clip normalize callback. struct audioClipNormalizeData { // Ctor. audioClipNormalizeData(unsigned short iChannels) : count(0), channels(iChannels), max(0.0f) {} // Members. unsigned int count; unsigned short channels; float max; }; static void audioClipNormalize ( float **ppFrames, unsigned int iFrames, void *pvArg ) { audioClipNormalizeData *pData = static_cast (pvArg); for (unsigned short i = 0; i < pData->channels; ++i) { float *pFrames = ppFrames[i]; for (unsigned int n = 0; n < iFrames; ++n) { float fSample = *pFrames++; if (fSample < 0.0f) // Take absolute value... fSample = -(fSample); if (pData->max < fSample) pData->max = fSample; } } if (++(pData->count) > 100) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { QProgressBar *pProgressBar = pMainForm->progressBar(); pProgressBar->setValue(pProgressBar->value() + iFrames); } qtractorSession::stabilize(); pData->count = 0; } } // MIDI clip normalize callback. static void midiClipNormalize ( qtractorMidiSequence *pSeq, void *pvArg ) { unsigned char *pMax = (unsigned char *) (pvArg); for (qtractorMidiEvent *pEvent = pSeq->events().first(); pEvent; pEvent = pEvent->next()) { if (pEvent->type() == qtractorMidiEvent::NOTEON && *pMax < pEvent->velocity()) { *pMax = pEvent->velocity(); } } } // Normalize given(current) clip. bool qtractorTracks::normalizeClip ( qtractorClip *pClip ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Make it as an undoable command... qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("clip normalize")); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Multiple clip selection... if (isClipSelected()) { qtractorClipSelect *pClipSelect = m_pTrackView->clipSelect(); const qtractorClipSelect::ItemList& items = pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { // Make sure it's legal selection... pClip = iter.key(); if (pClip->track() && pClip->isClipSelected()) normalizeClipCommand(pClipCommand, pClip); } } // Single, current clip instead? else normalizeClipCommand(pClipCommand, pClip); QApplication::restoreOverrideCursor(); // Check if valid... if (pClipCommand->isEmpty()) { delete pClipCommand; return false; } // That's it... return pSession->execute(pClipCommand); } bool qtractorTracks::normalizeClipCommand ( qtractorClipCommand *pClipCommand, qtractorClip *pClip ) { if (pClip == nullptr) pClip = m_pTrackView->currentClip(); if (pClip == nullptr) return false; qtractorTrack *pTrack = pClip->track(); if (pTrack == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); unsigned long iOffset = 0; unsigned long iLength = pClip->clipLength(); if (pClip->isClipSelected()) { iOffset = pClip->clipSelectStart() - pClip->clipStart(); iLength = pClip->clipSelectEnd() - pClip->clipSelectStart(); } // Default non-normalized setting... float fGain = pClip->clipGain(); if (pTrack->trackType() == qtractorTrack::Audio) { // Normalize audio clip... qtractorAudioClip *pAudioClip = static_cast (pClip); if (pAudioClip == nullptr) return false; qtractorAudioBus *pAudioBus = static_cast (pTrack->outputBus()); if (pAudioBus == nullptr) return false; QProgressBar *pProgressBar = nullptr; if (pMainForm) pProgressBar = pMainForm->progressBar(); if (pProgressBar) { pProgressBar->setRange(0, iLength / 100); pProgressBar->reset(); pProgressBar->show(); } audioClipNormalizeData data(pAudioBus->channels()); pAudioClip->clipExport(audioClipNormalize, &data, iOffset, iLength); if (data.max > 0.01f && data.max < 1.1f) fGain /= data.max; if (pProgressBar) pProgressBar->hide(); } else if (pTrack->trackType() == qtractorTrack::Midi) { // Normalize MIDI clip... qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip == nullptr) return false; unsigned char max = 0; pMidiClip->clipExport(midiClipNormalize, &max, iOffset, iLength); if (max > 0x0c && max < 0x7f) fGain *= (127.0f / float(max)); } // Make it as an undoable command... pClipCommand->gainClip(pClip, fGain); // That's it... return true; } // Execute tool on a given(current) MIDI clip. bool qtractorTracks::executeClipTool ( int iTool, qtractorClip *pClip ) { qtractorMidiToolsForm toolsForm(this); toolsForm.setToolIndex(iTool); if (!toolsForm.exec()) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Make it as an undoable named command... QString sTool; switch (iTool) { case qtractorMidiEditor::Quantize: sTool = tr("quantize"); break; case qtractorMidiEditor::Transpose: sTool = tr("transpose"); break; case qtractorMidiEditor::Normalize: sTool = tr("normalize"); break; case qtractorMidiEditor::Randomize: sTool = tr("randomize"); break; case qtractorMidiEditor::Resize: sTool = tr("resize"); break; case qtractorMidiEditor::Rescale: sTool = tr("rescale"); break; case qtractorMidiEditor::Timeshift: sTool = tr("timeshift"); break; case qtractorMidiEditor::Temporamp: sTool = tr("tempo ramp"); break; } qtractorClipToolCommand *pClipToolCommand = new qtractorClipToolCommand(sTool); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Multiple clip selection... if (isClipSelected()) { qtractorClipSelect *pClipSelect = m_pTrackView->clipSelect(); const qtractorClipSelect::ItemList& items = pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { // Make sure it's legal selection... qtractorClip *pClip = iter.key(); if (pClip->track() && pClip->isClipSelected()) addClipToolCommand(pClipToolCommand, pClip, &toolsForm); } } // Single, current clip instead? else addClipToolCommand(pClipToolCommand, pClip, &toolsForm); QApplication::restoreOverrideCursor(); // Check if valid... if (pClipToolCommand->isEmpty()) { delete pClipToolCommand; return false; } // That's it... qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand = toolsForm.timeScaleNodeCommand(); while (pTimeScaleNodeCommand) { pClipToolCommand->addTimeScaleNodeCommand(pTimeScaleNodeCommand); pTimeScaleNodeCommand = toolsForm.timeScaleNodeCommand(); } return pSession->execute(pClipToolCommand); } bool qtractorTracks::addClipToolCommand ( qtractorClipToolCommand *pClipToolCommand, qtractorClip *pClip, qtractorMidiToolsForm *pMidiToolsForm ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; if (pClip == nullptr) pClip = m_pTrackView->currentClip(); if (pClip == nullptr) return false; qtractorTrack *pTrack = pClip->track(); if (pTrack == nullptr) return false; if (pTrack->trackType() != qtractorTrack::Midi) return false; qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip == nullptr) return false; if (pClipToolCommand->isLinkedMidiClip(pMidiClip)) return false; unsigned long iOffset = 0; unsigned long iLength = pClip->clipLength(); if (pClip->isClipSelected()) { iOffset = pClip->clipSelectStart() - pClip->clipStart(); iLength = pClip->clipSelectEnd() - pClip->clipSelectStart(); } qtractorMidiSequence *pSeq = pMidiClip->sequence(); const unsigned long iTimeOffset = pSeq->timeOffset(); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekFrame(pClip->clipStart()); const unsigned long t0 = pNode->tickFromFrame(pClip->clipStart()); unsigned long f1 = pClip->clipStart() + pClip->clipOffset() + iOffset; pNode = cursor.seekFrame(f1); const unsigned long t1 = pNode->tickFromFrame(f1); unsigned long iTimeStart = t1 - t0; iTimeStart = (iTimeStart > iTimeOffset ? iTimeStart - iTimeOffset : 0); pNode = cursor.seekFrame(f1 += iLength); const unsigned long iTimeEnd = iTimeStart + pNode->tickFromFrame(f1) - t1; // Emulate an user-made selection... qtractorMidiEditSelect select; const QRect rect; // Dummy event rectangle. for (qtractorMidiEvent *pEvent = pSeq->events().first(); pEvent; pEvent = pEvent->next()) { const unsigned long iTime = pEvent->time(); if (iTime >= iTimeStart && iTime < iTimeEnd) { select.addItem(pEvent, rect, rect); } } // Add new edit command from tool... pClipToolCommand->addMidiEditCommand( pMidiToolsForm->midiEditCommand(pMidiClip, &select, pSession->tickFromFrame(pClip->clipStart()), iTimeStart, iTimeEnd)); // Must be brand new revision... pMidiClip->setRevision(0); // That's it... return true; } // Import (audio) clip(s) into current track... bool qtractorTracks::importClips ( QStringList files, unsigned long iClipStart ) { // Have we some? if (files.isEmpty()) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Create on current track, or take the first... qtractorTrack *pTrack = currentTrack(); if (pTrack == nullptr) pTrack = pSession->tracks().first(); if (pTrack == nullptr) // || pTrack->trackType() != qtractorTrack::Audio) return addAudioTracks(files, iClipStart); // To log this import into session description. QString sDescription = pSession->description().trimmed(); if (!sDescription.isEmpty()) sDescription += '\n'; qtractorClipCommand *pImportClipCommand = new qtractorClipCommand(tr("clip import")); // For each one of those files... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); QStringListIterator iter(files); while (iter.hasNext()) { // This is one of the selected filenames.... const QString& sPath = iter.next(); switch (pTrack->trackType()) { case qtractorTrack::Audio: { // Add the audio clip at once... qtractorAudioClip *pAudioClip = new qtractorAudioClip(pTrack); pAudioClip->setFilename(sPath); pAudioClip->setClipStart(iClipStart); // Redundant but necessary for multi-clip // concatenation, as we only know the actual // audio clip length after opening it... if (iter.hasNext()) { pAudioClip->open(); iClipStart += pAudioClip->clipLength(); } // Will add the new clip into session on command execute... pImportClipCommand->addClip(pAudioClip, pTrack); // Don't forget to add this one to local repository. if (pMainForm) { pMainForm->addAudioFile(sPath); // Log this successful import operation... sDescription += tr("Audio file import \"%1\" on %2 %3.\n") .arg(QFileInfo(sPath).fileName()) .arg(QDate::currentDate().toString("MMM dd yyyy")) .arg(QTime::currentTime().toString("hh:mm:ss")); pMainForm->appendMessages( tr("Audio file import: \"%1\".").arg(sPath)); } break; } case qtractorTrack::Midi: { // We'll be careful and pre-open the SMF header here... qtractorMidiFile file; if (file.open(sPath)) { // Depending of SMF format... int iTrackChannel = pTrack->midiChannel(); if (file.format() == 1) ++iTrackChannel; // Add the MIDI clip at once... qtractorMidiClip *pMidiClip = new qtractorMidiClip(pTrack); pMidiClip->setFilename(sPath); pMidiClip->setTrackChannel(iTrackChannel); pMidiClip->setClipStart(iClipStart); // Redundant but necessary for multi-clip // concatenation, as we only know the actual // MIDI clip length after opening it... if (iter.hasNext()) { pMidiClip->open(); iClipStart += pMidiClip->clipLength(); } // Will add the new clip into session on command execute... pImportClipCommand->addClip(pMidiClip, pTrack); // Don't forget to add this one to local repository. if (pMainForm) { pMainForm->addMidiFile(sPath); // Log this successful import operation... sDescription += tr("MIDI file import \"%1\"" " track-channel %2 on %3 %4.\n") .arg(QFileInfo(sPath).fileName()).arg(iTrackChannel) .arg(QDate::currentDate().toString("MMM dd yyyy")) .arg(QTime::currentTime().toString("hh:mm:ss")); pMainForm->appendMessages( tr("MIDI file import: \"%1\", track-channel: %2.") .arg(sPath).arg(iTrackChannel)); } } break; } default: break; } } // Log to session (undoable by import-track command)... pSession->setDescription(sDescription); // Done. return pSession->execute(pImportClipCommand); } // Export selected clips. bool qtractorTracks::exportClips (void) { return mergeExportClips(nullptr); } // Merge selected clips. bool qtractorTracks::mergeClips (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Make it as an undoable command... qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("clip merge")); const bool bResult = mergeExportClips(pClipCommand); // Have we failed the command prospect? if (!bResult || pClipCommand->isEmpty()) { delete pClipCommand; return false; } return pSession->execute(pClipCommand); } // Merge/export selected clips. bool qtractorTracks::mergeExportClips ( qtractorClipCommand *pClipCommand ) { // Multiple clip selection: // - make sure we have at least 2 clips to merge... qtractorClipSelect *pClipSelect = m_pTrackView->clipSelect(); if (pClipSelect->items().count() < 1) return false; // Should be one single track... qtractorTrack *pTrack = pClipSelect->singleTrack(); if (pTrack == nullptr) return false; // Dispatch to specialized method... bool bResult = false; switch (pTrack->trackType()) { case qtractorTrack::Audio: bResult = mergeExportAudioClips(pClipCommand); break; case qtractorTrack::Midi: bResult = mergeExportMidiClips(pClipCommand); break; case qtractorTrack::None: default: break; } // Done most. return bResult; } // Audio clip buffer merge/export item. struct audioClipBufferItem { // Constructor. audioClipBufferItem(qtractorClip *pClip, qtractorAudioBufferThread *pSyncThread, unsigned short iChannels) : clip(static_cast (pClip)) { buff = new qtractorAudioBuffer(pSyncThread, iChannels); buff->setOffset(clip->clipOffset()); buff->setLength(clip->clipLength()); buff->setTimeStretch(clip->timeStretch()); buff->setPitchShift(clip->pitchShift()); buff->setStretcherFlags(clip->stretcherFlags()); buff->open(clip->filename()); buff->syncExport(); } // Destructor. ~audioClipBufferItem() { if (buff) delete buff; } // Members. qtractorAudioClip *clip; qtractorAudioBuffer *buff; }; // Merge/export selected(audio) clips. bool qtractorTracks::mergeExportAudioClips ( qtractorClipCommand *pClipCommand ) { // Should be one single MIDI track... qtractorClipSelect *pClipSelect = m_pTrackView->clipSelect(); qtractorTrack *pTrack = pClipSelect->singleTrack(); if (pTrack == nullptr) return false; if (pTrack->trackType() != qtractorTrack::Audio) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorAudioBus *pAudioBus = static_cast (pTrack->outputBus()); if (pAudioBus == nullptr) return false; int iFormat = -1; // alias qtractorAudioFileFactory::defaultFormat(); #if 1//QTRACTOR_EXPORT_CLIP_FORM qtractorExportClipForm exportForm(this); exportForm.setExportTitle(tr("Merge/Export")); exportForm.setExportType(qtractorTrack::Audio); const QString& sExt = exportForm.exportExt(); QString sFilename = pSession->createFilePath(pTrack->shortTrackName(), sExt); exportForm.setExportPath(sFilename); if (!exportForm.exec()) return false; sFilename = exportForm.exportPath(); iFormat = exportForm.audioExportFormat(); #else const QString& sExt = qtractorAudioFileFactory::defaultExt(); const QString& sTitle = tr("Merge/Export Audio Clip"); const QString& sFilter = qtractorAudioFileFactory::filters().join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to save... QString sFilename = QFileDialog::getSaveFileName(pParentWidget, sTitle, pSession->createFilePath(pTrack->trackName(), sExt), sFilter, nullptr, options); #else // Construct save-file dialog... QFileDialog fileDialog(pParentWidget, sTitle, pSession->createFilePath(pTrack->trackName(), sExt), sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... if (pOptions) { QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sAudioDir)); fileDialog.setSidebarUrls(urls); } fileDialog.setOptions(options); // Show dialog... if (!fileDialog.exec()) return false; QString sFilename = fileDialog.selectedFiles().first(); #endif #endif // !QTRACTOR_EXPORT_CLIP_FORM if (sFilename.isEmpty() || sFilename.at(0) == '.') return false; if (QFileInfo(sFilename).suffix().isEmpty()) sFilename += '.' + sExt; const unsigned int iBufferSize = pSession->audioEngine()->bufferSizeEx(); qtractorAudioFile *pAudioFile = qtractorAudioFileFactory::createAudioFile(sFilename, pAudioBus->channels(), pSession->sampleRate(), iBufferSize, iFormat); if (pAudioFile == nullptr) return false; // Open the file for writing... if (!pAudioFile->open(sFilename, qtractorAudioFile::Write)) { delete pAudioFile; return false; } // Should take sometime now... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Start logging... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { pMainForm->appendMessages( tr("Audio clip merge/export: \"%1\" started...") .arg(sFilename)); } // Multiple clip selection... const qtractorClipSelect::ItemList& items = pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); const unsigned short iChannels = pAudioBus->channels(); // Multi-selection extents (in frames)... QList list; unsigned long iSelectStart = pSession->sessionEnd(); unsigned long iSelectEnd = pSession->sessionStart(); for ( ; iter != iter_end; ++iter) { qtractorClip *pClip = iter.key(); qtractorTrack *pTrack = pClip->track(); // Make sure it's a legal selection... if (pTrack && pClip->isClipSelected()) { qtractorAudioBufferThread *pSyncThread = pTrack->syncThread(); if (iSelectStart > pClip->clipSelectStart()) iSelectStart = pClip->clipSelectStart(); if (iSelectEnd < pClip->clipSelectEnd()) iSelectEnd = pClip->clipSelectEnd(); list.append(new audioClipBufferItem( pClip, pSyncThread, iChannels)); } } // A progress indication might be friendly... QProgressBar *pProgressBar = nullptr; if (pMainForm) pProgressBar = pMainForm->progressBar(); if (pProgressBar) { pProgressBar->setRange(0, (iSelectEnd - iSelectStart) / 100); pProgressBar->reset(); pProgressBar->show(); } // Allocate merge audio scratch buffer... const unsigned int iBlockSize = pSession->audioEngine()->blockSize(); unsigned short i; float **ppFrames = new float * [iChannels]; for (i = 0; i < iChannels; ++i) ppFrames[i] = new float[iBlockSize]; // Setup clip buffers... QListIterator it(list); while (it.hasNext()) { audioClipBufferItem *pItem = it.next(); qtractorAudioClip *pClip = pItem->clip; qtractorAudioBuffer *pBuff = pItem->buff; // Almost similar to qtractorAudioClip::process(0)... const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipEnd = iClipStart + pClip->clipLength(); unsigned long iOffset = 0; if (iSelectStart > iClipStart && iSelectStart < iClipEnd) iOffset = iSelectStart - iClipStart; // Make it initially filled... pBuff->seek(iOffset); // pBuff->syncExport(); } // Loop-merge audio clips... unsigned long iFrameStart = iSelectStart; unsigned long iFrameEnd = iFrameStart + iBlockSize; int count = 0; // Loop until EOF... while (iFrameStart < iSelectEnd && iFrameEnd > iSelectStart) { // Zero-silence on scratch buffers... for (i = 0; i < iChannels; ++i) ::memset(ppFrames[i], 0, iBlockSize * sizeof(float)); // Merge clips in window... it.toFront(); while (it.hasNext()) { audioClipBufferItem *pItem = it.next(); qtractorAudioClip *pClip = pItem->clip; qtractorAudioBuffer *pBuff = pItem->buff; // Should force sync now and then... if ((count % 33) == 0) pBuff->syncExport(); // Quite similar to qtractorAudioClip::process()... const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipEnd = iClipStart + pClip->clipLength();; const float fGain = pClip->clipGain(); if (iFrameStart < iClipStart && iFrameEnd > iClipStart) { const unsigned long iOffset = iFrameEnd - iClipStart; while (!pBuff->inSync(0, 0)) pBuff->syncExport(); pBuff->readMix(ppFrames, iOffset, iChannels, iClipStart - iFrameStart, fGain * pClip->fadeInOutGain(iOffset)); } else if (iFrameStart >= iClipStart && iFrameStart < iClipEnd) { const unsigned long iFrame = iFrameStart - iClipStart; while (!pBuff->inSync(iFrame, iFrame)) pBuff->syncExport(); pBuff->readMix(ppFrames, iBlockSize, iChannels, 0, fGain * pClip->fadeInOutGain(iFrameEnd - iClipStart)); } } // Actually write to merge audio file; // - check for last incomplete block... if (iFrameEnd > iSelectEnd) pAudioFile->write(ppFrames, iBlockSize - (iFrameEnd - iSelectEnd)); else pAudioFile->write(ppFrames, iBlockSize); // Advance to next buffer... iFrameStart = iFrameEnd; iFrameEnd = iFrameStart + iBlockSize; if (++count > 100 && pProgressBar) { pProgressBar->setValue(pProgressBar->value() + iBlockSize); qtractorSession::stabilize(); count = 0; } } for (i = 0; i < iChannels; ++i) delete [] ppFrames[i]; delete [] ppFrames; qDeleteAll(list); list.clear(); // Close and free it up... pAudioFile->close(); delete pAudioFile; if (pProgressBar) pProgressBar->hide(); // Stop logging... if (pMainForm) { pMainForm->addAudioFile(sFilename); pMainForm->appendMessages( tr("Audio clip merge/export: \"%1\" complete.") .arg(sFilename)); } // The resulting merge comands, if any... if (pClipCommand) { iter = items.constBegin(); for ( ; iter != iter_end; ++iter) { qtractorClip *pClip = iter.key(); // Clip parameters. const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipOffset = pClip->clipOffset(); const unsigned long iClipLength = pClip->clipLength(); const unsigned long iClipEnd = iClipStart + iClipLength; // Determine and keep clip regions... if (iSelectStart > iClipStart) { // -- Left clip... pClipCommand->resizeClip(pClip, iClipStart, iClipOffset, iSelectStart - iClipStart); // Done, left clip. } else if (iSelectEnd < iClipEnd) { // -- Right clip... pClipCommand->resizeClip(pClip, iSelectEnd, iClipOffset + (iSelectEnd - iClipStart), iClipEnd - iSelectEnd); // Done, right clip. } else { // -- Inner clip... pClipCommand->removeClip(pClip); // Done, inner clip. } } // Set the resulting clip command... qtractorAudioClip *pNewClip = new qtractorAudioClip(pTrack); pNewClip->setClipStart(iSelectStart); pNewClip->setClipLength(iSelectEnd - iSelectStart); pNewClip->setFilename(sFilename); pClipCommand->addClip(pNewClip, pTrack); } // Almost done with it... QApplication::restoreOverrideCursor(); // That's it... return true; } // Merge/export selected(MIDI) clips. bool qtractorTracks::mergeExportMidiClips ( qtractorClipCommand *pClipCommand ) { // Should be one single MIDI track... qtractorClipSelect *pClipSelect = m_pTrackView->clipSelect(); qtractorTrack *pTrack = pClipSelect->singleTrack(); if (pTrack == nullptr) return false; if (pTrack->trackType() != qtractorTrack::Midi) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; int iFormat = qtractorMidiClip::defaultFormat(); #if 1//QTRACTOR_EXPORT_CLIP_FORM qtractorExportClipForm exportForm(this); exportForm.setExportTitle(tr("Merge/Export")); exportForm.setExportType(qtractorTrack::Midi); const QString& sExt = exportForm.exportExt(); QString sFilename = pSession->createFilePath(pTrack->shortTrackName(), sExt); exportForm.setExportPath(sFilename); if (!exportForm.exec()) return false; sFilename = exportForm.exportPath(); iFormat = exportForm.midiExportFormat(); #else // Merge MIDI Clip filename requester... const QString sExt("mid"); const QString& sTitle = tr("Merge/Export MIDI Clip"); QStringList filters; filters.append(tr("MIDI files (*.mid *.smf *.midi)")); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to save... QString sFilename = QFileDialog::getSaveFileName(pParentWidget, sTitle, pSession->createFilePath(pTrack->trackName(), sExt), sFilter, nullptr, options); #else // Construct save-file dialog... QFileDialog fileDialog(pParentWidget, sTitle, pSession->createFilePath(pTrack->trackName(), sExt), sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... if (pOptions) { QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sMidiDir)); fileDialog.setSidebarUrls(urls); } fileDialog.setOptions(options); // Show dialog... if (!fileDialog.exec()) return false; QString sFilename = fileDialog.selectedFiles().first(); #endif #endif // !QTRACTOR_EXPORT_CLIP_FORM if (sFilename.isEmpty() || sFilename.at(0) == '.') return false; if (QFileInfo(sFilename).suffix().isEmpty()) sFilename += '.' + sExt; // Should take sometime... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Create SMF... qtractorMidiFile file; if (!file.open(sFilename, qtractorMidiFile::Write)) { QApplication::restoreOverrideCursor(); return false; } // Write SMF header... const unsigned short iTicksPerBeat = pSession->ticksPerBeat(); const unsigned short iTracks = (iFormat == 0 ? 1 : 2); if (!file.writeHeader(iFormat, iTracks, iTicksPerBeat)) { QApplication::restoreOverrideCursor(); return false; } // Start logging... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { pMainForm->appendMessages( tr("MIDI clip merge/export: \"%1\" started...") .arg(sFilename)); } // Multiple clip selection... const qtractorClipSelect::ItemList& items = pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); // Multi-selection extents (in frames)... unsigned long iSelectStart = pSession->sessionEnd(); unsigned long iSelectEnd = pSession->sessionStart(); for ( ; iter != iter_end; ++iter) { qtractorClip *pClip = iter.key(); // Make sure it's a legal selection... if (pClip->track() && pClip->isClipSelected()) { if (iSelectStart > pClip->clipSelectStart()) iSelectStart = pClip->clipSelectStart(); if (iSelectEnd < pClip->clipSelectEnd()) iSelectEnd = pClip->clipSelectEnd(); } } // Multi-selection extents (in ticks)... const unsigned long iTimeStart = pSession->tickFromFrame(iSelectStart); const unsigned long iTimeEnd = pSession->tickFromFrame(iSelectEnd); // Set proper tempo map... if (file.tempoMap()) { file.tempoMap()->fromTimeScale( pSession->timeScale(), iTimeStart); } // Setup track (SMF format 1). if (iFormat == 1) file.writeTrack(nullptr); // Setup merge sequence... qtractorMidiSequence seq(pTrack->shortTrackName(), 0, iTicksPerBeat); seq.setChannel(pTrack->midiChannel()); seq.setBankSelMethod(pTrack->midiBankSelMethod()); seq.setBank(pTrack->midiBank()); seq.setProg(pTrack->midiProg()); // The merge... iter = items.constBegin(); for ( ; iter != iter_end; ++iter) { qtractorClip *pClip = iter.key(); // Make sure it's a legal selection... if (pClip->track() && pClip->isClipSelected()) { // Clip parameters. const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipOffset = pClip->clipOffset(); const unsigned long iClipLength = pClip->clipLength(); const unsigned long iClipEnd = iClipStart + iClipLength; // Determine and keep clip regions... if (pClipCommand) { if (iSelectStart > iClipStart) { // -- Left clip... pClipCommand->resizeClip(pClip, iClipStart, iClipOffset, iSelectStart - iClipStart); // Done, left clip. } else if (iSelectEnd < iClipEnd) { // -- Right clip... pClipCommand->resizeClip(pClip, iSelectEnd, iClipOffset + (iSelectEnd - iClipStart), iClipEnd - iSelectEnd); // Done, right clip. } else { // -- Inner clip... pClipCommand->removeClip(pClip); // Done, inner clip. } } // Do the MIDI merge, itself... qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) { const unsigned long iTimeClip = pSession->tickFromFrame(pClip->clipStart()); const unsigned long iTimeOffset = iTimeClip - iTimeStart; const float fGain = pMidiClip->clipGain(); // For each event... qtractorMidiEvent *pEvent = pMidiClip->sequence()->events().first(); while (pEvent && iTimeClip + pEvent->time() < iTimeStart) pEvent = pEvent->next(); while (pEvent && iTimeClip + pEvent->time() < iTimeEnd) { qtractorMidiEvent *pNewEvent = new qtractorMidiEvent(*pEvent); pNewEvent->setTime(iTimeOffset + pEvent->time()); if (pNewEvent->type() == qtractorMidiEvent::NOTEON) { const unsigned long iTimeEvent = iTimeClip + pEvent->time(); const float fVolume = fGain * pMidiClip->fadeInOutGain( pSession->frameFromTick(iTimeEvent) - pClip->clipStart()); pNewEvent->setVelocity((unsigned char) (fVolume * float(pEvent->velocity())) & 0x7f); if (iTimeEvent + pEvent->duration() > iTimeEnd) pNewEvent->setDuration(iTimeEnd - iTimeEvent); } seq.insertEvent(pNewEvent); pEvent = pEvent->next(); } } } } // Write the track and close SMF... file.writeTrack(&seq); file.close(); // Stop logging... if (pMainForm) { pMainForm->addMidiFile(sFilename); pMainForm->appendMessages( tr("MIDI clip merge/export: \"%1\" complete.") .arg(sFilename)); } // Set the resulting clip command... if (pClipCommand) { qtractorMidiClip *pNewClip = new qtractorMidiClip(pTrack); pNewClip->setClipStart(iSelectStart); pNewClip->setClipLength(iSelectEnd - iSelectStart); pNewClip->setFilename(sFilename); pNewClip->setTrackChannel(iFormat == 0 ? seq.channel() : 1); pClipCommand->addClip(pNewClip, pTrack); } // Almost done with it... QApplication::restoreOverrideCursor(); // That's it... return true; } // Edit/loop-range from current clip settlers. bool qtractorTracks::rangeClip ( qtractorClip *pClip ) { return rangeClipEx(pClip, false); } bool qtractorTracks::loopClip ( qtractorClip *pClip ) { return rangeClipEx(pClip, true); } bool qtractorTracks::rangeClipEx ( qtractorClip *pClip, bool bLoopSet ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; unsigned long iEditHead = 0; unsigned long iEditTail = 0; // Multiple clip selection... if (pClip == nullptr && isClipSelected()) { // Multi-selection extents (in frames)... iEditHead = pSession->sessionEnd(); iEditTail = pSession->sessionStart(); qtractorClipSelect *pClipSelect = m_pTrackView->clipSelect(); const qtractorClipSelect::ItemList& items = pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { pClip = iter.key(); // Make sure it's a legal selection... if (pClip->isClipSelected()) { const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipEnd = iClipStart + pClip->clipLength(); if (iEditHead > iClipStart) iEditHead = iClipStart; if (iEditTail < iClipEnd) iEditTail = iClipEnd; } } } else { if (pClip == nullptr) pClip = m_pTrackView->currentClip(); if (pClip) { iEditHead = pClip->clipStart(); iEditTail = iEditHead + pClip->clipLength(); } } pSession->setEditHead(iEditHead); pSession->setEditTail(iEditTail); if (bLoopSet) { if (pSession->isLooping() && iEditHead == pSession->loopStart() && iEditTail == pSession->loopEnd()) { iEditHead = iEditTail = 0; } pSession->execute( new qtractorSessionLoopCommand(pSession, iEditHead, iEditTail)); return (iEditHead < iEditTail); } selectionChangeNotify(); return true; } // Adjust current tempo from clip selection or interactive tapping... bool qtractorTracks::tempoClip ( qtractorClip *pClip ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; unsigned long iRangeStart = pSession->editHead(); unsigned long iRangeLength = pSession->editTail() - iRangeStart; if (pClip == nullptr) pClip = m_pTrackView->currentClip(); if (pClip) { if (pClip->isClipSelected()) { iRangeStart = pClip->clipSelectStart(); iRangeLength = pClip->clipSelectEnd() - iRangeStart; } else { iRangeStart = pClip->clipStart(); iRangeLength = pClip->clipLength(); } } qtractorTempoAdjustForm form(this); form.setClip(pClip); form.setRangeStart(iRangeStart); form.setRangeLength(iRangeLength); if (!form.exec()) return false; // Avoid automatic time stretching option for audio clips... const bool bAutoTimeStretch = pSession->isAutoTimeStretch(); pSession->setAutoTimeStretch(false); iRangeStart = form.rangeStart(); iRangeLength = form.rangeLength(); // Find appropriate node... qtractorTimeScale *pTimeScale = pSession->timeScale(); qtractorTimeScale::Cursor& cursor = pTimeScale->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(iRangeStart); // Now, express the change as a undoable command... pSession->execute( new qtractorTimeScaleUpdateNodeCommand(pTimeScale, pNode->frame, form.tempo(), 2, form.beatsPerBar(), form.beatDivisor())); // Done. pSession->setAutoTimeStretch(bAutoTimeStretch); if (pClip) { if (pClip->isClipSelected()) { iRangeStart = pClip->clipSelectStart(); iRangeLength = pClip->clipSelectEnd() - iRangeStart; } else { iRangeStart = pClip->clipStart(); iRangeLength = pClip->clipLength(); } } pSession->setEditHead(iRangeStart); pSession->setEditTail(iRangeStart + iRangeLength); selectionChangeNotify(); return true; } // Auto-crossfade a give clip. bool qtractorTracks::crossFadeClip ( qtractorClip *pClip ) { if (pClip == nullptr) pClip = m_pTrackView->currentClip(); if (pClip == nullptr) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // We'll build a command... qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("clip cross-fade")); QList clips; // Multiple clip selection... if (isClipSelected()) { qtractorClipSelect *pClipSelect = m_pTrackView->clipSelect(); const qtractorClipSelect::ItemList& items = pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { // Make sure it's legal selection... pClip = iter.key(); if (pClip->track() && pClip->isClipSelected()) clips.append(pClip); } } // Single, current clip instead? else clips.append(pClip); QListIterator clip_iter(clips); while (clip_iter.hasNext()) { pClip = clip_iter.next(); qtractorTrack *pTrack = pClip->track(); if (pTrack == nullptr) continue; const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipEnd = iClipStart + pClip->clipLength(); qtractorClip *pClip2 = pTrack->clips().first(); while (pClip2 && pClip2->clipStart() < iClipEnd) { // Avoid cross-fading over the very self... const unsigned long iClipStart2 = pClip2->clipStart(); const unsigned long iClipEnd2 = iClipStart2 + pClip2->clipLength(); if (iClipEnd2 > iClipStart && iClipStart > iClipStart2) { const unsigned long iCrossFadeLength = iClipEnd2 - iClipStart; if (pClip2->fadeOutLength() != iCrossFadeLength) { pClipCommand->fadeOutClip(pClip2, iCrossFadeLength, pClip2->fadeOutType()); } if (pClip->fadeInLength() != iCrossFadeLength) { pClipCommand->fadeInClip(pClip, iCrossFadeLength, pClip->fadeInType()); } } else if (iClipStart2 < iClipEnd && iClipEnd < iClipEnd2) { const unsigned long iCrossFadeLength = iClipEnd - iClipStart2; if (pClip->fadeOutLength() != iCrossFadeLength) { pClipCommand->fadeOutClip(pClip, iCrossFadeLength, pClip->fadeOutType()); } if (pClip2->fadeInLength() != iCrossFadeLength) { pClipCommand->fadeInClip(pClip2, iCrossFadeLength, pClip2->fadeInType()); } } // Move forward... pClip2 = pClip2->next(); } } // Check if valid... if (pClipCommand->isEmpty()) { delete pClipCommand; return false; } // Make it undoable command return pSession->execute(pClipCommand); } // Whether there's anything currently selected. bool qtractorTracks::isSelected (void) const { return isClipSelected() || isCurveSelected(); } // Whether there's any clip currently selected. bool qtractorTracks::isClipSelected (void) const { return m_pTrackView->isClipSelected(); } // Whether there's any curve/automation currently selected. bool qtractorTracks::isCurveSelected (void) const { return m_pTrackView->isCurveSelected(); } // Whether there's a single track selection. qtractorTrack *qtractorTracks::singleTrackSelected (void) { return m_pTrackView->singleTrackSelected(); } // Retrieve actual clip selection range. void qtractorTracks::clipSelectedRange ( unsigned long& iSelectStart, unsigned long& iSelectEnd ) const { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; iSelectStart = pSession->sessionEnd(); iSelectEnd = pSession->sessionStart(); qtractorClipSelect *pClipSelect = m_pTrackView->clipSelect(); const qtractorClipSelect::ItemList& items = pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorClip *pClip = iter.key(); qtractorTrack *pTrack = pClip->track(); // Make sure it's a legal selection... if (pTrack && pClip->isClipSelected()) { if (iSelectStart > pClip->clipSelectStart()) iSelectStart = pClip->clipSelectStart(); if (iSelectEnd < pClip->clipSelectEnd()) iSelectEnd = pClip->clipSelectEnd(); } } } // Clipboard methods. void qtractorTracks::cutClipboard (void) { if (m_pTrackView->isCurveEdit()) m_pTrackView->executeCurveSelect(qtractorTrackView::Cut); else m_pTrackView->executeClipSelect(qtractorTrackView::Cut); } void qtractorTracks::copyClipboard (void) { if (m_pTrackView->isCurveEdit()) m_pTrackView->executeCurveSelect(qtractorTrackView::Copy); else m_pTrackView->executeClipSelect(qtractorTrackView::Copy); } void qtractorTracks::pasteClipboard (void) { m_pTrackView->pasteClipboard(); } // Special paste/repeat prompt. void qtractorTracks::pasteRepeatClipboard (void) { qtractorPasteRepeatForm pasteForm(this); pasteForm.setRepeatPeriod(m_pTrackView->pastePeriod()); if (pasteForm.exec()) { m_pTrackView->pasteClipboard( pasteForm.repeatCount(), pasteForm.repeatPeriod()); } } // Delete current selection. void qtractorTracks::deleteSelect (void) { if (m_pTrackView->isCurveEdit()) m_pTrackView->executeCurveSelect(qtractorTrackView::Delete); else m_pTrackView->executeClipSelect(qtractorTrackView::Delete); } // Split selection method. void qtractorTracks::splitSelect (void) { m_pTrackView->executeClipSelect(qtractorTrackView::Split); } // Select range interval between edit head and tail. void qtractorTracks::selectEditRange ( bool bReset ) { if (m_pTrackView->isCurveEdit()) m_pTrackView->selectCurveTrackRange(nullptr, bReset); else m_pTrackView->selectClipTrackRange(nullptr, bReset); } // Select all clips on current track. void qtractorTracks::selectCurrentTrack ( bool bReset ) { qtractorTrack *pTrack = currentTrack(); if (pTrack == nullptr) return; if (m_pTrackView->isCurveEdit()) m_pTrackView->selectCurveTrack(pTrack, bReset); else m_pTrackView->selectClipTrack(pTrack, bReset); } // Select all clips on current track range. void qtractorTracks::selectCurrentTrackRange ( bool bReset ) { qtractorTrack *pTrack = currentTrack(); if (pTrack == nullptr) return; if (m_pTrackView->isCurveEdit()) m_pTrackView->selectCurveTrackRange(pTrack, bReset); else m_pTrackView->selectClipTrackRange(pTrack, bReset); } // Select everything on all tracks. void qtractorTracks::selectAll (void) { if (m_pTrackView->isCurveEdit()) m_pTrackView->selectCurveAll(); else m_pTrackView->selectClipAll(); } // Select nothing on all tracks. void qtractorTracks::selectNone (void) { m_pTrackView->clearSelect(); selectionChangeNotify(); } // Invert selection on all tracks and clips. void qtractorTracks::selectInvert (void) { if (m_pTrackView->isCurveEdit()) m_pTrackView->selectCurveInvert(); else m_pTrackView->selectClipInvert(); } // Insertion method. bool qtractorTracks::insertEditRange ( qtractorTrack *pTrack ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return false; unsigned long iInsertStart = pSession->editHead(); unsigned long iInsertEnd = pSession->editTail(); if (iInsertStart >= iInsertEnd) { unsigned short iBar = pTimeScale->barFromFrame(iInsertStart); iInsertEnd = pTimeScale->frameFromBar(iBar + 1); } unsigned int iInsertOptions = qtractorEditRangeForm::None; iInsertOptions |= qtractorEditRangeForm::Clips; iInsertOptions |= qtractorEditRangeForm::Automation; if (pTrack == nullptr) { qtractorEditRangeForm rangeForm(this); rangeForm.setWindowTitle(tr("Insert Range")); if (isClipSelected()) clipSelectedRange(iInsertStart, iInsertEnd); rangeForm.setSelectionRange(iInsertStart, iInsertEnd); if (!rangeForm.exec()) return false; iInsertStart = rangeForm.rangeStart(); iInsertEnd = rangeForm.rangeEnd(); iInsertOptions = rangeForm.rangeOptions(); } if (iInsertStart >= iInsertEnd) return false; const unsigned long iInsertLength = iInsertEnd - iInsertStart; int iUpdate = 0; qtractorClipRangeCommand *pClipRangeCommand = new qtractorClipRangeCommand(pTrack == nullptr ? tr("insert range") : tr("insert track range")); if (pTrack) { iUpdate += insertEditRangeTrack(pClipRangeCommand, pTrack, iInsertStart, iInsertEnd, iInsertOptions); } else { // Clips & Automation... pTrack = pSession->tracks().first(); while (pTrack) { iUpdate += insertEditRangeTrack(pClipRangeCommand, pTrack, iInsertStart, iInsertEnd, iInsertOptions); pTrack = pTrack->next(); } // Loop... if (iInsertOptions & qtractorEditRangeForm::Loop) { unsigned long iLoopStart = pSession->loopStart(); unsigned long iLoopEnd = pSession->loopEnd(); if (iLoopStart < iLoopEnd) { int iLoopUpdate = 0; if (iLoopStart > iInsertStart) { iLoopStart += iInsertLength; ++iLoopUpdate; } if (iLoopEnd > iInsertStart) { iLoopEnd += iInsertLength; ++iLoopUpdate; } if (iLoopUpdate > 0) { pClipRangeCommand->addSessionCommand( new qtractorSessionLoopCommand(pSession, iLoopStart, iLoopEnd)); ++iUpdate; } } } // Punch In/Out... if (iInsertOptions & qtractorEditRangeForm::Punch) { unsigned long iPunchIn = pSession->punchOut(); unsigned long iPunchOut = pSession->punchIn(); if (iPunchIn < iPunchOut) { int iPunchUpdate = 0; if (iPunchIn > iInsertStart) { iPunchIn += iInsertLength; ++iPunchUpdate; } if (iPunchOut > iInsertStart) { iPunchOut += iInsertLength; ++iPunchUpdate; } if (iPunchUpdate > 0) { pClipRangeCommand->addSessionCommand( new qtractorSessionPunchCommand(pSession, iPunchIn, iPunchOut)); ++iUpdate; } } } // Markers... if (iInsertOptions & qtractorEditRangeForm::Markers) { qtractorTimeScale::Marker *pMarker = pTimeScale->markers().last(); while (pMarker && pMarker->frame > iInsertStart) { pClipRangeCommand->addTimeScaleMarkerCommand( new qtractorTimeScaleMoveMarkerCommand(pTimeScale, pMarker, pMarker->frame + iInsertLength)); pMarker = pMarker->prev(); ++iUpdate; } } // Tempo-map... if (iInsertOptions & qtractorEditRangeForm::TempoMap) { qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(pSession->sessionEnd()); while (pNode && pNode->frame > iInsertStart) { pClipRangeCommand->addTimeScaleNodeCommand( new qtractorTimeScaleMoveNodeCommand(pTimeScale, pNode, pNode->frame + iInsertLength)); pNode = pNode->prev(); ++iUpdate; } } } if (iUpdate < 1) { delete pClipRangeCommand; return false; } const bool bResult = pSession->execute(pClipRangeCommand); if (bResult) { pSession->setEditHead(iInsertStart); pSession->setEditTail(iInsertEnd); selectionChangeNotify(); } return bResult; } // Insertion method (track). int qtractorTracks::insertEditRangeTrack ( qtractorClipRangeCommand *pClipRangeCommand, qtractorTrack *pTrack, unsigned long iInsertStart, unsigned long iInsertEnd, unsigned int iInsertOptions ) const { const unsigned long iInsertLength = iInsertEnd - iInsertStart; int iUpdate = 0; if (iInsertOptions & qtractorEditRangeForm::Clips) { qtractorClip *pClip = pTrack->clips().first(); while (pClip) { const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipOffset = pClip->clipOffset(); const unsigned long iClipLength = pClip->clipLength(); const unsigned long iClipEnd = iClipStart + iClipLength; if (iClipEnd > iInsertStart) { // Slip/move clip... if (iClipStart < iInsertStart) { // Left-clip... pClipRangeCommand->resizeClip(pClip, iClipStart, iClipOffset, iInsertStart - iClipStart); // Right-clip... qtractorClip *pClipEx = m_pTrackView->cloneClip(pClip); if (pClipEx) { const unsigned long iClipOffset2 = iClipOffset + iInsertStart - iClipStart; pClipEx->setClipStart(iInsertEnd); pClipEx->setClipOffset(iClipOffset2); pClipEx->setClipLength(iClipEnd - iInsertStart); pClipEx->setFadeOutLength(pClip->fadeOutLength()); pClipRangeCommand->addClip(pClipEx, pTrack); } } else { // Whole-clip... pClipRangeCommand->moveClip(pClip, pTrack, iClipStart + iInsertLength, iClipOffset, iClipLength); } ++iUpdate; } pClip = pClip->next(); } } if (iInsertOptions & qtractorEditRangeForm::Automation) { qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList) { qtractorCurve *pCurve = pCurveList->first(); while (pCurve) { int iCurveEditUpdate = 0; qtractorCurveEditCommand *pCurveEditCommand = new qtractorCurveEditCommand(QString(), pCurve); qtractorCurve::Node *pNode = pCurve->seek(iInsertStart); while (pNode) { pCurveEditCommand->moveNode(pNode, pNode->frame + iInsertLength, pNode->value); ++iCurveEditUpdate; pNode = pNode->next(); } if (iCurveEditUpdate > 0) { pClipRangeCommand->addCurveEditCommand(pCurveEditCommand); iUpdate += iCurveEditUpdate; } else delete pCurveEditCommand; pCurve = pCurve->next(); } } } return iUpdate; } // Removal method. bool qtractorTracks::removeEditRange ( qtractorTrack *pTrack ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return false; unsigned long iRemoveStart = pSession->editHead(); unsigned long iRemoveEnd = pSession->editTail(); if (iRemoveStart >= iRemoveEnd) { unsigned short iBar = pTimeScale->barFromFrame(iRemoveStart); iRemoveEnd = pTimeScale->frameFromBar(iBar + 1); } unsigned int iRemoveOptions = qtractorEditRangeForm::None; iRemoveOptions |= qtractorEditRangeForm::Clips; iRemoveOptions |= qtractorEditRangeForm::Automation; if (pTrack == nullptr) { qtractorEditRangeForm rangeForm(this); rangeForm.setWindowTitle(tr("Remove Range")); if (isClipSelected()) clipSelectedRange(iRemoveStart, iRemoveEnd); rangeForm.setSelectionRange(iRemoveStart, iRemoveEnd); if (!rangeForm.exec()) return false; iRemoveStart = rangeForm.rangeStart(); iRemoveEnd = rangeForm.rangeEnd(); iRemoveOptions = rangeForm.rangeOptions(); } if (iRemoveStart >= iRemoveEnd) return false; const unsigned long iRemoveLength = iRemoveEnd - iRemoveStart; int iUpdate = 0; qtractorClipRangeCommand *pClipRangeCommand = new qtractorClipRangeCommand(pTrack == nullptr ? tr("remove range") : tr("remove track range")); if (pTrack) { iUpdate += removeEditRangeTrack(pClipRangeCommand, pTrack, iRemoveStart, iRemoveEnd, iRemoveOptions); } else { // Clips & Automation... pTrack = pSession->tracks().first(); while (pTrack) { iUpdate += removeEditRangeTrack(pClipRangeCommand, pTrack, iRemoveStart, iRemoveEnd, iRemoveOptions); pTrack = pTrack->next(); } // Loop... if (iRemoveOptions & qtractorEditRangeForm::Loop) { unsigned long iLoopStart = pSession->loopStart(); unsigned long iLoopEnd = pSession->loopEnd(); if (iLoopStart < iLoopEnd) { int iLoopUpdate = 0; if (iLoopStart > iRemoveStart) { if (iLoopStart > iRemoveEnd) iLoopStart = iRemoveStart; else iLoopStart -= iRemoveLength; ++iLoopUpdate; } if (iLoopEnd > iRemoveStart) { if (iLoopEnd > iRemoveEnd) iLoopEnd -= iRemoveLength; else iLoopEnd = iRemoveEnd; ++iLoopUpdate; } if (iLoopUpdate > 0) { pClipRangeCommand->addSessionCommand( new qtractorSessionLoopCommand(pSession, iLoopStart, iLoopEnd)); ++iUpdate; } } } // Punch In/Out... if (iRemoveOptions & qtractorEditRangeForm::Punch) { unsigned long iPunchIn = pSession->punchOut(); unsigned long iPunchOut = pSession->punchIn(); if (iPunchIn < iPunchOut) { int iPunchUpdate = 0; if (iPunchIn > iRemoveStart) { if (iPunchIn > iRemoveEnd) iPunchIn = iRemoveStart; else iPunchIn -= iRemoveLength; ++iPunchUpdate; } if (iPunchOut > iRemoveStart) { if (iPunchOut > iRemoveEnd) iPunchOut -= iRemoveLength; else iPunchOut = iRemoveEnd; ++iPunchUpdate; } if (iPunchUpdate > 0) { pClipRangeCommand->addSessionCommand( new qtractorSessionPunchCommand(pSession, iPunchIn, iPunchOut)); ++iUpdate; } } } // Markers... if (iRemoveOptions & qtractorEditRangeForm::Markers) { qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekFrame(iRemoveStart); while (pMarker) { if (pMarker->frame > iRemoveStart) { if (pMarker->frame < iRemoveEnd) { pClipRangeCommand->addTimeScaleMarkerCommand( new qtractorTimeScaleRemoveMarkerCommand( pTimeScale, pMarker)); ++iUpdate; } else if (pMarker->frame > iRemoveEnd) { pClipRangeCommand->addTimeScaleMarkerCommand( new qtractorTimeScaleMoveMarkerCommand(pTimeScale, pMarker, pMarker->frame - iRemoveLength)); ++iUpdate; } } pMarker = pMarker->next(); } } // Tempo-map... if (iRemoveOptions & qtractorEditRangeForm::TempoMap) { qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(iRemoveStart); while (pNode) { if (pNode->frame > iRemoveStart) { if (pNode->frame < iRemoveEnd) { pClipRangeCommand->addTimeScaleNodeCommand( new qtractorTimeScaleRemoveNodeCommand( pTimeScale, pNode)); ++iUpdate; } else if (pNode->frame > iRemoveEnd) { pClipRangeCommand->addTimeScaleNodeCommand( new qtractorTimeScaleMoveNodeCommand(pTimeScale, pNode, pNode->frame - iRemoveLength)); ++iUpdate; } } pNode = pNode->next(); } } } if (iUpdate < 1) { delete pClipRangeCommand; return false; } clearSelect(true); const bool bResult = pSession->execute(pClipRangeCommand); if (bResult) { pSession->setEditHead(iRemoveStart); pSession->setEditTail(iRemoveEnd); selectionChangeNotify(); } return bResult; } // Removal method (track). int qtractorTracks::removeEditRangeTrack ( qtractorClipRangeCommand *pClipRangeCommand, qtractorTrack *pTrack, unsigned long iRemoveStart, unsigned long iRemoveEnd, unsigned int iRemoveOptions ) const { const unsigned long iRemoveLength = iRemoveEnd - iRemoveStart; int iUpdate = 0; if (iRemoveOptions & qtractorEditRangeForm::Clips) { qtractorClip *pClip = pTrack->clips().first(); while (pClip) { const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipOffset = pClip->clipOffset(); const unsigned long iClipLength = pClip->clipLength(); const unsigned long iClipEnd = iClipStart + iClipLength; if (iClipEnd > iRemoveStart) { // Slip/move clip... if (iClipStart < iRemoveEnd) { // Left-clip... if (iClipStart < iRemoveStart) { pClipRangeCommand->resizeClip(pClip, iClipStart, iClipOffset, iRemoveStart - iClipStart); // Right-clip... if (iClipEnd > iRemoveEnd) { qtractorClip *pClipEx = m_pTrackView->cloneClip(pClip); if (pClipEx) { const unsigned long iClipOffset2 = iClipOffset + iRemoveEnd - iClipStart; pClipEx->setClipStart(iRemoveStart); pClipEx->setClipOffset(iClipOffset2); pClipEx->setClipLength(iClipEnd - iRemoveEnd); pClipEx->setFadeOutLength(pClip->fadeOutLength()); pClipRangeCommand->addClip(pClipEx, pTrack); } } } else if (iClipEnd > iRemoveEnd) { pClipRangeCommand->resizeClip(pClip, iRemoveStart, iClipOffset + iRemoveEnd - iClipStart, iClipEnd - iRemoveEnd); } else { pClipRangeCommand->removeClip(pClip); } } else { // Whole-clip... pClipRangeCommand->moveClip(pClip, pTrack, iClipStart - iRemoveLength, iClipOffset, iClipLength); } ++iUpdate; } pClip = pClip->next(); } } if (iRemoveOptions & qtractorEditRangeForm::Automation) { qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList) { qtractorCurve *pCurve = pCurveList->first(); while (pCurve) { int iCurveEditUpdate = 0; qtractorCurveEditCommand *pCurveEditCommand = new qtractorCurveEditCommand(QString(), pCurve); qtractorCurve::Node *pNode = pCurve->seek(iRemoveStart); while (pNode) { if (pNode->frame < iRemoveEnd) pCurveEditCommand->removeNode(pNode); else pCurveEditCommand->moveNode(pNode, pNode->frame - iRemoveLength, pNode->value); ++iCurveEditUpdate; pNode = pNode->next(); } if (iCurveEditUpdate > 0) { pClipRangeCommand->addCurveEditCommand(pCurveEditCommand); iUpdate += iCurveEditUpdate; } else delete pCurveEditCommand; pCurve = pCurve->next(); } } } return iUpdate; } // Adds a new track into session. bool qtractorTracks::addTrack (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Create a new track right away... const int iTrack = pSession->tracks().count() + 1; const QColor& color = qtractorTrack::trackColor(iTrack); qtractorTrack *pTrack = new qtractorTrack(pSession); pTrack->setTrackName( pSession->uniqueTrackName(QString("Track %1").arg(iTrack))); pTrack->setMidiChannel(pSession->midiTag() % 16); pTrack->setBackground(color); pTrack->setForeground(color.darker()); // Open dialog for settings... QWidget *pParent = QApplication::activeWindow(); if (pParent == nullptr) pParent = static_cast (qtractorMainForm::getInstance()); qtractorTrackForm trackForm(pParent); trackForm.setTrack(pTrack); if (!trackForm.exec()) { delete pTrack; return false; } // Might take a while... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Take care of user supplied properties... pTrack->setProperties(trackForm.properties()); // Put it in the form of an undoable command... const bool bResult = pSession->execute( new qtractorAddTrackCommand(pTrack, currentTrack())); QApplication::restoreOverrideCursor(); return bResult; } // Remove given(current) track from session. bool qtractorTracks::removeTrack ( qtractorTrack *pTrack ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Get the list view item reference of the intended track... if (pTrack == nullptr) pTrack = currentTrack(); if (pTrack == nullptr) return false; // Don't remove tracks engaged in recording... if (pTrack->isRecord() && pSession->isRecording() && pSession->isPlaying()) return false; // Prompt user if he/she's sure about this... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bConfirmRemove) { if (QMessageBox::warning(this, tr("Warning"), tr("About to remove track:\n\n" "\"%1\"\n\n" "Are you sure?") .arg(pTrack->shortTrackName()), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return false; } // Might take a while... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Put it in the form of an undoable command... const bool bResult = pSession->execute( new qtractorRemoveTrackCommand(pTrack)); QApplication::restoreOverrideCursor(); return bResult; } // Edit given(current) track properties. bool qtractorTracks::editTrack ( qtractorTrack *pTrack ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Get the list view item reference of the intended track... if (pTrack == nullptr) pTrack = currentTrack(); if (pTrack == nullptr) return false; // Don't edit tracks engaged in recording... if (pTrack->isRecord() && pSession->isRecording() && pSession->isPlaying()) return false; // Open dialog for settings... QWidget *pParent = QApplication::activeWindow(); if (pParent == nullptr) pParent = static_cast (qtractorMainForm::getInstance()); qtractorTrackForm trackForm(pParent); trackForm.setTrack(pTrack); if (!trackForm.exec()) return false; // Might take a while... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Put it in the form of an undoable command... const bool bResult = pSession->execute( new qtractorEditTrackCommand(pTrack, trackForm.properties())); QApplication::restoreOverrideCursor(); return bResult; } // Copy/duplicate given(current) track. bool qtractorTracks::copyTrack ( qtractorTrack *pTrack ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Get the list view item reference of the intended track... if (pTrack == nullptr) pTrack = currentTrack(); if (pTrack == nullptr) return false; // Might take a while... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Duplicate into a new track... const int iTrack = pSession->tracks().count() + 1; const QColor& color = qtractorTrack::trackColor(iTrack); qtractorTrack *pNewTrack = new qtractorTrack(pSession, pTrack->trackType()); pNewTrack->setProperties(pTrack->properties()); // Find an incremental/next track name... pNewTrack->setTrackName(pSession->uniqueTrackName(pTrack->trackName())); pNewTrack->setBackground(color); pNewTrack->setForeground(color.darker()); pNewTrack->setZoomHeight(pTrack->zoomHeight()); // Put it in the form of an undoable command... const bool bResult = pSession->execute( new qtractorCopyTrackCommand(pNewTrack, pTrack)); QApplication::restoreOverrideCursor(); return bResult; } // Add new tracks from audio/MIDI file(s)... bool qtractorTracks::addTracks ( const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength, qtractorTrack *pAfterTrack ) { // Have we some? if (files.isEmpty()) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // We'll build a composite command... qtractorImportTrackCommand *pImportTrackCommand = new qtractorImportTrackCommand(pAfterTrack); // Have we changed anything? bool bResult = false; if (importTracks(files, iClipStart, iClipOffset, iClipLength, pAfterTrack, pImportTrackCommand)) { // Put it in the form of an undoable command... bResult = pSession->execute(pImportTrackCommand); } else { delete pImportTrackCommand; } return bResult; } // Import Audio files into new tracks... bool qtractorTracks::addAudioTracks ( const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength, qtractorTrack *pAfterTrack ) { // Have we some? if (files.isEmpty()) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // We'll build a composite command... qtractorImportTrackCommand *pImportTrackCommand = new qtractorImportTrackCommand(pAfterTrack); // Have we changed anything? bool bResult = false; if (importAudioTracks(files, iClipStart, iClipOffset, iClipLength, pAfterTrack, pImportTrackCommand)) { // Put it in the form of an undoable command... bResult = pSession->execute(pImportTrackCommand); } else { delete pImportTrackCommand; } return bResult; } // Import MIDI files into new tracks... bool qtractorTracks::addMidiTracks ( const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength, qtractorTrack *pAfterTrack ) { // Have we some? if (files.isEmpty()) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // We'll build a composite command... qtractorImportTrackCommand *pImportTrackCommand = new qtractorImportTrackCommand(pAfterTrack); // Have we changed anything? bool bResult = false; if (importMidiTracks(files, iClipStart, iClipOffset, iClipLength, pAfterTrack, pImportTrackCommand)) { // Put it in the form of an undoable command... bResult = pSession->execute(pImportTrackCommand); } else { delete pImportTrackCommand; } return bResult; } // Import MIDI file track-channel into new track... bool qtractorTracks::addMidiTrackChannel ( const QString& sPath, int iTrackChannel, unsigned long iClipStart, qtractorTrack *pAfterTrack ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // To log this import into session description. QString sDescription = pSession->description().trimmed(); if (!sDescription.isEmpty()) sDescription += '\n'; // We'll build a composite command... qtractorImportTrackCommand *pImportTrackCommand = new qtractorImportTrackCommand(pAfterTrack); // Increment this for suggestive track coloring... const int iTrack = pSession->tracks().count(); // Create a new track right away... const QColor& color = qtractorTrack::trackColor(iTrack + 1); qtractorTrack *pTrack = new qtractorTrack(pSession, qtractorTrack::Midi); // pTrack->setTrackName(QFileInfo(sPath).baseName()); pTrack->setBackground(color); pTrack->setForeground(color.darker()); // Add the clip at once... qtractorMidiClip *pMidiClip = new qtractorMidiClip(pTrack); pMidiClip->setFilename(sPath); pMidiClip->setTrackChannel(iTrackChannel); pMidiClip->setClipStart(iClipStart); // Time to add the new track/clip into session... pTrack->addClipEx(pMidiClip); pTrack->setTrackName( pSession->uniqueTrackName(pMidiClip->clipName())); pTrack->setMidiChannel(pMidiClip->channel()); // Add the new track to composite command... pImportTrackCommand->addTrack(pTrack); // Don't forget to add this one to local repository. qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { pMainForm->addMidiFile(sPath); // Log this successful import operation... sDescription += tr("MIDI file import \"%1\"" " track-channel %2 on %3 %4.\n") .arg(QFileInfo(sPath).fileName()).arg(iTrackChannel) .arg(QDate::currentDate().toString("MMM dd yyyy")) .arg(QTime::currentTime().toString("hh:mm:ss")); pMainForm->appendMessages( tr("MIDI file import: \"%1\", track-channel: %2.") .arg(sPath).arg(iTrackChannel)); } // Log to session (undoable by import-track command)... pSession->setDescription(sDescription); // Put it in the form of an undoable command... return pSession->execute(pImportTrackCommand); } // Import new tracks from audio/MIDI file(s)... bool qtractorTracks::importTracks ( const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength, qtractorTrack *pAfterTrack, qtractorImportTrackCommand *pImportTrackCommand ) { // Let's see how many files there are // to split between audio/MIDI files... QStringList audio_files; QStringList midi_files; QStringListIterator iter(files); while (iter.hasNext()) { const QString& sPath = iter.next(); if (sPath.isEmpty()) continue; // Try first as a MIDI file... qtractorMidiFile file; if (file.open(sPath)) { midi_files.append(sPath); file.close(); continue; } // Then as an audio file?... qtractorAudioFile *pFile = qtractorAudioFileFactory::createAudioFile(sPath); if (pFile) { if (pFile->open(sPath)) { audio_files.append(sPath); pFile->close(); } delete pFile; continue; } } int iImportTracks = 0; if (importMidiTracks(midi_files, iClipStart, iClipOffset, iClipLength, pAfterTrack, pImportTrackCommand)) { ++iImportTracks; } if (importAudioTracks(audio_files, iClipStart, iClipOffset, iClipLength, pAfterTrack, pImportTrackCommand)) { ++iImportTracks; } return (iImportTracks > 0); } // Import new tracks from audio file(s)... bool qtractorTracks::importAudioTracks ( const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength, qtractorTrack *pAfterTrack, qtractorImportTrackCommand *pImportTrackCommand ) { if (files.isEmpty()) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // pSession->lock(); // Account for actual updates... int iUpdate = 0; // Increment this for suggestive track coloring... int iTrack = pSession->tracks().count(); // To log this import into session description. QString sDescription = pSession->description().trimmed(); if (!sDescription.isEmpty()) sDescription += '\n'; // Needed whether we'll span to one single track // or will have each clip intto several tracks... const bool bDropSpan = m_pTrackView->isDropSpan(); qtractorTrack *pTrack = nullptr; int iTrackClip = 0; // For each one of those files... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); QStringListIterator iter(files); while (iter.hasNext()) { // This is one of the selected filenames.... const QString& sPath = iter.next(); // Create a new track right away... if (pTrack == nullptr || !bDropSpan) { const QColor& color = qtractorTrack::trackColor(++iTrack); pTrack = new qtractorTrack(pSession, qtractorTrack::Audio); pTrack->setBackground(color); pTrack->setForeground(color.darker()); if (pImportTrackCommand) pImportTrackCommand->addTrack(pTrack); else pSession->addTrack(pTrack); iTrackClip = 0; } // Add the clip at once... qtractorAudioClip *pAudioClip = new qtractorAudioClip(pTrack); pAudioClip->setFilename(sPath); pAudioClip->setClipStart(iClipStart); if (iClipOffset > 0) pAudioClip->setClipOffset(iClipOffset); if (iClipLength > 0) pAudioClip->setClipLength(iClipLength); // Time to add the new track/clip into session; // actuallly, this is when the given audio file gets open... pTrack->addClipEx(pAudioClip); if (iTrackClip == 0) { pTrack->setTrackName( pSession->uniqueTrackName(pAudioClip->clipName())); } ++iTrackClip; // Add the new track to composite command... if (bDropSpan) iClipStart += pAudioClip->clipLength(); ++iUpdate; // Don't forget to add this one to local repository. if (pMainForm) { pMainForm->addAudioFile(sPath); // Log this successful import operation... sDescription += tr("Audio file import \"%1\" on %2 %3.\n") .arg(QFileInfo(sPath).fileName()) .arg(QDate::currentDate().toString("MMM dd yyyy")) .arg(QTime::currentTime().toString("hh:mm:ss")); pMainForm->appendMessages( tr("Audio file import: \"%1\".").arg(sPath)); } } // Have we changed anything? const bool bResult = (iUpdate > 0); if (bResult) pSession->setDescription(sDescription); return bResult; } // Import new tracks from MIDI file(s)... bool qtractorTracks::importMidiTracks ( const QStringList& files, unsigned long iClipStart, unsigned long /*iClipOffset*/, unsigned long /*iClipLength*/, qtractorTrack *pAfterTrack, qtractorImportTrackCommand *pImportTrackCommand ) { // Have we some? if (files.isEmpty()) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Account for actual updates... int iUpdate = 0; // Increment this for suggestive track coloring... int iTrack = pSession->tracks().count(); // Needed to help on setting whole session properties // from the first imported MIDI file... int iImport = iTrack; const unsigned long iTimeStart = pSession->tickFromFrame(iClipStart); // To log this import into session description. QString sDescription = pSession->description().trimmed(); if (!sDescription.isEmpty()) sDescription += '\n'; // For each one of those files... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); QStringListIterator iter(files); while (iter.hasNext()) { // This is one of the selected filenames.... const QString& sPath = iter.next(); // We'll be careful and pre-open the SMF header here... qtractorMidiFile file; if (!file.open(sPath)) continue; // It all depends on the format... const int iTracks = (file.format() == 1 ? file.tracks() : 16); for (int iTrackChannel = 0; iTrackChannel < iTracks; ++iTrackChannel) { // Create a new track right away... const QColor& color = qtractorTrack::trackColor(++iTrack); qtractorTrack *pTrack = new qtractorTrack(pSession, qtractorTrack::Midi); // pTrack->setTrackName(QFileInfo(sPath).baseName()); pTrack->setBackground(color); pTrack->setForeground(color.darker()); // Add the clip at once... qtractorMidiClip *pMidiClip = new qtractorMidiClip(pTrack); pMidiClip->setFilename(sPath); pMidiClip->setTrackChannel(iTrackChannel); pMidiClip->setClipStart(iClipStart); if (iTrackChannel == 0 && iImport == 0) pMidiClip->setSessionFlag(true); // Time to add the new track/clip into session; // actuallly, this is when the given MIDI file and // track-channel gets open and read into the clip! pTrack->addClipEx(pMidiClip); // As far the standards goes,from which we'll strictly follow, // only the first track/channel has some tempo/time signature... if (iTrackChannel == 0) { // Some adjustment required... ++iImport; iClipStart = pSession->frameFromTick(iTimeStart); pMidiClip->setClipStart(iClipStart); } // Time to check whether there is actual data on track... if (pMidiClip->clipLength() > 0) { // Add the new track to composite command... pTrack->setTrackName( pSession->uniqueTrackName(pMidiClip->clipName())); pTrack->setMidiChannel(pMidiClip->channel()); if (pImportTrackCommand) pImportTrackCommand->addTrack(pTrack); else pSession->addTrack(pTrack); ++iUpdate; // Don't forget to add this one to local repository. if (pMainForm) pMainForm->addMidiFile(sPath); } else { // Get rid of these, now... pTrack->removeClip(pMidiClip); delete pMidiClip; delete pTrack; } } // Log this successful import operation... if (iUpdate > 0 && pMainForm) { sDescription += tr("MIDI file import \"%1\" on %2 %3.\n") .arg(QFileInfo(sPath).fileName()) .arg(QDate::currentDate().toString("MMM dd yyyy")) .arg(QTime::currentTime().toString("hh:mm:ss")); pMainForm->appendMessages( tr("MIDI file import: \"%1\".").arg(sPath)); } } const bool bResult = (iUpdate > 0); if (bResult) pSession->setDescription(sDescription); return bResult; } // Track-list active maintenance update. void qtractorTracks::updateTrack ( qtractorTrack *pTrack ) { m_pTrackList->updateTrack(pTrack); if (pTrack && pTrack->trackType() == qtractorTrack::Midi) updateMidiTrack(pTrack); } // MIDI track/bus/channel alias active maintenance method. void qtractorTracks::updateMidiTrack ( qtractorTrack *pMidiTrack ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const QString& sBusName = pMidiTrack->outputBusName(); const unsigned short iChannel = pMidiTrack->midiChannel(); for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { // If same channel, force same bank/program stuff... if (pTrack != pMidiTrack && pTrack->trackType() == qtractorTrack::Midi && pTrack->outputBusName() == sBusName && pTrack->midiChannel() == iChannel) { // Make else tracks MIDI attributes the same.... pTrack->setMidiBankSelMethod(pMidiTrack->midiBankSelMethod()); pTrack->setMidiBank(pMidiTrack->midiBank()); pTrack->setMidiProg(pMidiTrack->midiProg()); pTrack->updateMidiClips(); // Update the track list view, immediately... m_pTrackList->updateTrack(pTrack); } } #if 0 // Re-open all MIDI clips (channel might have changed?)... qtractorClip *pClip = pMidiTrack->clips().first(); for ( ; pClip; pClip = pClip->next()) pClip->open(); #endif // Update MIDI bus patch... qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; qtractorMidiBus *pMidiBus = static_cast (pMidiTrack->outputBus()); if (pMidiBus == nullptr) return; const qtractorMidiBus::Patch& patch = pMidiBus->patch(iChannel); pMidiBus->setPatch(iChannel, patch.instrumentName, pMidiTrack->midiBankSelMethod(), pMidiTrack->midiBank(), pMidiTrack->midiProg(), pMidiTrack); } // MIDI track meters maintenance method. void qtractorTracks::updateMidiTrackItem ( qtractorMidiManager *pMidiManager ) { m_pTrackList->updateMidiTrackItem(pMidiManager); } // Simple main-form stabilizer redirector. void qtractorTracks::selectionChangeNotify (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->selectionNotifySlot(nullptr); } // Simple main-form dirty-flag redirectors. void qtractorTracks::contentsChangeNotify (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->changeNotifySlot(nullptr); } void qtractorTracks::dirtyChangeNotify (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->dirtyNotifySlot(); } // Track-list update (current track only). void qtractorTracks::updateTrackList ( qtractorTrack *pTrack ) { m_pTrackList->updateTrack(pTrack); } // Update/sync recording tracks. void qtractorTracks::updateContentsRecord (void) { m_pTrackView->updateContentsRecord(); } // Track-view update (obviously a slot). void qtractorTracks::updateTrackView (void) { m_pTrackView->update(); } // Overall selection clear/reset. void qtractorTracks::clearSelect ( bool bReset ) { m_pTrackView->clearSelect(bReset); } // Overall selection update. void qtractorTracks::updateSelect (void) { m_pTrackView->updateSelect(); m_pTrackView->update(); } // Overall contents reset. void qtractorTracks::clear (void) { m_pTrackList->clear(); m_pTrackView->clear(); updateContents(true); } // end of qtractorTracks.cpp qtractor-1.5.11/src/PaxHeaders/qtractorClipCommand.h0000644000000000000000000000013215124701674017431 xustar0030 mtime=1767080892.781263504 30 atime=1767080892.781263504 30 ctime=1767080892.781263504 qtractor-1.5.11/src/qtractorClipCommand.h0000644000175000001440000002513415124701674017426 0ustar00rncbcusers// qtractorClipCommand.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorClipCommand_h #define __qtractorClipCommand_h #include "qtractorCommand.h" #include "qtractorClip.h" #include // Forward declarations. class qtractorTrackCommand; class qtractorSessionCommand; class qtractorCurveEditCommand; class qtractorTimeScaleNodeCommand; class qtractorTimeScaleMarkerCommand; class qtractorMidiEditCommand; class qtractorMidiClip; //---------------------------------------------------------------------- // class qtractorClipCommand - declaration. // class qtractorClipCommand : public qtractorCommand { public: // Constructor. qtractorClipCommand(const QString& sName); // Destructor. virtual ~qtractorClipCommand(); // Primitive command methods. void addClip(qtractorClip *pClip, qtractorTrack *pTrack); void removeClip(qtractorClip *pClip); // Edit clip command methods. void fileClip(qtractorClip *pClip, const QString& sFilename, unsigned short iTrackChannel = 0); void renameClip(qtractorClip *pClip, const QString& sClipName); void moveClip(qtractorClip *pClip, qtractorTrack *pTrack, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength); void resizeClip(qtractorClip *pClip, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength, float fTimeStretch = 0.0f, float fPitchShift = 0.0f); void gainClip(qtractorClip *pClip, float fGain); void panningClip(qtractorClip *pClip, float fPanning); void muteClip(qtractorClip *pClip, bool bMute); void fadeInClip(qtractorClip *pClip, unsigned long iFadeInLength, qtractorClip::FadeType fadeInType); void fadeOutClip(qtractorClip *pClip, unsigned long iFadeOutLength, qtractorClip::FadeType fadeOutType); void timeStretchClip(qtractorClip *pClip, float fTimeStretch); void pitchShiftClip(qtractorClip *pClip, float fPitchShift); void takeInfoClip(qtractorClip *pClip, qtractorClip::TakeInfo *pTakeInfo); void resetClip(qtractorClip *pClip); void stretcherFlagsClip(qtractorClip *pClip, unsigned int iStretcherFlags); // Special clip record methods. bool addClipRecord(qtractorTrack *pTrack, unsigned long iFrameTime); bool addClipRecordTake(qtractorTrack *pTrack, qtractorClip *pClip, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength, qtractorClip::TakePart *pTakePart = nullptr); // When new tracks are needed. void addTrack(qtractorTrack *pTrack); // When MIDI clips are stretched. qtractorMidiEditCommand *createMidiEditCommand( qtractorMidiClip *pMidiClip, float fTimeStretch); // Composite predicate. bool isEmpty() const; // Virtual command methods. bool redo(); bool undo(); protected: // Conveniency helper: whether a clip is certain type. bool isAudioClip(qtractorClip *pClip) const; // When clips need to get reopenned. void reopenClip(qtractorClip *pClip, bool bClose = false); // Common executive method. virtual bool execute(bool bRedo); private: // Primitive command types. enum CommandType { AddClip, RemoveClip, FileClip, RenameClip, MoveClip, ResizeClip, GainClip, PanningClip, MuteClip, FadeInClip, FadeOutClip, TimeStretchClip, PitchShiftClip, TakeInfoClip, ResetClip, StretcherFlagsClip }; // Clip item struct. struct Item { // Item constructor. Item(CommandType cmd, qtractorClip *pClip, qtractorTrack *pTrack) : command(cmd), clip(pClip), track(pTrack), autoDelete(false), trackChannel(0), clipStart(0), clipOffset(0), clipLength(0), clipGain(0.0f), clipPanning(0.0f), fadeInLength(0), fadeInType(qtractorClip::InQuad), fadeOutLength(0), fadeOutType(qtractorClip::OutQuad), timeStretch(0.0f), pitchShift(0.0f), stretcherFlags(0), editCommand(nullptr), takeInfo(nullptr) {} // Item members. CommandType command; qtractorClip *clip; qtractorTrack *track; bool autoDelete; QString filename; unsigned short trackChannel; QString clipName; unsigned long clipStart; unsigned long clipOffset; unsigned long clipLength; float clipGain; float clipPanning; bool clipMute; unsigned long fadeInLength; qtractorClip::FadeType fadeInType; unsigned long fadeOutLength; qtractorClip::FadeType fadeOutType; float timeStretch; float pitchShift; unsigned int stretcherFlags; // When MIDI clips are time-stretched... qtractorMidiEditCommand *editCommand; // When clips have take(record) descriptors... qtractorClip::TakeInfo *takeInfo; }; // Instance variables. QList m_items; // When new tracks are needed. QList m_trackCommands; // When clips need to reopem. QHash m_clips; }; //---------------------------------------------------------------------- // class qtractorClipTakeCommand - declaration. // class qtractorClipTakeCommand : public qtractorClipCommand { public: // Constructor. qtractorClipTakeCommand(qtractorClip::TakeInfo *pTakeInfo, qtractorTrack *pTrack = nullptr, int iCurrentTake = -1); protected: // Executive override. bool execute(bool bRedo); private: // Instance variables. qtractorClip::TakeInfo *m_pTakeInfo; int m_iCurrentTake; }; //---------------------------------------------------------------------- // class qtractorClipRangeCommand - declaration. // class qtractorClipRangeCommand : public qtractorClipCommand { public: // Constructor. qtractorClipRangeCommand(const QString& sName); // Destructor. ~qtractorClipRangeCommand(); // When Loop/Punch changes are needed. void addSessionCommand( qtractorSessionCommand *pSessionCommand); // When automation curves are needed. void addCurveEditCommand( qtractorCurveEditCommand *pCurveEditCommand); // When location markers are needed. void addTimeScaleMarkerCommand( qtractorTimeScaleMarkerCommand *pTimeScaleMarkerCommand); // When tempo-map/time-sig nodes are needed. void addTimeScaleNodeCommand( qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand); protected: // Executive override. bool execute(bool bRedo); private: // Instance variables. QList m_sessionCommands; QList m_curveEditCommands; QList m_timeScaleMarkerCommands; QList m_timeScaleNodeCommands; }; //---------------------------------------------------------------------- // class qtractorClipContextCommand - declaration. (virtual class) // class qtractorClipContextCommand : public qtractorCommand { public: // Constructor. qtractorClipContextCommand(const QString& sName); // Destructor. virtual ~qtractorClipContextCommand(); // Composite command methods. void addMidiClipContext(qtractorMidiClip *pMidiClip); // Composite predicate. bool isEmpty() const; // Virtual command methods. bool redo(); bool undo(); protected: // Main executive method. bool execute(bool bRedo); // Virtual executive method. struct MidiClipCtx { QString filename; unsigned long offset; unsigned long length; }; virtual bool executeMidiClipContext( qtractorMidiClip *pMidiClip, const MidiClipCtx& mctx, bool bRedo) = 0; private: int m_iRedoCount; typedef QHash MidiClipCtxs; MidiClipCtxs m_midiClipCtxs; }; //---------------------------------------------------------------------- // class qtractorClipSaveFileCommand - declaration. // class qtractorClipSaveFileCommand : public qtractorClipContextCommand { public: // Constructor. qtractorClipSaveFileCommand(); protected: // Context (visitor) executive method. bool executeMidiClipContext( qtractorMidiClip *pMidiClip, const MidiClipCtx& mctx, bool bRedo); }; //---------------------------------------------------------------------- // class qtractorClipUnlinkCommand - declaration. // class qtractorClipUnlinkCommand : public qtractorClipContextCommand { public: // Constructor. qtractorClipUnlinkCommand(); protected: // Context (visitor) executive method. bool executeMidiClipContext( qtractorMidiClip *pMidiClip, const MidiClipCtx& mctx, bool bRedo); }; //---------------------------------------------------------------------- // class qtractorClipToolCommand - declaration. // class qtractorClipToolCommand : public qtractorCommand { public: // Constructor. qtractorClipToolCommand(const QString& sName); // Destructor. virtual ~qtractorClipToolCommand(); // Check if a clip is already part of the editing set. bool isLinkedMidiClip(qtractorMidiClip *pMidiClip) const; // Composite command methods. void addMidiEditCommand(qtractorMidiEditCommand *pMidiEditCommand); // When additional tempo-map/time-sig nodes are needed. void addTimeScaleNodeCommand(qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand); // Composite predicate. bool isEmpty() const; // Virtual command methods. bool redo(); bool undo(); protected: // Execute tempo-map/time-sig commands. void executeTimeScaleNodeCommands(bool bRedo); private: // Instance variables. int m_iRedoCount; // Multi-clip command list. QList m_midiEditCommands; // When tempo-map node commands. QList m_timeScaleNodeCommands; qtractorClipSaveFileCommand *m_pClipSaveFileCommand; }; //---------------------------------------------------------------------- // class qtractorClipRecordExCommand - declaration. // class qtractorClipRecordExCommand : public qtractorCommand { public: // Constructor. qtractorClipRecordExCommand(qtractorClip *pClipRecordEx, bool bClipRecordEx); // Virtual command methods. bool redo(); bool undo(); private: // Instance variables. qtractorTrack *m_pTrack; bool m_bClipRecordEx; qtractorClip *m_pClipRecordEx; }; #endif // __qtractorClipCommand_h // end of qtractorClipCommand.h qtractor-1.5.11/src/PaxHeaders/qtractorPluginForm.ui0000644000000000000000000000013215124701674017513 xustar0030 mtime=1767080892.798263432 30 atime=1767080892.798263432 30 ctime=1767080892.798263432 qtractor-1.5.11/src/qtractorPluginForm.ui0000644000175000001440000003041215124701674017503 0ustar00rncbcusers rncbc aka Rui Nuno Capela qtractor - An Audio/MIDI multi-track sequencer. Copyright (C) 2005-2025, 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. qtractorPluginForm 0 0 480 240 Plugin Properties 4 4 Qt::TabFocus Open preset 160 0 Preset name true Qt::TabFocus Save preset Qt::TabFocus Delete preset Qt::Horizontal QSizePolicy::MinimumExpanding 20 20 Alias: AliasLineEdit 120 0 Plugin alias Qt::TabFocus Edit plugin Edit true Qt::ToolButtonTextBesideIcon Qt::TabFocus Active true Qt::ToolButtonTextBesideIcon About 12 75 true Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false 12 75 true Qt::AlignRight|Qt::AlignVCenter true 0 0 360 60 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::TabFocus Outputs (Sends) Sends Qt::ToolButtonTextBesideIcon Qt::TabFocus Inputs (Returns) Returns Qt::ToolButtonTextBesideIcon Auto-connect Aux Send Bus: 120 22 22 22 24 24 Qt::TabFocus Manage buses ... 22 22 96 96 Qt::TabFocus Audio bus I/O matrix I/O Matrix... Qt::Horizontal QSizePolicy::Expanding 20 0 80 22 120 24 Qt::TabFocus Direct Access Parameter Direct Access PresetComboBox AliasLineEdit OpenPresetToolButton SavePresetToolButton DeletePresetToolButton EditToolButton ActivateToolButton SendsToolButton ReturnsToolButton AutoConnectCheckBox AuxSendBusNameComboBox AuxSendBusNameToolButton AuxSendIOMatrixToolButton DirectAccessParamPushButton qtractor-1.5.11/src/PaxHeaders/qtractorAudioMonitor.h0000644000000000000000000000013215124701674017654 xustar0030 mtime=1767080892.779263512 30 atime=1767080892.779263512 30 ctime=1767080892.779263512 qtractor-1.5.11/src/qtractorAudioMonitor.h0000644000175000001440000000646315124701674017655 0ustar00rncbcusers// qtractorAudioMonitor.h // /**************************************************************************** Copyright (C) 2005-2022, 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 __qtractorAudioMonitor_h #define __qtractorAudioMonitor_h #include "qtractorMonitor.h" // Forward decls. class qtractorAudioMeter; //---------------------------------------------------------------------------- // qtractorAudioMonitor -- Audio monitor bridge value processor. class qtractorAudioMonitor : public qtractorMonitor { public: // Constructor. qtractorAudioMonitor(unsigned short iChannels, float fGain = 1.0f, float fPanning = 0.0f); // Copy constructor. qtractorAudioMonitor(const qtractorAudioMonitor& monitor); // Destructor. ~qtractorAudioMonitor(); // Channel property accessors. void setChannels(unsigned short iChannels); unsigned short channels() const; // Value holder accessor. float value_stamp(unsigned short iChannel, unsigned long iStamp) const; // Batch processors. void process(float **ppFrames, unsigned int iFrames, unsigned short iChannels = 0); void process_meter(float **ppFrames, unsigned int iFrames, unsigned short iChannels = 0); // Reset channel gain trackers. void reset(); protected: // Rebuild the whole panning-gain array... void update(); private: // Instance variables. unsigned short m_iChannels; unsigned long *m_piStamps; float *m_pfValues; float *m_pfPrevValues; float *m_pfGains; float *m_pfPrevGains; volatile int m_iProcessRamp; // Monitoring evaluator processor. void (*m_pfnProcess)(float *, unsigned int, float, float *); void (*m_pfnProcessRamp)(float *, unsigned int, float, float, float *); void (*m_pfnProcessMeter)(float *, unsigned int, float *); }; //---------------------------------------------------------------------------- // qtractorAudioOutputMonitor -- Audio-output monitor bridge value processor. class qtractorAudioOutputMonitor : public qtractorAudioMonitor { public: // Constructor. qtractorAudioOutputMonitor(unsigned short iChannels, float fGain = 1.0f, float fPanning = 0.0f); // Destructor. ~qtractorAudioOutputMonitor(); // Channel property accessors. void setChannels(unsigned short iChannels); // Associated meters (kinda observers) management methods. void addAudioMeter(qtractorAudioMeter *pAudioMeter); void removeAudioMeter(qtractorAudioMeter *pAudioMeter); private: // Instance variables. QList m_meters; }; #endif // __qtractorAudioMonitor_h // end of qtractorAudioMonitor.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiToolsForm.ui0000644000000000000000000000013215124701674020160 xustar0030 mtime=1767080892.795263445 30 atime=1767080892.795263445 30 ctime=1767080892.795263445 qtractor-1.5.11/src/qtractorMidiToolsForm.ui0000644000175000001440000014650515124701674020163 0ustar00rncbcusers rncbc aka Rui Nuno Capela qtractor - An Audio/MIDI multi-track sequencer. Copyright (C) 2005-2025, 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. qtractorMidiToolsForm 0 0 480 240 MIDI Tools 4 4 2 2 160 0 Preset name true Qt::TabFocus Save preset Qt::TabFocus Delete preset Qt::Horizontal 180 20 0 &Quantize 75 true Quantize selected events &Quantize Qt::Horizontal QSizePolicy::Fixed 20 20 &Time: Quantize time 60 0 Quantize time percent % true 1 0.0 100.0 0.1 100.0 Qt::Horizontal 20 20 &Duration: Quantize duration 60 0 Quantize duration percent % true 1 0.0 100.0 0.1 100.0 S&wing: Swing-quantize time 60 0 Swing-quantize percent % true 1 -100.0 100.0 0.1 0.0 Swing-quantize type Linear Quadratic Cubic Qt::Horizontal 20 20 &Scale: Scale-quantize key Scale-quantize type Qt::Vertical 20 20 &Transpose 75 true Transpose selected events &Transpose Qt::Horizontal QSizePolicy::Fixed 16 20 &Note: 60 0 Transpose note -64 64 Qt::Horizontal 20 20 &Time: 120 0 Transpose time Transpose time format Frames Time BBT Qt::Horizontal 20 20 &Reverse Qt::Vertical 20 20 &Normalize 75 true Normalize selected events &Normalize Qt::Horizontal QSizePolicy::Fixed 16 20 &Percent: 60 0 Normalize percent % true 1 150.0 0.1 Qt::Horizontal 20 20 &Value: 60 0 Normalize value 127 &Compress Qt::Vertical 20 20 &Randomize 75 true Randomize selected events &Randomize Qt::Horizontal QSizePolicy::Fixed 16 20 &Note: 60 0 Randomize note/pitch % true 1 150.0 0.1 Qt::Horizontal 20 20 &Time: 60 0 Randomize time % true 1 150.0 0.1 &Duration: 60 0 Randomize duration % true 1 150.0 0.1 &Value: 60 0 Randomize value % true 1 150.0 0.1 Qt::Vertical 20 20 Resi&ze 75 true Resize selected events Resi&ze Qt::Horizontal QSizePolicy::Fixed 16 20 &Duration: 120 0 Resize duration Resize duration format Frames Time BBT Qt::Horizontal 20 20 &Value: 60 0 Resize value 127 Resize value mode Flat Ramp 60 0 Resize final value 127 Qt::Horizontal 20 20 &Legato: 60 0 Legato trim/extend type Normal Trim Extend Legato trim/extend length Legato mode Mono Poly Qt::Horizontal 20 20 4 0 &Join &Split: Split notes length Split notes offset Relative Absolute Qt::Horizontal 20 20 Qt::Vertical 20 20 Re&scale 75 true Rescale selected events Re&scale Qt::Horizontal QSizePolicy::Fixed 16 20 &Time: 60 0 Rescale time % true 1 0.1 1000.0 0.1 100.0 Qt::Horizontal 240 20 &Duration: 60 0 Rescale duration % true 1 0.1 1000.0 0.1 100.0 &Value: 60 0 Rescale value % true 1 0.1 1000.0 0.1 Reverse 100.0 &Invert Qt::Vertical 360 20 T&imeshift 75 true Timeshift selected events Timeshift Qt::Horizontal QSizePolicy::Fixed 16 20 P: 60 0 Timeshift parameter true 1 -100.0 100.0 0.1 0.0 % Timeshift parameter (log) -10000 10000 10 100 0 Qt::Horizontal 80 80 80 80 Timeshift curve QFrame::Panel QFrame::Sunken P = 0 : no change. P > 0 : accelerating shift. P < 0 : slowing down shift. Edit head/tail (blue) markers define the shift range. Timeshift duration Qt::Vertical 360 20 T&empo ramp 75 true Tempo ramp selected events Tempo ramp Qt::Horizontal QSizePolicy::Fixed 16 20 From 80 0 Tempo ramp start to 80 0 Temporamp end Qt::Horizontal 20 20 Edit head/tail (blue) markers define the ramp range. Tempo ramp duration Qt::Vertical 360 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorTimeSpinBox QSpinBox
qtractorSpinBox.h
qtractorTempoSpinBox QSpinBox
qtractorSpinBox.h
PresetNameComboBox PresetSaveToolButton PresetDeleteToolButton ToolTabWidget QuantizeCheckBox QuantizeTimeCheckBox QuantizeTimeComboBox QuantizeTimeSpinBox QuantizeDurationCheckBox QuantizeDurationComboBox QuantizeDurationSpinBox QuantizeSwingCheckBox QuantizeSwingComboBox QuantizeSwingSpinBox QuantizeSwingTypeComboBox QuantizeScaleCheckBox QuantizeScaleKeyComboBox QuantizeScaleComboBox TransposeCheckBox TransposeNoteCheckBox TransposeNoteSpinBox TransposeTimeCheckBox TransposeTimeSpinBox TransposeFormatComboBox TransposeReverseCheckBox NormalizeCheckBox NormalizePercentCheckBox NormalizePercentSpinBox NormalizeValueCheckBox NormalizeValueSpinBox NormalizeCompressCheckBox RandomizeCheckBox RandomizeNoteCheckBox RandomizeNoteSpinBox RandomizeTimeCheckBox RandomizeTimeSpinBox RandomizeDurationCheckBox RandomizeDurationSpinBox RandomizeValueCheckBox RandomizeValueSpinBox ResizeCheckBox ResizeDurationCheckBox ResizeDurationSpinBox ResizeDurationFormatComboBox ResizeValueCheckBox ResizeValueSpinBox ResizeValue2ComboBox ResizeValue2SpinBox ResizeLegatoCheckBox ResizeLegatoTypeComboBox ResizeLegatoLengthComboBox ResizeLegatoModeComboBox ResizeJoinCheckBox ResizeSplitCheckBox ResizeSplitLengthComboBox ResizeSplitOffsetComboBox RescaleCheckBox RescaleTimeCheckBox RescaleTimeSpinBox RescaleDurationCheckBox RescaleDurationSpinBox RescaleValueCheckBox RescaleValueSpinBox RescaleInvertCheckBox TimeshiftCheckBox TimeshiftSpinBox TimeshiftSlider TimeshiftDurationCheckBox TemporampFromSpinBox TemporampToSpinBox TemporampDurationCheckBox DialogButtonBox
qtractor-1.5.11/src/PaxHeaders/qtractorMidiSequence.h0000644000000000000000000000013215124701674017616 xustar0030 mtime=1767080892.795263445 30 atime=1767080892.795263445 30 ctime=1767080892.795263445 qtractor-1.5.11/src/qtractorMidiSequence.h0000644000175000001440000001223715124701674017613 0ustar00rncbcusers// qtractorMidiSequence.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorMidiSequence_h #define __qtractorMidiSequence_h #include "qtractorMidiEvent.h" #include #include // typedef unsigned long long uint64_t; #include //---------------------------------------------------------------------- // class qtractorMidiSequence -- The generic MIDI event sequence buffer. // class qtractorMidiSequence { public: // Constructor. qtractorMidiSequence(const QString& sName = QString(), unsigned short iChannel = 0, unsigned short iTicksPerBeat = 960); // Destructor. ~qtractorMidiSequence(); // Sequencer reset method. void clear(); // Sequence/track name accessors. void setName(const QString& sName) { m_sName = sName; } const QString& name() const { return m_sName; } // Sequence/track channel accessors. void setChannel(unsigned short iChannel) { m_iChannel = (iChannel & 0x0f); } unsigned short channel() const { return m_iChannel; } // Sequence/track ban-select-method accessors (optional). void setBankSelMethod(int iBankSelMethod) { m_iBankSelMethod = iBankSelMethod; } int bankSelMethod() const { return m_iBankSelMethod; } // Sequence/track bank accessors (optional). void setBank(int iBank) { m_iBank = iBank; } int bank() const { return m_iBank; } // Sequence/track prog accessors (optional). void setProg(int iProg) { m_iProg = iProg; } int prog() const { return m_iProg; } // Sequence/track resolution accessors. void setTicksPerBeat(unsigned short iTicksPerBeat) { m_iTicksPerBeat = iTicksPerBeat; } unsigned short ticksPerBeat() const { return m_iTicksPerBeat; } // Sequence time-offset parameter accessors. void setTimeOffset(unsigned long iTimeOffset) { m_iTimeOffset = iTimeOffset; } unsigned long timeOffset() const { return m_iTimeOffset; } // Sequence duration parameter accessors. void setTimeLength(unsigned long iTimeLength) { m_iTimeLength = iTimeLength; } unsigned long timeLength() const { return m_iTimeLength; } void setDuration(unsigned long iDuration) { m_duration = iDuration; } unsigned long duration() const { return m_duration; } // Statiscal helper accessors. void setNoteMin(unsigned char note) { if (m_noteMin > note || m_noteMin == 0) m_noteMin = note; } void setNoteMax(unsigned char note) { if (m_noteMax < note || m_noteMax == 0) m_noteMax = note; } unsigned char noteMin() const { return m_noteMin; } unsigned char noteMax() const { return m_noteMax; } // Event list accessor. const qtractorList& events() const { return m_events; } // Event list management methods. void addEvent (qtractorMidiEvent *pEvent); void insertEvent (qtractorMidiEvent *pEvent); void unlinkEvent (qtractorMidiEvent *pEvent); void removeEvent (qtractorMidiEvent *pEvent); // Adjust time resolutions (64bit). unsigned long timep(unsigned long iTime, unsigned short p) const { return uint64_t(iTime) * p / m_iTicksPerBeat; } unsigned long timeq(unsigned long iTime, unsigned short q) const { return uint64_t(iTime) * m_iTicksPerBeat / q; } // Replace events from another sequence in given range. void replaceEvents(qtractorMidiSequence *pSeq, unsigned long iTimeOffset = 0, unsigned long iTimeLength = 0); // Clopy all events from another sequence (raw-copy). void copyEvents(qtractorMidiSequence *pSeq); // Sequence closure method. void close(); // Typed hash table to track note-ons. typedef QHash NoteOns; protected: // NOTEON/OFF: Find previous note event and compute duration... void addNoteEvent(qtractorMidiEvent *pEvent); private: // Sequence/track properties. QString m_sName; unsigned short m_iChannel; unsigned short m_iTicksPerBeat; // Sequence time-offset/duration parameters. unsigned long m_iTimeOffset; unsigned long m_iTimeLength; // Sequence/track optional properties. int m_iBankSelMethod; int m_iBank; int m_iProg; // Statistical helper variables. unsigned char m_noteMin; unsigned char m_noteMax; unsigned long m_duration; // Sequence instance event list (all same MIDI channel). qtractorList m_events; // Local hash table to track note-ons. NoteOns m_notes; }; #endif // __qtractorMidiSequence_h // end of qtractorMidiSequence.h qtractor-1.5.11/src/PaxHeaders/qtractorInstrumentForm.cpp0000644000000000000000000000013215124701674020572 xustar0030 mtime=1767080892.786263483 30 atime=1767080892.785263487 30 ctime=1767080892.786263483 qtractor-1.5.11/src/qtractorInstrumentForm.cpp0000644000175000001440000005421415124701674020570 0ustar00rncbcusers// qtractorInstrumentForm.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorInstrumentForm.h" #include "qtractorInstrument.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include #include #include #include #include //---------------------------------------------------------------------- // class qtractorInstrumentGroupItem -- custom group list view item. // class qtractorInstrumentGroupItem : public QTreeWidgetItem { public: // Constructors. qtractorInstrumentGroupItem() : QTreeWidgetItem(qtractorInstrumentForm::GroupItem) { initGroupItem(); } qtractorInstrumentGroupItem(QTreeWidgetItem *pParent, QTreeWidgetItem *pAfter) : QTreeWidgetItem(pParent, pAfter, qtractorInstrumentForm::GroupItem) { initGroupItem(); } protected: // Initializer. void initGroupItem() { setIcon(0, QIcon::fromTheme("itemGroup")); } }; //---------------------------------------------------------------------- // class qtractorInstrumentForm -- instrument file manager form. // // Constructor. qtractorInstrumentForm::qtractorInstrumentForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); m_pInstruments = nullptr; m_iDirtyCount = 0; QHeaderView *pHeader = m_ui.InstrumentsListView->header(); pHeader->setDefaultAlignment(Qt::AlignLeft); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // pHeader->setSectionResizeMode(QHeaderView::Custom); pHeader->setSectionResizeMode(QHeaderView::ResizeToContents); pHeader->setSectionsMovable(false); #else // pHeader->setResizeMode(QHeaderView::Custom); pHeader->setResizeMode(QHeaderView::ResizeToContents); pHeader->setMovable(false); #endif pHeader = m_ui.FilesListView->header(); pHeader->setDefaultAlignment(Qt::AlignLeft); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // pHeader->setSectionResizeMode(QHeaderView::Custom); pHeader->setSectionResizeMode(QHeaderView::ResizeToContents); pHeader->setSectionsMovable(false); #else // pHeader->setResizeMode(QHeaderView::Custom); pHeader->setResizeMode(QHeaderView::ResizeToContents); pHeader->setMovable(false); #endif pHeader = m_ui.NamesListView->header(); pHeader->setDefaultAlignment(Qt::AlignLeft); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // pHeader->setSectionResizeMode(QHeaderView::Custom); pHeader->setSectionResizeMode(QHeaderView::ResizeToContents); pHeader->setSectionsMovable(false); #else // pHeader->setResizeMode(QHeaderView::Custom); pHeader->setResizeMode(QHeaderView::ResizeToContents); pHeader->setMovable(false); #endif qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) setInstruments(pSession->instruments()); adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.FilesListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(stabilizeForm())); QObject::connect(m_ui.NamesListView, SIGNAL(itemCollapsed(QTreeWidgetItem*)), SLOT(itemCollapsed(QTreeWidgetItem*))); QObject::connect(m_ui.NamesListView, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(itemExpanded(QTreeWidgetItem*))); QObject::connect(m_ui.InstrumentsListView, SIGNAL(itemCollapsed(QTreeWidgetItem*)), SLOT(itemCollapsed(QTreeWidgetItem*))); QObject::connect(m_ui.InstrumentsListView, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(itemExpanded(QTreeWidgetItem*))); QObject::connect(m_ui.ImportPushButton, SIGNAL(clicked()), SLOT(importSlot())); QObject::connect(m_ui.RemovePushButton, SIGNAL(clicked()), SLOT(removeSlot())); QObject::connect(m_ui.MoveUpPushButton, SIGNAL(clicked()), SLOT(moveUpSlot())); QObject::connect(m_ui.MoveDownPushButton, SIGNAL(clicked()), SLOT(moveDownSlot())); QObject::connect(m_ui.ExportPushButton, SIGNAL(clicked()), SLOT(exportSlot())); QObject::connect(m_ui.ClosePushButton, SIGNAL(clicked()), SLOT(reject())); } // Destructor. qtractorInstrumentForm::~qtractorInstrumentForm (void) { } // Instrument list accessors. void qtractorInstrumentForm::setInstruments ( qtractorInstrumentList *pInstruments ) { m_pInstruments = pInstruments; if (m_pInstruments) m_files = m_pInstruments->files(); else m_files.clear(); refreshForm(); stabilizeForm(); } qtractorInstrumentList *qtractorInstrumentForm::instruments (void) const { return m_pInstruments; } // Load the complete instrument definitions, from list. void qtractorInstrumentForm::reloadFiles ( const QStringList& files ) { if (m_pInstruments == nullptr) return; // Tell that we may take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Ooops... m_pInstruments->clearAll(); // Load each file in order... QStringListIterator iter(files); while (iter.hasNext()) m_pInstruments->load(iter.next()); // Done with reload. QApplication::restoreOverrideCursor(); } // Import new instrument file(s) into listing. void qtractorInstrumentForm::importSlot (void) { if (m_pInstruments == nullptr) return; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; QStringList files; const QString sExt("ins"); const QString& sTitle = tr("Import Instrument Files"); QStringList filters; filters.append(tr("Instrument files (*.%1 *.sf2 *.sf3 *.midnam)").arg(sExt)); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to open... files = QFileDialog::getOpenFileNames(pParentWidget, sTitle, pOptions->sInstrumentDir, sFilter, nullptr, options); #else // Construct open-files dialog... QFileDialog fileDialog(pParentWidget, sTitle, pOptions->sInstrumentDir, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFiles); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sInstrumentDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) files = fileDialog.selectedFiles(); #endif if (files.isEmpty()) return; // Tell that we may take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // For avery selected instrument file to load... QTreeWidgetItem *pItem = nullptr; QStringListIterator iter(files); while (iter.hasNext()) { // Merge the file contents into global container... const QString& sPath = iter.next(); if (m_pInstruments->load(sPath)) { // Start inserting in the current selected or last item... if (pItem == nullptr) pItem = m_ui.FilesListView->currentItem(); if (pItem == nullptr) { int iLastItem = m_ui.FilesListView->topLevelItemCount() - 1; if (iLastItem >= 0) pItem = m_ui.FilesListView->topLevelItem(iLastItem); } // New item on the block :-) pItem = new QTreeWidgetItem(m_ui.FilesListView, pItem); if (pItem) { const QFileInfo info(sPath); pItem->setIcon(0, QIcon::fromTheme("itemFile")); pItem->setText(0, info.completeBaseName()); pItem->setText(1, sPath); m_ui.FilesListView->setCurrentItem(pItem); pOptions->sInstrumentDir = info.absolutePath(); ++m_iDirtyCount; } } } // May refresh the whole form? refreshForm(); stabilizeForm(); // Done waiting. QApplication::restoreOverrideCursor(); } // Remove a file from instrument list. void qtractorInstrumentForm::removeSlot (void) { if (m_pInstruments == nullptr) return; QTreeWidgetItem *pItem = m_ui.FilesListView->currentItem(); if (pItem) { delete pItem; ++m_iDirtyCount; } reloadSlot(); } // Move a file up on the instrument list. void qtractorInstrumentForm::moveUpSlot (void) { QTreeWidgetItem *pItem = m_ui.FilesListView->currentItem(); if (pItem) { const int iItem = m_ui.FilesListView->indexOfTopLevelItem(pItem); if (iItem > 0) { pItem = m_ui.FilesListView->takeTopLevelItem(iItem); m_ui.FilesListView->insertTopLevelItem(iItem - 1, pItem); m_ui.FilesListView->setCurrentItem(pItem); ++m_iDirtyCount; } } reloadSlot(); } // Move a file down on the instrument list. void qtractorInstrumentForm::moveDownSlot (void) { QTreeWidgetItem *pItem = m_ui.FilesListView->currentItem(); if (pItem) { const int iItem = m_ui.FilesListView->indexOfTopLevelItem(pItem); if (iItem < m_ui.FilesListView->topLevelItemCount() - 1) { pItem = m_ui.FilesListView->takeTopLevelItem(iItem); m_ui.FilesListView->insertTopLevelItem(iItem + 1, pItem); m_ui.FilesListView->setCurrentItem(pItem); ++m_iDirtyCount; } } reloadSlot(); } // Reload the complete instrument definitions, from list. void qtractorInstrumentForm::reloadSlot (void) { // Get current instrument file list... QStringList files; const int iItemCount = m_ui.FilesListView->topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = m_ui.FilesListView->topLevelItem(iItem); if (pItem) files.append(pItem->text(1)); } // Load each file in order... reloadFiles(files); refreshForm(); stabilizeForm(); } // Export the whole state into a single instrument file. void qtractorInstrumentForm::exportSlot (void) { if (m_pInstruments == nullptr) return; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; QString sPath; const QString sExt("ins"); const QString& sTitle = tr("Export Instrument File"); QStringList filters; filters.append(tr("Instrument files (*.%1)").arg(sExt)); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to open... sPath = QFileDialog::getSaveFileName(pParentWidget, sTitle, pOptions->sInstrumentDir, sFilter, nullptr, options); #else // Construct open-files dialog... QFileDialog fileDialog(options & QFileDialog::DontUseNativeDialog ? this : nullptr, sTitle, pOptions->sInstrumentDir, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sInstrumentDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sPath = fileDialog.selectedFiles().first(); #endif if (sPath.isEmpty() || sPath.at(0) == '.') return; // Enforce .ins extension... if (QFileInfo(sPath).suffix().isEmpty()) { sPath += '.' + sExt; // Check if already exists... if (QFileInfo(sPath).exists()) { if (QMessageBox::warning(this, tr("Warning"), tr("The instrument file already exists:\n\n" "\"%1\"\n\n" "Do you want to replace it?") .arg(sPath), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } } // Tell that we may take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Just save the whole bunch... if (m_pInstruments->save(sPath)) pOptions->sInstrumentDir = QFileInfo(sPath).absolutePath(); // Done with export. QApplication::restoreOverrideCursor(); } // Accept settings (OK button slot). void qtractorInstrumentForm::accept (void) { // If we're dirty do a complete and final reload... if (m_iDirtyCount > 0) reloadSlot(); // Just go with dialog acceptance. QDialog::accept(); } // Reject settings (Cancel button slot). void qtractorInstrumentForm::reject (void) { bool bReject = true; // Check if there's any pending changes... if (m_iDirtyCount > 0) { switch (QMessageBox::warning(this, tr("Warning"), tr("Instrument settings have been changed.\n\n" "Do you want to apply the changes?"), QMessageBox::Apply | QMessageBox::Discard | QMessageBox::Cancel)) { case QMessageBox::Apply: accept(); return; case QMessageBox::Discard: reloadFiles(m_files); break; default: // Cancel. bReject = false; } } if (bReject) QDialog::reject(); } // Stabilize form status. void qtractorInstrumentForm::stabilizeForm (void) { QTreeWidgetItem *pItem = m_ui.FilesListView->currentItem(); if (pItem) { const int iItemCount = m_ui.FilesListView->topLevelItemCount(); const int iItem = m_ui.FilesListView->indexOfTopLevelItem(pItem); m_ui.RemovePushButton->setEnabled(true); m_ui.MoveUpPushButton->setEnabled(iItem > 0); m_ui.MoveDownPushButton->setEnabled(iItem < iItemCount - 1); } else { m_ui.RemovePushButton->setEnabled(false); m_ui.MoveUpPushButton->setEnabled(false); m_ui.MoveDownPushButton->setEnabled(false); } m_ui.ExportPushButton->setEnabled( m_pInstruments && m_pInstruments->count() > 0); } // Refresh all instrument definition views. void qtractorInstrumentForm::refreshForm (void) { if (m_pInstruments == nullptr) return; // Freeze... m_ui.InstrumentsListView->setUpdatesEnabled(false); m_ui.FilesListView->setUpdatesEnabled(false); m_ui.NamesListView->setUpdatesEnabled(false); // Files list view... m_ui.FilesListView->clear(); QList files; QStringListIterator ifile(m_pInstruments->files()); while (ifile.hasNext()) { const QString& sPath = ifile.next(); QTreeWidgetItem *pFileItem = new QTreeWidgetItem(); pFileItem->setIcon(0, QIcon::fromTheme("itemFile")); pFileItem->setText(0, QFileInfo(sPath).completeBaseName()); pFileItem->setText(1, sPath); files.append(pFileItem); } m_ui.FilesListView->addTopLevelItems(files); // Instruments list view... m_ui.InstrumentsListView->clear(); QList instrs; qtractorInstrumentList::ConstIterator iter = m_pInstruments->constBegin(); const qtractorInstrumentList::ConstIterator& iter_end = m_pInstruments->constEnd(); for ( ; iter != iter_end; ++iter) { const qtractorInstrument& instr = iter.value(); // Instrument Name... QTreeWidgetItem *pChildItem = nullptr; QTreeWidgetItem *pInstrItem = new QTreeWidgetItem(); pInstrItem->setIcon(0, QIcon::fromTheme("itemInstrument")); pInstrItem->setText(0, instr.instrumentName()); // - Patches Names for Banks... pChildItem = new qtractorInstrumentGroupItem(pInstrItem, pChildItem); pChildItem->setText(0, tr("Patch Names for Banks")); QTreeWidgetItem *pBankItem = nullptr; const qtractorInstrumentPatches& patches = instr.patches(); qtractorInstrumentPatches::ConstIterator pat = patches.constBegin(); const qtractorInstrumentPatches::ConstIterator& pat_end = patches.constEnd(); for ( ; pat != pat_end; ++pat) { pBankItem = new QTreeWidgetItem(pChildItem, pBankItem); const int iBank = pat.key(); const QString sBankName = (iBank < 0 ? QString("*") : QString::number(iBank)); pBankItem->setIcon(0, QIcon::fromTheme("itemPatches")); pBankItem->setText(0, QString("%1 = %2").arg(sBankName).arg(pat.value().name())); // Patches/Progs... const qtractorInstrumentData& patch = instr.patch(iBank); QTreeWidgetItem *pProgItem = nullptr; if (!patch.basedOn().isEmpty()) { pProgItem = new QTreeWidgetItem(pBankItem, pProgItem); pProgItem->setIcon(0, QIcon::fromTheme("itemProperty")); pProgItem->setText(0, QString("Based On = %1").arg(patch.basedOn())); } qtractorInstrumentData::ConstIterator it = patch.constBegin(); const qtractorInstrumentData::ConstIterator& it_end = patch.constEnd(); for ( ; it != it_end; ++it) { const int iProg = it.key(); pProgItem = new QTreeWidgetItem(pBankItem, pProgItem); pProgItem->setText(0, QString("%1 = %2").arg(iProg).arg(it.value())); if (instr.isDrum(iBank, iProg)) listInstrumentData(pProgItem, instr.notes(iBank, iProg)); } } // - Controller Names... if (instr.controllers().count() > 0) { pChildItem = new QTreeWidgetItem(pInstrItem, pChildItem); pChildItem->setIcon(0, QIcon::fromTheme("itemControllers")); pChildItem->setText(0, tr("Controller Names = %1").arg(instr.controllers().name())); listInstrumentData(pChildItem, instr.controllers()); } // - RPN Names... if (instr.rpns().count() > 0) { pChildItem = new QTreeWidgetItem(pInstrItem, pChildItem); pChildItem->setIcon(0, QIcon::fromTheme("itemRpns")); pChildItem->setText(0, tr("RPN Names = %1").arg(instr.rpns().name())); listInstrumentData(pChildItem, instr.rpns()); } // - NRPN Names... if (instr.nrpns().count() > 0) { pChildItem = new QTreeWidgetItem(pInstrItem, pChildItem); pChildItem->setIcon(0, QIcon::fromTheme("itemNrpns")); pChildItem->setText(0, tr("NRPN Names = %1").arg(instr.nrpns().name())); listInstrumentData(pChildItem, instr.nrpns()); } // - BankSelMethod... pChildItem = new QTreeWidgetItem(pInstrItem, pChildItem); pChildItem->setIcon(0, QIcon::fromTheme("itemProperty")); pChildItem->setText(0, tr("Bank Select Method = %1") .arg(bankSelMethod(instr.bankSelMethod()))); instrs.append(pInstrItem); } m_ui.InstrumentsListView->addTopLevelItems(instrs); // Names list view... m_ui.NamesListView->clear(); QList names; if (m_pInstruments->count() > 0) { QTreeWidgetItem *pListItem = nullptr; // - Patch Names... pListItem = new qtractorInstrumentGroupItem(); pListItem->setText(0, tr("Patch Names")); listInstrumentDataList(pListItem, m_pInstruments->patches(), QIcon::fromTheme("itemPatches")); names.append(pListItem); // - Note Names... pListItem = new qtractorInstrumentGroupItem(); pListItem->setText(0, tr("Note Names")); listInstrumentDataList(pListItem, m_pInstruments->notes(), QIcon::fromTheme("itemNotes")); names.append(pListItem); // - Controller Names... pListItem = new qtractorInstrumentGroupItem(); pListItem->setText(0, tr("Controller Names")); listInstrumentDataList(pListItem, m_pInstruments->controllers(), QIcon::fromTheme("itemControllers")); names.append(pListItem); // - RPN Names... pListItem = new qtractorInstrumentGroupItem(); pListItem->setText(0, tr("RPN Names")); listInstrumentDataList(pListItem, m_pInstruments->rpns(), QIcon::fromTheme("itemRpns")); names.append(pListItem); // - NRPN Names... pListItem = new qtractorInstrumentGroupItem(); pListItem->setText(0, tr("NRPN Names")); listInstrumentDataList(pListItem, m_pInstruments->nrpns(), QIcon::fromTheme("itemNrpns")); names.append(pListItem); // - Bank Select Methods... pListItem = new qtractorInstrumentGroupItem(); pListItem->setText(0, tr("Bank Select Methods")); if (m_pInstruments->count() > 0) { QTreeWidgetItem *pChildItem = nullptr; for (int iBankSelMethod = 0; iBankSelMethod < 4; ++iBankSelMethod) { pChildItem = new qtractorInstrumentGroupItem(pListItem, pChildItem); pChildItem->setIcon(0, QIcon::fromTheme("itemProperty")); pChildItem->setText(0, tr("%1 = %2").arg(iBankSelMethod) .arg(bankSelMethod(iBankSelMethod))); } } names.append(pListItem); } m_ui.NamesListView->addTopLevelItems(names); // Bail out... m_ui.NamesListView->setUpdatesEnabled(true); m_ui.FilesListView->setUpdatesEnabled(true); m_ui.InstrumentsListView->setUpdatesEnabled(true); } void qtractorInstrumentForm::itemCollapsed ( QTreeWidgetItem *pItem ) { if (pItem->type() == GroupItem) pItem->setIcon(0, QIcon::fromTheme("itemGroup")); } void qtractorInstrumentForm::itemExpanded ( QTreeWidgetItem *pItem ) { if (pItem->type() == GroupItem) pItem->setIcon(0, QIcon::fromTheme("itemGroupOpen")); } void qtractorInstrumentForm::listInstrumentData ( QTreeWidgetItem *pParentItem, const qtractorInstrumentData& data ) { QTreeWidgetItem *pItem = nullptr; if (!data.basedOn().isEmpty()) { pItem = new QTreeWidgetItem(pParentItem, pItem); pItem->setIcon(0, QIcon::fromTheme("itemProperty")); pItem->setText(0, tr("Based On = %1").arg(data.basedOn())); } qtractorInstrumentData::ConstIterator it = data.constBegin(); const qtractorInstrumentData::ConstIterator& it_end = data.constEnd(); for ( ; it != it_end; ++it) { pItem = new QTreeWidgetItem(pParentItem, pItem); pItem->setText(0, QString("%1 = %2").arg(it.key()).arg(it.value())); } } void qtractorInstrumentForm::listInstrumentDataList ( QTreeWidgetItem *pParentItem, const qtractorInstrumentDataList& list, const QIcon& icon ) { QTreeWidgetItem *pItem = nullptr; qtractorInstrumentDataList::ConstIterator it = list.constBegin(); const qtractorInstrumentDataList::ConstIterator& it_end = list.constEnd(); for ( ; it != it_end; ++it) { pItem = new QTreeWidgetItem(pParentItem, pItem); pItem->setIcon(0, icon); pItem->setText(0, it.value().name()); listInstrumentData(pItem, it.value()); } } QString qtractorInstrumentForm::bankSelMethod ( int iBankSelMethod ) const { QString sText; switch (iBankSelMethod) { case 0: sText = tr("Normal"); break; case 1: sText = tr("Bank MSB"); break; case 2: sText = tr("Bank LSB"); break; case 3: sText = tr("Patch"); break; default: sText = tr("Unknown"); break; } return sText; } // end of qtractorInstrumentForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorEngine.h0000644000000000000000000000013215124701674016450 xustar0030 mtime=1767080892.784263491 30 atime=1767080892.784263491 30 ctime=1767080892.784263491 qtractor-1.5.11/src/qtractorEngine.h0000644000175000001440000002360615124701674016447 0ustar00rncbcusers// qtractorEngine.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorEngine_h #define __qtractorEngine_h #include "qtractorTrack.h" // Forward declarations. class qtractorBus; class qtractorPluginList; class qtractorSessionCursor; class qtractorCurveFile; class QDomElement; //---------------------------------------------------------------------- // class qtractorEngine -- Abstract device engine instance (singleton). // class qtractorEngine { public: // Constructor. qtractorEngine(qtractorSession *pSession, qtractorTrack::TrackType syncType); // Destructor. virtual ~qtractorEngine(); // Device engine activation methods. bool open(); void close(); // Buses list clear. void clear(); // Session helper accessor. qtractorSession *session() const; // Session cursor accessor. qtractorSessionCursor *sessionCursor() const; // Engine type method. qtractorTrack::TrackType syncType() const; // Client name accessor. const QString& clientName() const; // Engine status methods. bool isActivated() const; // Engine state methods. void setPlaying(bool bPlaying); bool isPlaying() const; // Buses list management methods. const qtractorList& buses() const; void addBus(qtractorBus *pBus, qtractorBus *pAfterBus = nullptr); void removeBus(qtractorBus *pBus); void moveBus(qtractorBus *pBus, qtractorBus *pAfterBus); qtractorBus *findBus(const QString& sBusName) const; qtractorBus *findInputBus(const QString& sInputBusName) const; qtractorBus *findOutputBus(const QString& sOutputBusName) const; // Exo-buses list management methods. const qtractorList& busesEx() const; void addBusEx(qtractorBus *pBus); void removeBusEx(qtractorBus *pBus); qtractorBus *findBusEx(const QString& sBusName) const; // Retrieve/restore all connections, on all buses; // return the effective number of connection attempts. virtual int updateConnects(); // Document element methods. virtual bool loadElement(qtractorDocument *pDocument, QDomElement *pElement) = 0; virtual bool saveElement(qtractorDocument *pDocument, QDomElement *pElement) const = 0; // Clear/reset all pending connections. void clearConnects(); // Front-end/UI buses accessors. // const QList& buses2() const; void moveBus2(qtractorBus *pBus, int iDelta); void setBuses2List(const QStringList& list); QStringList buses2List() const; QStringList loadBuses2List( qtractorDocument *pDocument, QDomElement *pElement, const QString& sTagName); bool saveBuses2List( qtractorDocument *pDocument, QDomElement *pElement, const QString& sTagName, const QStringList& list) const; protected: // Derived classes must set on this... virtual bool init() = 0; virtual bool activate() = 0; virtual bool start() = 0; virtual void stop() = 0; virtual void deactivate() = 0; virtual void clean() = 0; // Retrieve/restore connections, on given buses; // return the effective number of connection attempts. int updateConnects(qtractorBus *pBus); private: // Device instance variables. qtractorSession *m_pSession; qtractorSessionCursor *m_pSessionCursor; // Engine running flags. bool m_bActivated; bool m_bPlaying; qtractorList m_buses; qtractorList m_busesEx; // Front-end/UI buses stuff. bool m_bBuses2; QList m_buses2; }; //---------------------------------------------------------------------- // class qtractorBus -- Abstract device bus. // class qtractorBus : public qtractorList::Link { public: // Bus operation mode bit-flags. enum BusMode { None = 0, Input = 1, Output = 2, Duplex = 3, Ex = 4 }; // Constructor. qtractorBus(qtractorEngine *pEngine, const QString& sBusName, BusMode busMode, bool bMonitor = false); // Destructor. virtual ~qtractorBus(); // Device accessor. qtractorEngine *engine() const; // Bus type method. qtractorTrack::TrackType busType() const; // Bus name accessors. void setBusName(const QString& sBusName); const QString& busName() const; // Bus mode property accessor. void setBusMode(BusMode busMode); BusMode busMode() const; // Pass-thru mode accessor. void setMonitor(bool bMonitor); bool isMonitor() const; // Pure virtual activation methods. virtual bool open() = 0; virtual void close() = 0; // I/O bus-monitor accessors. virtual qtractorMonitor *monitor_in() const = 0; virtual qtractorMonitor *monitor_out() const = 0; // I/O bus-monitor accessors. virtual qtractorPluginList *pluginList_in() const = 0; virtual qtractorPluginList *pluginList_out() const = 0; // State (monitor) button setup. qtractorSubject *monitorSubject() const; qtractorMidiControlObserver *monitorObserver() const; // State (monitor) notifier (proto-slot). void monitorChangeNotify(bool bOn); // Load/save bus (monitor, gain, pan) controllers (MIDI). void loadControllers( QDomElement *pElement, BusMode busMode); void saveControllers(qtractorDocument *pDocument, QDomElement *pElement, BusMode busMode) const; // Map bus (monitor, gain, pan) controllers (MIDI). void mapControllers(BusMode busMode); // Bus automation curve serialization methods. static void loadCurveFile( QDomElement *pElement, BusMode busMode, qtractorCurveFile *pCurveFile); void saveCurveFile(qtractorDocument *pDocument, QDomElement *pElement, BusMode busMode, qtractorCurveFile *pCurveFile) const; void applyCurveFile(BusMode busMode, qtractorCurveFile *pCurveFile) const; // Connection list stuff. struct ConnectItem { // Default contructor ConnectItem() : index(0), client(-1), port (-1) {} // Copy contructor ConnectItem(const ConnectItem& item) : index(item.index), client(item.client), port(item.port), clientName(item.clientName), portName(item.portName) {} // Item matcher... bool match(const ConnectItem& item) const { return index == item.index && clientName == item.clientName && portName == item.portName && (client < 0 || (128 >= client && client == item.client)); } // Item members. unsigned short index; int client, port; QString clientName; QString portName; }; class ConnectList : public QList { public: // Constructor. ConnectList() {} // Copy onstructor. ConnectList(const ConnectList& connects) : QList() { copy(connects); } // Destructor. ~ConnectList() { clear(); } // Item cleaner... void clear() { qDeleteAll(*this); QList::clear(); } // List copy... void copy (const ConnectList& connects) { clear(); QListIterator iter(connects); while (iter.hasNext()) append(new ConnectItem(*iter.next())); } // Item finder... ConnectItem *findItem(const ConnectItem& item) { QListIterator iter(*this); while (iter.hasNext()) { ConnectItem *pItem = iter.next(); if (pItem->match(item)) return pItem; } return nullptr; } }; // Connection lists accessors. ConnectList& inputs() { return m_inputs; } ConnectList& outputs() { return m_outputs; } // Retrieve/restore client:port connections; // return the effective number of connection attempts. virtual int updateConnects(BusMode busMode, ConnectList& connects, bool bConnect = false) const = 0; // Document element methods. static bool loadConnects(ConnectList& connects, qtractorDocument *pDocument, QDomElement *pElement); static bool saveConnects(ConnectList& connects, qtractorDocument *pDocument, QDomElement *pElement); // Bus mode textual helper methods. static BusMode busModeFromText (const QString& sText); static QString textFromBusMode (BusMode busMode); // Generic connections snapshot stuff. // class Connects { public: // Constructor. Connects() : m_busMode(None) {} // Destructor. ~Connects() { clear(); } // Property accessors. const QString& busName() const { return m_sBusName; } BusMode busMode() const { return m_busMode; } // Executive mthods. int save(qtractorBus *pBus); int load(qtractorBus *pBus); // Cleaner. void clear(); // private: // Instance members. QString m_sBusName; BusMode m_busMode; ConnectList m_inputs; ConnectList m_outputs; }; class Connections { public: // Constructor. Connections() {} // Destructor. ~Connections() { clear(); } // Executive methods. bool load(qtractorEngine *pEngine); bool save(qtractorEngine *pEngine); int save(qtractorBus *pBus); // Cleaner. void clear(); // private: // Container list. QList m_list; }; protected: // Bus mode/name change events. virtual void updateBusMode() = 0; virtual void updateBusName(); private: // Instance variables. qtractorEngine *m_pEngine; QString m_sBusName; BusMode m_busMode; // Connections stuff. ConnectList m_inputs; ConnectList m_outputs; // State (monitor) observer stuff. qtractorSubject *m_pMonitorSubject; qtractorMidiControlObserver *m_pMonitorObserver; qtractorMidiControl::Controllers m_controllers_in; qtractorMidiControl::Controllers m_controllers_out; }; #endif // __qtractorEngine_h // end of qtractorEngine.h qtractor-1.5.11/src/PaxHeaders/qtractorVst3Plugin.cpp0000644000000000000000000000013215124701674017614 xustar0030 mtime=1767080892.805263403 30 atime=1767080892.804263407 30 ctime=1767080892.805263403 qtractor-1.5.11/src/qtractorVst3Plugin.cpp0000644000175000001440000026507415124701674017622 0ustar00rncbcusers// qtractorVst3Plugin.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #ifdef CONFIG_VST3 #include "qtractorVst3Plugin.h" #include "qtractorSession.h" #include "qtractorAudioEngine.h" #include "qtractorMidiManager.h" #include "pluginterfaces/vst/ivsthostapplication.h" #include "pluginterfaces/vst/ivstpluginterfacesupport.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstprocesscontext.h" #include "pluginterfaces/vst/ivstparameterchanges.h" #include "pluginterfaces/vst/ivstmidicontrollers.h" #include "pluginterfaces/vst/ivstevents.h" #include "pluginterfaces/vst/ivstunits.h" #include "pluginterfaces/gui/iplugview.h" #include "pluginterfaces/base/ibstream.h" #include "base/source/fobject.h" #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #define CONFIG_VST3_XCB #endif #ifdef CONFIG_VST3_XCB #include #endif #if 0//QTRACTOR_VST3_EDITOR_TOOL #include "qtractorOptions.h" #endif #include "qtractorMainForm.h" using namespace Steinberg; using namespace Linux; //----------------------------------------------------------------------------- // A Vst::String128 to QString converter. // static inline QString fromTChar ( const Vst::TChar *str ) { return QString::fromUtf16(reinterpret_cast (str)); } //----------------------------------------------------------------------------- // class qtractorVst3PluginHost -- VST3 plugin host context decl. // class qtractorVst3PluginHost : public Vst::IHostApplication { public: // Constructor. qtractorVst3PluginHost (); // Destructor. virtual ~qtractorVst3PluginHost (); DECLARE_FUNKNOWN_METHODS //--- IHostApplication --- // tresult PLUGIN_API getName (Vst::String128 name) override; tresult PLUGIN_API createInstance (TUID cid, TUID _iid, void **obj) override; FUnknown *get() { return static_cast (this); } // QTimer stuff... // void startTimer (int msecs); void stopTimer (); int timerInterval() const; // RunLoop adapters... // tresult registerEventHandler (IEventHandler *handler, FileDescriptor fd); tresult unregisterEventHandler (IEventHandler *handler); tresult registerTimer (ITimerHandler *handler, TimerInterval msecs); tresult unregisterTimer (ITimerHandler *handler); // Executive methods. // void processTimers(); void processEventHandlers(); #ifdef CONFIG_VST3_XCB void openXcbConnection(); void closeXcbConnection(); #endif // Common host time-keeper context accessors. Vst::ProcessContext *processContext(); void processAddRef(); void processReleaseRef(); // Common host time-keeper process context. void updateProcessContext(qtractorAudioEngine *pAudioEngine); // Cleanup. void clear(); protected: class PlugInterfaceSupport; class Attribute; class AttributeList; class Message; class Timer; private: // Instance members. IPtr m_plugInterfaceSupport; Timer *m_pTimer; unsigned int m_timerRefCount; struct TimerHandlerItem { TimerHandlerItem(ITimerHandler *h, TimerInterval i) : handler(h), interval(i), counter(0) {} void reset(TimerInterval i) { interval = i; counter = 0; } ITimerHandler *handler; TimerInterval interval; TimerInterval counter; }; QHash m_timerHandlers; QList m_timerHandlerItems; QMultiHash m_eventHandlers; #ifdef CONFIG_VST3_XCB xcb_connection_t *m_pXcbConnection; int m_iXcbFileDescriptor; #endif Vst::ProcessContext m_processContext; unsigned int m_processRefCount; }; //----------------------------------------------------------------------------- // class qtractorVst3PluginHost::PlugInterfaceSupport : public FObject, public Vst::IPlugInterfaceSupport { public: // Constructor. PlugInterfaceSupport () { addPluInterfaceSupported(Vst::IComponent::iid); addPluInterfaceSupported(Vst::IAudioProcessor::iid); addPluInterfaceSupported(Vst::IEditController::iid); addPluInterfaceSupported(Vst::IConnectionPoint::iid); addPluInterfaceSupported(Vst::IUnitInfo::iid); // addPluInterfaceSupported(Vst::IUnitData::iid); addPluInterfaceSupported(Vst::IProgramListData::iid); addPluInterfaceSupported(Vst::IMidiMapping::iid); // addPluInterfaceSupported(Vst::IEditController2::iid); } OBJ_METHODS (PlugInterfaceSupport, FObject) REFCOUNT_METHODS (FObject) DEFINE_INTERFACES DEF_INTERFACE (Vst::IPlugInterfaceSupport) END_DEFINE_INTERFACES (FObject) //--- IPlugInterfaceSupport ---- // tresult PLUGIN_API isPlugInterfaceSupported (const TUID _iid) override { if (m_fuids.contains(QString::fromLocal8Bit(_iid))) return kResultOk; else return kResultFalse; } protected: void addPluInterfaceSupported(const TUID& _iid) { m_fuids.append(QString::fromLocal8Bit(_iid)); } private: // Instance members. QList m_fuids; }; //----------------------------------------------------------------------------- // class qtractorVst3PluginHost::Attribute { public: enum Type { kInteger, kFloat, kString, kBinary }; // Constructors. Attribute (int64 value) : m_size(0), m_type(kInteger) { m_v.intValue = value; } Attribute (double value) : m_size(0), m_type(kFloat) { m_v.floatValue = value; } Attribute (const Vst::TChar *value, uint32 size) : m_size(size), m_type(kString) { m_v.stringValue = new Vst::TChar[size]; ::memcpy(m_v.stringValue, value, size * sizeof (Vst::TChar)); } Attribute (const void *value, uint32 size) : m_size(size), m_type(kBinary) { m_v.binaryValue = new char[size]; ::memcpy(m_v.binaryValue, value, size); } // Destructor. ~Attribute () { if (m_size) delete [] m_v.binaryValue; } // Accessors. int64 intValue () const { return m_v.intValue; } double floatValue () const { return m_v.floatValue; } const Vst::TChar *stringValue ( uint32& stringSize ) { stringSize = m_size; return m_v.stringValue; } const void *binaryValue ( uint32& binarySize ) { binarySize = m_size; return m_v.binaryValue; } Type getType () const { return m_type; } protected: // Instance members. union v { int64 intValue; double floatValue; Vst::TChar *stringValue; char *binaryValue; } m_v; uint32 m_size; Type m_type; }; //----------------------------------------------------------------------------- // class qtractorVst3PluginHost::AttributeList : public Vst::IAttributeList { public: // Constructor. AttributeList () { FUNKNOWN_CTOR } // Destructor. virtual ~AttributeList () { qDeleteAll(m_list); m_list.clear(); FUNKNOWN_DTOR } DECLARE_FUNKNOWN_METHODS //--- IAttributeList --- // tresult PLUGIN_API setInt (AttrID aid, int64 value) override { removeAttrID(aid); m_list.insert(aid, new Attribute(value)); return kResultTrue; } tresult PLUGIN_API getInt (AttrID aid, int64& value) override { Attribute *attr = m_list.value(aid, nullptr); if (attr) { value = attr->intValue(); return kResultTrue; } return kResultFalse; } tresult PLUGIN_API setFloat (AttrID aid, double value) override { removeAttrID(aid); m_list.insert(aid, new Attribute(value)); return kResultTrue; } tresult PLUGIN_API getFloat (AttrID aid, double& value) override { Attribute *attr = m_list.value(aid, nullptr); if (attr) { value = attr->floatValue(); return kResultTrue; } return kResultFalse; } tresult PLUGIN_API setString (AttrID aid, const Vst::TChar *string) override { removeAttrID(aid); m_list.insert(aid, new Attribute(string, fromTChar(string).length())); return kResultTrue; } tresult PLUGIN_API getString (AttrID aid, Vst::TChar *string, uint32 size) override { Attribute *attr = m_list.value(aid, nullptr); if (attr) { uint32 size2 = 0; const Vst::TChar *string2 = attr->stringValue(size2); ::memcpy(string, string2, qMin(size, size2) * sizeof(Vst::TChar)); return kResultTrue; } return kResultFalse; } tresult PLUGIN_API setBinary (AttrID aid, const void* data, uint32 size) override { removeAttrID(aid); m_list.insert(aid, new Attribute(data, size)); return kResultTrue; } tresult PLUGIN_API getBinary (AttrID aid, const void*& data, uint32& size) override { Attribute *attr = m_list.value(aid, nullptr); if (attr) { data = attr->binaryValue(size); return kResultTrue; } size = 0; return kResultFalse; } protected: void removeAttrID (AttrID aid) { Attribute *attr = m_list.value(aid, nullptr); if (attr) { delete attr; m_list.remove(aid); } } private: // Instance members. QHash m_list; }; IMPLEMENT_FUNKNOWN_METHODS (qtractorVst3PluginHost::AttributeList, IAttributeList, IAttributeList::iid) //----------------------------------------------------------------------------- // class qtractorVst3PluginHost::Message : public Vst::IMessage { public: // Constructor. Message () : m_messageId(nullptr), m_attributeList(nullptr) { FUNKNOWN_CTOR } // Destructor. virtual ~Message () { setMessageID(nullptr); if (m_attributeList) m_attributeList->release(); FUNKNOWN_DTOR } DECLARE_FUNKNOWN_METHODS //--- IMessage --- // const char *PLUGIN_API getMessageID () override { return m_messageId; } void PLUGIN_API setMessageID (const char *messageId) override { if (m_messageId) delete [] m_messageId; m_messageId = nullptr; if (messageId) { size_t len = strlen(messageId) + 1; m_messageId = new char[len]; ::strcpy(m_messageId, messageId); } } Vst::IAttributeList* PLUGIN_API getAttributes () override { if (!m_attributeList) m_attributeList = new AttributeList(); return m_attributeList; } protected: // Instance members. char *m_messageId; AttributeList *m_attributeList; }; IMPLEMENT_FUNKNOWN_METHODS (qtractorVst3PluginHost::Message, IMessage, IMessage::iid) //----------------------------------------------------------------------------- // class qtractorVst3PluginHost::Timer -- VST3 plugin host timer impl. // class qtractorVst3PluginHost::Timer : public QTimer { public: // Constructor. Timer (qtractorVst3PluginHost *pHost) : QTimer(), m_pHost(pHost) {} // Main method. void start (int msecs) { const int DEFAULT_MSECS = 30; int iInterval = QTimer::interval(); if (iInterval == 0) iInterval = DEFAULT_MSECS; if (iInterval > msecs) iInterval = msecs; QTimer::start(iInterval); } protected: void timerEvent (QTimerEvent *pTimerEvent) { if (pTimerEvent->timerId() == QTimer::timerId()) { m_pHost->processTimers(); m_pHost->processEventHandlers(); } } private: // Instance members. qtractorVst3PluginHost *m_pHost; }; //----------------------------------------------------------------------------- // class qtractorVst3PluginHost -- VST3 plugin host context impl. // // Constructor. qtractorVst3PluginHost::qtractorVst3PluginHost (void) { FUNKNOWN_CTOR m_plugInterfaceSupport = owned(NEW PlugInterfaceSupport()); m_pTimer = new Timer(this); m_timerRefCount = 0; #ifdef CONFIG_VST3_XCB m_pXcbConnection = nullptr; m_iXcbFileDescriptor = 0; #endif m_processRefCount = 0; } // Destructor. qtractorVst3PluginHost::~qtractorVst3PluginHost (void) { clear(); delete m_pTimer; m_plugInterfaceSupport = nullptr; FUNKNOWN_DTOR } //--- IHostApplication --- // tresult PLUGIN_API qtractorVst3PluginHost::getName ( Vst::String128 name ) { const QString str("qtractorVst3PluginHost"); const int nsize = qMin(str.length(), 127); ::memcpy(name, str.utf16(), nsize * sizeof(Vst::TChar)); name[nsize] = 0; return kResultOk; } tresult PLUGIN_API qtractorVst3PluginHost::createInstance ( TUID cid, TUID _iid, void **obj ) { const FUID classID (FUID::fromTUID(cid)); const FUID interfaceID (FUID::fromTUID(_iid)); if (classID == Vst::IMessage::iid && interfaceID == Vst::IMessage::iid) { *obj = new Message(); return kResultOk; } else if (classID == Vst::IAttributeList::iid && interfaceID == Vst::IAttributeList::iid) { *obj = new AttributeList(); return kResultOk; } *obj = nullptr; return kResultFalse; } tresult PLUGIN_API qtractorVst3PluginHost::queryInterface ( const char *_iid, void **obj ) { QUERY_INTERFACE(_iid, obj, FUnknown::iid, IHostApplication) QUERY_INTERFACE(_iid, obj, IHostApplication::iid, IHostApplication) if (m_plugInterfaceSupport && m_plugInterfaceSupport->queryInterface(_iid, obj) == kResultOk) return kResultOk; *obj = nullptr; return kResultFalse; } uint32 PLUGIN_API qtractorVst3PluginHost::addRef (void) { return 1; } uint32 PLUGIN_API qtractorVst3PluginHost::release (void) { return 1; } // QTimer stuff... // void qtractorVst3PluginHost::startTimer ( int msecs ) { if (++m_timerRefCount == 1) m_pTimer->start(msecs); } void qtractorVst3PluginHost::stopTimer (void) { if (m_timerRefCount > 0 && --m_timerRefCount == 0) m_pTimer->stop(); } int qtractorVst3PluginHost::timerInterval (void) const { return m_pTimer->interval(); } // IRunLoop stuff... // tresult qtractorVst3PluginHost::registerEventHandler ( IEventHandler *handler, FileDescriptor fd ) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginHost::registerEventHandler(%p, %d)", handler, int(fd)); #endif m_eventHandlers.insert(handler, int(fd)); return kResultOk; } tresult qtractorVst3PluginHost::unregisterEventHandler ( IEventHandler *handler ) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginHost::unregisterEventHandler(%p)", handler); #endif m_eventHandlers.remove(handler); return kResultOk; } tresult qtractorVst3PluginHost::registerTimer ( ITimerHandler *handler, TimerInterval msecs ) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginHost::registerTimer(%p, %u)", handler, uint(msecs)); #endif TimerHandlerItem *timer_handler = m_timerHandlers.value(handler, nullptr); if (timer_handler) { timer_handler->reset(msecs); } else { timer_handler = new TimerHandlerItem(handler, msecs); m_timerHandlers.insert(handler, timer_handler); } m_pTimer->start(int(msecs)); return kResultOk; } tresult qtractorVst3PluginHost::unregisterTimer ( ITimerHandler *handler ) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginHost::unregisterTimer(%p)", handler); #endif TimerHandlerItem *timer_handler = m_timerHandlers.value(handler, nullptr); if (timer_handler) { m_timerHandlers.remove(handler); m_timerHandlerItems.append(timer_handler); } if (m_timerHandlers.isEmpty()) m_pTimer->stop(); return kResultOk; } // Executive methods. // void qtractorVst3PluginHost::processTimers (void) { foreach (TimerHandlerItem *timer_handler, m_timerHandlers) { timer_handler->counter += timerInterval(); if (timer_handler->counter >= timer_handler->interval) { timer_handler->handler->onTimer(); timer_handler->counter = 0; } } } void qtractorVst3PluginHost::processEventHandlers (void) { int nfds = 0; fd_set rfds; fd_set wfds; fd_set efds; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); #ifdef CONFIG_VST3_XCB if (m_iXcbFileDescriptor) { FD_SET(m_iXcbFileDescriptor, &rfds); FD_SET(m_iXcbFileDescriptor, &wfds); FD_SET(m_iXcbFileDescriptor, &efds); nfds = qMax(nfds, m_iXcbFileDescriptor); } #endif QMultiHash::ConstIterator iter = m_eventHandlers.constBegin(); for ( ; iter != m_eventHandlers.constEnd(); ++iter) { foreach (int fd, m_eventHandlers.values(iter.key())) { FD_SET(fd, &rfds); FD_SET(fd, &wfds); FD_SET(fd, &efds); nfds = qMax(nfds, fd); } } timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 1000 * timerInterval(); const int result = ::select(nfds, &rfds, &wfds, nullptr, &timeout); if (result > 0) { iter = m_eventHandlers.constBegin(); for ( ; iter != m_eventHandlers.constEnd(); ++iter) { foreach (int fd, m_eventHandlers.values(iter.key())) { if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &wfds) || FD_ISSET(fd, &efds)) { IEventHandler *handler = iter.key(); handler->onFDIsSet(fd); } } } } } #ifdef CONFIG_VST3_XCB void qtractorVst3PluginHost::openXcbConnection (void) { if (m_pXcbConnection == nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginHost::openXcbConnection()"); #endif m_pXcbConnection = ::xcb_connect(nullptr, nullptr); m_iXcbFileDescriptor = ::xcb_get_file_descriptor(m_pXcbConnection); } } void qtractorVst3PluginHost::closeXcbConnection (void) { if (m_pXcbConnection) { ::xcb_disconnect(m_pXcbConnection); m_pXcbConnection = nullptr; m_iXcbFileDescriptor = 0; #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginHost::closeXcbConnection()"); #endif } } #endif // CONFIG_VST3_XCB // Common host time-keeper context accessor. Vst::ProcessContext *qtractorVst3PluginHost::processContext (void) { return &m_processContext; } void qtractorVst3PluginHost::processAddRef (void) { ++m_processRefCount; } void qtractorVst3PluginHost::processReleaseRef (void) { if (m_processRefCount > 0) --m_processRefCount; } // Common host time-keeper process context. void qtractorVst3PluginHost::updateProcessContext ( qtractorAudioEngine *pAudioEngine ) { if (m_processRefCount < 1) return; const qtractorAudioEngine::TimeInfo& timeInfo = pAudioEngine->timeInfo(); if (timeInfo.playing) m_processContext.state |= Vst::ProcessContext::kPlaying; else m_processContext.state &= ~Vst::ProcessContext::kPlaying; m_processContext.sampleRate = timeInfo.sampleRate; m_processContext.projectTimeSamples = timeInfo.frame; m_processContext.state |= Vst::ProcessContext::kProjectTimeMusicValid; m_processContext.projectTimeMusic = timeInfo.beats; m_processContext.state |= Vst::ProcessContext::kBarPositionValid; m_processContext.barPositionMusic = timeInfo.beats; m_processContext.state |= Vst::ProcessContext::kTempoValid; m_processContext.tempo = timeInfo.tempo; m_processContext.state |= Vst::ProcessContext::kTimeSigValid; m_processContext.timeSigNumerator = timeInfo.beatsPerBar; m_processContext.timeSigDenominator = timeInfo.beatType; } // Cleanup. void qtractorVst3PluginHost::clear (void) { #ifdef CONFIG_VST3_XCB closeXcbConnection(); #endif m_timerRefCount = 0; m_processRefCount = 0; qDeleteAll(m_timerHandlerItems); m_timerHandlerItems.clear(); m_timerHandlers.clear(); m_eventHandlers.clear(); ::memset(&m_processContext, 0, sizeof(Vst::ProcessContext)); } // Host singleton. static qtractorVst3PluginHost g_hostContext; //---------------------------------------------------------------------- // class qtractorVst3PluginType::Impl -- VST3 plugin meta-interface impl. // class qtractorVst3PluginType::Impl { public: // Constructor. Impl (qtractorPluginFile *pFile) : m_pFile(pFile), m_component(nullptr), m_controller(nullptr), m_unitInfos(nullptr), m_iUniqueID(0) {} // Destructor. ~Impl () { close(); } // Executive methods. bool open (unsigned long iIndex); void close (); // Accessors. Vst::IComponent *component () const { return m_component; } Vst::IEditController *controller () const { return m_controller; } Vst::IUnitInfo *unitInfos () const { return m_unitInfos; } const QString& name () const { return m_sName; } const QString& category () const { return m_sCategory; } const QString& subCategories () const { return m_sSubCategories; } const QString& vendor () const { return m_sVendor; } const QString& email () const { return m_sEmail; } const QString& url () const { return m_sUrl; } const QString& version () const { return m_sVersion; } const QString& sdkVersion () const { return m_sSdkVersion; } unsigned long uniqueID () const { return m_iUniqueID; } int numChannels (Vst::MediaType type, Vst::BusDirection direction) const; private: // Instance members. qtractorPluginFile *m_pFile; IPtr m_component; IPtr m_controller; IPtr m_unitInfos; QString m_sName; QString m_sCategory; QString m_sSubCategories; QString m_sVendor; QString m_sEmail; QString m_sUrl; QString m_sVersion; QString m_sSdkVersion; unsigned long m_iUniqueID; }; //---------------------------------------------------------------------- // class qtractorVst3PluginType::Impl -- VST3 plugin interface impl. // // Executive methods. // bool qtractorVst3PluginType::Impl::open ( unsigned long iIndex ) { close(); typedef bool (PLUGIN_API *VST3_ModuleEntry)(void *); const VST3_ModuleEntry module_entry = reinterpret_cast (m_pFile->resolve("ModuleEntry")); if (module_entry) module_entry(m_pFile->module()); typedef IPluginFactory *(PLUGIN_API *VST3_GetPluginFactory)(); const VST3_GetPluginFactory get_plugin_factory = reinterpret_cast (m_pFile->resolve("GetPluginFactory")); if (!get_plugin_factory) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginType::Impl[%p]::open(\"%s\", %lu)" " *** Failed to resolve plug-in factory.", this, m_pFile->filename().toUtf8().constData(), iIndex); #endif return false; } IPluginFactory *factory = get_plugin_factory(); if (!factory) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginType::Impl[%p]::open(\"%s\", %lu)" " *** Failed to retrieve plug-in factory.", this, m_pFile->filename().toUtf8().constData(), iIndex); #endif return false; } PFactoryInfo factoryInfo; if (factory->getFactoryInfo(&factoryInfo) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginType::Impl[%p]::open(\"%s\", %lu)" " *** Failed to retrieve plug-in factory information.", this, m_pFile->filename().toUtf8().constData(), iIndex); #endif return false; } IPluginFactory2 *factory2 = FUnknownPtr (factory); IPluginFactory3 *factory3 = FUnknownPtr (factory); if (factory3) factory3->setHostContext(g_hostContext.get()); const int32 nclasses = factory->countClasses(); unsigned long i = 0; for (int32 n = 0; n < nclasses; ++n) { PClassInfo classInfo; if (factory->getClassInfo(n, &classInfo) != kResultOk) continue; if (::strcmp(classInfo.category, kVstAudioEffectClass)) continue; if (iIndex == i) { PClassInfoW classInfoW; if (factory3 && factory3->getClassInfoUnicode(n, &classInfoW) == kResultOk) { m_sName = fromTChar(classInfoW.name); m_sCategory = QString::fromLocal8Bit(classInfoW.category); m_sSubCategories = QString::fromLocal8Bit(classInfoW.subCategories); m_sVendor = fromTChar(classInfoW.vendor); m_sVersion = fromTChar(classInfoW.version); m_sSdkVersion = fromTChar(classInfoW.sdkVersion); } else { PClassInfo2 classInfo2; if (factory2 && factory2->getClassInfo2(n, &classInfo2) == kResultOk) { m_sName = QString::fromLocal8Bit(classInfo2.name); m_sCategory = QString::fromLocal8Bit(classInfo2.category); m_sSubCategories = QString::fromLocal8Bit(classInfo2.subCategories); m_sVendor = QString::fromLocal8Bit(classInfo2.vendor); m_sVersion = QString::fromLocal8Bit(classInfo2.version); m_sSdkVersion = QString::fromLocal8Bit(classInfo2.sdkVersion); } else { m_sName = QString::fromLocal8Bit(classInfo.name); m_sCategory = QString::fromLocal8Bit(classInfo.category); m_sSubCategories.clear(); m_sVendor.clear(); m_sVersion.clear(); m_sSdkVersion.clear(); } } if (m_sVendor.isEmpty()) m_sVendor = QString::fromLocal8Bit(factoryInfo.vendor); if (m_sEmail.isEmpty()) m_sEmail = QString::fromLocal8Bit(factoryInfo.email); if (m_sUrl.isEmpty()) m_sUrl = QString::fromLocal8Bit(factoryInfo.url); m_iUniqueID = qHash(QByteArray(classInfo.cid, sizeof(TUID))); Vst::IComponent *component = nullptr; if (factory->createInstance( classInfo.cid, Vst::IComponent::iid, (void **) &component) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginType::Impl[%p]::open(\"%s\", %lu)" " *** Failed to create plug-in component.", this, m_pFile->filename().toUtf8().constData(), iIndex); #endif return false; } m_component = owned(component); if (m_component->initialize(g_hostContext.get()) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginType::Impl[%p]::open(\"%s\", %lu)" " *** Failed to initialize plug-in component.", this, m_pFile->filename().toUtf8().constData(), iIndex); #endif close(); return false; } Vst::IEditController *controller = nullptr; if (m_component->queryInterface( Vst::IEditController::iid, (void **) &controller) != kResultOk) { TUID controller_cid; if (m_component->getControllerClassId(controller_cid) == kResultOk) { if (factory->createInstance( controller_cid, Vst::IEditController::iid, (void **) &controller) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginType::Impl[%p]::open(\"%s\", %lu)" " *** Failed to create plug-in controller.", this, m_pFile->filename().toUtf8().constData(), iIndex); #endif } if (controller && controller->initialize(g_hostContext.get()) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginType::Impl[%p]::open(\"%s\", %lu)" " *** Failed to initialize plug-in controller.", this, m_pFile->filename().toUtf8().constData(), iIndex); controller = nullptr; #endif } } } if (controller) m_controller = owned(controller); Vst::IUnitInfo *unitInfos = nullptr; if (m_component->queryInterface( Vst::IUnitInfo::iid, (void **) &unitInfos) != kResultOk) { if (m_controller && m_controller->queryInterface( Vst::IUnitInfo::iid, (void **) &unitInfos) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3PluginType::Impl[%p]::open(\"%s\", %lu)" " *** Failed to create plug-in units information.", this, m_pFile->filename().toUtf8().constData(), iIndex); #endif } } if (unitInfos) m_unitInfos = owned(unitInfos); // Connect components... if (m_component && m_controller) { FUnknownPtr component_cp(m_component); FUnknownPtr controller_cp(m_controller); if (component_cp && controller_cp) { component_cp->connect(controller_cp); controller_cp->connect(component_cp); } } return true; } ++i; } return false; } void qtractorVst3PluginType::Impl::close (void) { if (m_component && m_controller) { FUnknownPtr component_cp(m_component); FUnknownPtr controller_cp(m_controller); if (component_cp && controller_cp) { component_cp->disconnect(controller_cp); controller_cp->disconnect(component_cp); } } m_unitInfos = nullptr; if (m_component && m_controller && FUnknownPtr (m_component).getInterface()) { m_controller->terminate(); } m_controller = nullptr; if (m_component) { m_component->terminate(); m_component = nullptr; typedef bool (PLUGIN_API *VST3_ModuleExit)(); const VST3_ModuleExit module_exit = reinterpret_cast (m_pFile->resolve("ModuleExit")); if (module_exit) module_exit(); } } int qtractorVst3PluginType::Impl::numChannels ( Vst::MediaType type, Vst::BusDirection direction ) const { if (!m_component) return -1; int nchannels = 0; int nactive = 0; const int32 nbuses = m_component->getBusCount(type, direction); for (int32 i = 0; i < nbuses; ++i) { Vst::BusInfo busInfo; if (m_component->getBusInfo(type, direction, i, busInfo) == kResultOk) { if (busInfo.busType == Vst::kMain) { nchannels += busInfo.channelCount; if (busInfo.flags & Vst::BusInfo::kDefaultActive) nactive += busInfo.channelCount; } } } return (nactive > 0 ? nactive : nchannels); } //---------------------------------------------------------------------- // class qtractorVst3PluginType -- VST3 plugin meta-interface impl. // // Constructor. qtractorVst3PluginType::qtractorVst3PluginType ( qtractorPluginFile *pFile, unsigned long iIndex ) : qtractorPluginType(pFile, iIndex, Vst3), m_pImpl(new Impl(pFile)) { } // Destructor. qtractorVst3PluginType::~qtractorVst3PluginType (void) { close(); delete m_pImpl; } // Factory method (static) qtractorVst3PluginType *qtractorVst3PluginType::createType ( qtractorPluginFile *pFile, unsigned long iIndex ) { return new qtractorVst3PluginType(pFile, iIndex); } // Executive methods. bool qtractorVst3PluginType::open (void) { close(); if (!m_pImpl->open(index())) return false; // Properties... m_sName = m_pImpl->name(); m_sLabel = m_sName.simplified().replace(QRegularExpression("[\\s|\\.|\\-]+"), "_"); m_iUniqueID = m_pImpl->uniqueID(); m_iAudioIns = m_pImpl->numChannels(Vst::kAudio, Vst::kInput); m_iAudioOuts = m_pImpl->numChannels(Vst::kAudio, Vst::kOutput); m_iMidiIns = m_pImpl->numChannels(Vst::kEvent, Vst::kInput); m_iMidiOuts = m_pImpl->numChannels(Vst::kEvent, Vst::kOutput); Vst::IEditController *controller = m_pImpl->controller(); if (controller) { IPtr editor = owned(controller->createView(Vst::ViewType::kEditor)); m_bEditor = (editor != nullptr); } m_bRealtime = true; m_bConfigure = true; m_iControlIns = 0; m_iControlOuts = 0; if (controller) { const int32 nparams = controller->getParameterCount(); for (int32 i = 0; i < nparams; ++i) { Vst::ParameterInfo paramInfo; ::memset(¶mInfo, 0, sizeof(Vst::ParameterInfo)); if (controller->getParameterInfo(i, paramInfo) == kResultOk) { if (paramInfo.flags & Vst::ParameterInfo::kIsReadOnly) ++m_iControlOuts; else if (paramInfo.flags & Vst::ParameterInfo::kCanAutomate) ++m_iControlIns; } } } return true; } void qtractorVst3PluginType::close (void) { m_pImpl->close(); } // Instance cached-deferred accessors. const QString& qtractorVst3PluginType::aboutText (void) { if (m_sAboutText.isEmpty()) { #if 0 m_sAboutText += QObject::tr("Name: "); m_sAboutText += m_pImpl->name(); #endif QString sText = m_pImpl->version(); if (!sText.isEmpty()) { // m_sAboutText += '\n'; m_sAboutText += QObject::tr("Version: "); m_sAboutText += sText; } sText = m_pImpl->sdkVersion(); if (!sText.isEmpty()) { m_sAboutText += '\t'; m_sAboutText += '('; m_sAboutText += sText; m_sAboutText += ')'; } #if 0 sText = m_pImpl->category(); if (!sText.isEmpty()) { m_sAboutText += '\n'; m_sAboutText += QObject::tr("Category: "); m_sAboutText += sText; } #endif sText = m_pImpl->subCategories(); if (!sText.isEmpty()) { m_sAboutText += '\n'; m_sAboutText += QObject::tr("Categories: "); m_sAboutText += sText; } sText = m_pImpl->vendor(); if (!sText.isEmpty()) { m_sAboutText += '\n'; m_sAboutText += QObject::tr("Vendor: "); m_sAboutText += sText; } #if 0 sText = m_pImpl->email(); if (!sText.isEmpty()) { m_sAboutText += '\n'; // m_sAboutText += QObject::tr("Email: "); m_sAboutText += sText; } #endif sText = m_pImpl->url(); if (!sText.isEmpty()) { m_sAboutText += '\n'; // m_sAboutText += QObject::tr("URL: "); m_sAboutText += sText; } } return m_sAboutText; } //---------------------------------------------------------------------- // class qtractorVst3Plugin::ParamQueue -- VST3 plugin parameter queue impl. // class qtractorVst3Plugin::ParamQueue : public Vst::IParamValueQueue { public: // Constructor. ParamQueue (int32 nsize = 8) : m_id(Vst::kNoParamId), m_queue(nullptr), m_nsize(0), m_ncount(0) { FUNKNOWN_CTOR resize(nsize); } // Destructor. virtual ~ParamQueue () { resize(0); FUNKNOWN_DTOR } DECLARE_FUNKNOWN_METHODS //--- IParamValueQueue --- // Vst::ParamID PLUGIN_API getParameterId () override { return m_id; } int32 PLUGIN_API getPointCount () override { return m_ncount; } tresult PLUGIN_API getPoint ( int32 index, int32& offset, Vst::ParamValue& value ) override { if (index < 0 || index >= m_ncount) return kResultFalse; const QueueItem& item = m_queue[index]; offset = item.offset; value = item.value; return kResultOk; } tresult PLUGIN_API addPoint ( int32 offset, Vst::ParamValue value, int32& index ) override { int32 i = 0; for ( ; i < m_ncount; ++i) { QueueItem& item = m_queue[i]; if (item.offset > offset) break; if (item.offset == offset) { item.value = value; index = i; return kResultOk; } } if (i >= m_nsize) resize(m_nsize); // warning: non RT-safe! index = i; QueueItem& item = m_queue[index]; item.value = value; item.offset = offset; i = m_ncount++; while (i > index) { QueueItem& item2 = m_queue[i]; QueueItem& item1 = m_queue[--i]; item2.value = item1.value; item2.offset = item1.offset; } return kResultOk; } // Helper methods. // void setParameterId (Vst::ParamID id) { m_id = id; } void takeFrom (ParamQueue& queue) { m_id = queue.m_id; m_queue = queue.m_queue; m_nsize = queue.m_nsize; m_ncount = queue.m_ncount; queue.m_id = Vst::kNoParamId; queue.m_queue = nullptr; queue.m_nsize = 0; queue.m_ncount = 0; } void clear () { m_ncount = 0; } protected: void resize (int32 nsize) { const int32 nsize2 = (nsize << 1); if (m_nsize != nsize2) { QueueItem *old_queue = m_queue; m_queue = nullptr; m_nsize = nsize2; if (m_nsize > 0) { m_queue = new QueueItem [m_nsize]; if (m_ncount > m_nsize) m_ncount = m_nsize; for (int32 i = 0; old_queue && i < m_ncount; ++i) m_queue[i] = old_queue[i]; } if (old_queue) delete [] old_queue; } } private: // Instance members. struct QueueItem { QueueItem (Vst::ParamValue val = 0.0, int32 offs = 0) : value(val), offset(offs) {} Vst::ParamValue value; int32 offset; }; Vst::ParamID m_id; QueueItem *m_queue; int32 m_nsize; volatile int32 m_ncount; }; IMPLEMENT_FUNKNOWN_METHODS (qtractorVst3Plugin::ParamQueue, Vst::IParamValueQueue, Vst::IParamValueQueue::iid) //---------------------------------------------------------------------- // class qtractorVst3Plugin::ParamChanges -- VST3 plugin parameter changes impl. // class qtractorVst3Plugin::ParamChanges : public Vst::IParameterChanges { public: // Constructor. ParamChanges (int32 nsize = 4) : m_queues(nullptr), m_nsize(0), m_ncount(0) { FUNKNOWN_CTOR resize(nsize); } // Destructor. virtual ~ParamChanges () { resize(0); FUNKNOWN_DTOR } DECLARE_FUNKNOWN_METHODS //--- IParameterChanges ---- // int32 PLUGIN_API getParameterCount () override { return m_ncount; } Vst::IParamValueQueue *PLUGIN_API getParameterData (int32 index) override { if (index >= 0 && index < m_ncount) return &m_queues[index]; else return nullptr; } Vst::IParamValueQueue *PLUGIN_API addParameterData ( const Vst::ParamID& id, int32& index ) override { int32 i = 0; for ( ; i < m_ncount; ++i) { ParamQueue *queue = &m_queues[i]; if (queue->getParameterId() == id) { index = i; return queue; } } if (i >= m_nsize) resize(m_nsize); // warning: non RT-safe! if (i >= m_ncount) ++m_ncount; index = i; ParamQueue *queue = &m_queues[index]; queue->setParameterId(id); return queue; } // Helper methods. // void clear () { for (int32 i = 0; i < m_ncount; ++i) m_queues[i].clear(); m_ncount = 0; } protected: void resize (int32 nsize) { const int32 nsize2 = (nsize << 1); if (m_nsize != nsize2) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::ParamChanges[%p]::resize(%d)", this, nsize); #endif ParamQueue *old_queues = m_queues; m_queues = nullptr; m_nsize = nsize2; if (m_nsize > 0) { m_queues = new ParamQueue [m_nsize]; if (m_ncount > m_nsize) m_ncount = m_nsize; for (int32 i = 0; old_queues && i < m_ncount; ++i) m_queues[i].takeFrom(old_queues[i]); } if (old_queues) delete [] old_queues; } } private: // Instance members. ParamQueue *m_queues; int32 m_nsize; volatile int32 m_ncount; }; IMPLEMENT_FUNKNOWN_METHODS (qtractorVst3Plugin::ParamChanges, Vst::IParameterChanges, Vst::IParameterChanges::iid) //---------------------------------------------------------------------- // class qtractorVst3Plugin::EventList -- VST3 plugin event list impl. // class qtractorVst3Plugin::EventList : public Vst::IEventList { public: // Constructor. EventList (uint32 nsize = 0x100) : m_events(nullptr), m_nsize(0), m_ncount(0) { resize(nsize); FUNKNOWN_CTOR } // Destructor. virtual ~EventList () { resize(0); FUNKNOWN_DTOR } DECLARE_FUNKNOWN_METHODS //--- IEventList --- // int32 PLUGIN_API getEventCount () override { return m_ncount; } tresult PLUGIN_API getEvent (int32 index, Vst::Event& event) override { if (index < 0 || index >= m_nsize) return kInvalidArgument; event = m_events[index]; return kResultOk; } tresult PLUGIN_API addEvent (Vst::Event& event) override { if (m_ncount >= m_nsize) resize(m_nsize); // warning: non RT-safe! m_events[m_ncount++] = event; return kResultOk; } // Helper methods. // void clear () { m_ncount = 0; } protected: void resize (int32 nsize) { const int32 nsize2 = (nsize << 1); if (m_nsize != nsize2) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::EventList[%p]::resize(%d)", this, nsize); #endif Vst::Event *old_events = m_events; m_events = nullptr; m_nsize = nsize2; if (m_nsize > 0) { m_events = new Vst::Event [m_nsize]; if (m_ncount > m_nsize) m_ncount = m_nsize; if (old_events) ::memcpy(&m_events[0], old_events, sizeof(Vst::Event) * m_ncount); if (m_nsize > m_ncount) ::memset(&m_events[m_ncount], 0, sizeof(Vst::Event) * (m_nsize - m_ncount)); } if (old_events) delete [] old_events; } } private: // Instance members. Vst::Event *m_events; int32 m_nsize; volatile int32 m_ncount; }; IMPLEMENT_FUNKNOWN_METHODS (qtractorVst3Plugin::EventList, IEventList, IEventList::iid) //---------------------------------------------------------------------- // class qtractorVst3Plugin::Impl -- VST3 plugin interface impl. // class qtractorVst3Plugin::Impl { public: // Constructor. Impl (qtractorVst3Plugin *pPlugin); // Destructor. ~Impl (); // Do the actual (de)activation. void activate (); void deactivate (); // Editor controller methods. bool openEditor (); void closeEditor (); tresult notify (Vst::IMessage *message); IPlugView *plugView () const { return m_plugView; } // Audio processor methods. bool process_reset (qtractorAudioEngine *pAudioEngine); void process_midi_in (unsigned char *data, unsigned int size, unsigned long offset, unsigned short port); void process (float **ins, float **outs, unsigned int nframes); // Plugin current latency (in frames); unsigned long latency () const; // Set/add a parameter value/point. void setParameter ( Vst::ParamID id, Vst::ParamValue value, uint32 offset); // Total parameter count. int32 parameterCount () const; // Get current parameter value. Vst::ParamValue getParameter (Vst::ParamID id) const; // Parameter info accessors. bool getParameterInfo (int32 index, Vst::ParameterInfo& paramInfo) const; // Program names list accessor. const QList& programs () const { return m_programs; } // Program-change selector. void selectProgram (int iBank, int iProg); // Plugin preset/state snapshot accessors. bool setState (const QByteArray& data); bool getState (QByteArray& data); // MIDI event buffer accessors. EventList& events_in () { return m_events_in; } EventList& events_out () { return m_events_out; } protected: // Plugin module (de)initializer. void initialize (); void deinitialize (); // Cleanup. void clear (); // Channel/bus (de)activation helper method. void activate (Vst::IComponent *component, Vst::MediaType type, Vst::BusDirection direction, bool state); private: // Instance variables. qtractorVst3Plugin *m_pPlugin; IPtr m_handler; IPtr m_plugView; Vst::IAudioProcessor *m_processor; volatile bool m_processing; ParamChanges m_params_in; // ParamChanges m_params_out; EventList m_events_in; EventList m_events_out; // Processor buffers. Vst::AudioBusBuffers m_buffers_in; Vst::AudioBusBuffers m_buffers_out; // Processor data. Vst::ProcessData m_process_data; // Program-change parameter info. Vst::ParameterInfo m_programParamInfo; // Program name list. QList m_programs; // MIDI controller assignment hash key/map. struct MidiMapKey { MidiMapKey (int16 po = 0, int16 ch = 0, int16 co = 0) : port(po), channel(ch), controller(co) {} MidiMapKey (const MidiMapKey& key) : port(key.port), channel(key.channel), controller(key.controller) {} bool operator< (const MidiMapKey& key) const { if (port != key.port) return (port < key.port); else if (channel != key.channel) return (channel < key.channel); else return (controller < key.controller); } int16 port; int16 channel; int16 controller; }; QMap m_midiMap; }; //---------------------------------------------------------------------- // class qtractorVst3Plugin::Handler -- VST3 plugin interface handler. // class qtractorVst3Plugin::Handler : public Vst::IComponentHandler , public Vst::IConnectionPoint { public: // Constructor. Handler (qtractorVst3Plugin *pPlugin) : m_pPlugin(pPlugin), m_restarting(false) { FUNKNOWN_CTOR } // Destructor. virtual ~Handler () { FUNKNOWN_DTOR } DECLARE_FUNKNOWN_METHODS //--- IComponentHandler --- // tresult PLUGIN_API beginEdit (Vst::ParamID /*id*/) override { #ifdef CONFIG_DEBUG_0 qDebug("qtractorVst3Plugin::Handler[%p]::beginEdit(%d)", this, int(id)); #endif return kResultOk; } tresult PLUGIN_API performEdit (Vst::ParamID id, Vst::ParamValue value) override { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::Handler[%p]::performEdit(%d, %g)", this, int(id), float(value)); #endif m_pPlugin->impl()->setParameter(id, value, 0); qtractorPlugin::Param *pParam = m_pPlugin->findParamId(int(id)); if (pParam) { pParam->setValueEnabled(true); pParam->updateValue(float(value), false); } return kResultOk; } tresult PLUGIN_API endEdit (Vst::ParamID /*id*/) override { #ifdef CONFIG_DEBUG_0 qDebug("qtractorVst3Plugin::Handler[%p]::endEdit(%d)", this, int(id)); #endif return kResultOk; } tresult PLUGIN_API restartComponent (int32 flags) override { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::Handler[%p]::restartComponent(0x%08x)", this, flags); #endif if (!m_restarting) { m_restarting = true; if (flags & Vst::kReloadComponent) m_pPlugin->impl()->deactivate(); m_pPlugin->resetParamValues(true); QByteArray data; if (m_pPlugin->impl()->getState(data)) m_pPlugin->impl()->setState(data); m_pPlugin->updateParamValues(true); m_pPlugin->resetParamValues(false); if (flags & Vst::kReloadComponent) m_pPlugin->impl()->activate(); m_restarting = false; } return kResultOk; } //--- IConnectionPoint --- // tresult PLUGIN_API connect (Vst::IConnectionPoint *other) override { return (other ? kResultOk : kInvalidArgument); } tresult PLUGIN_API disconnect (Vst::IConnectionPoint *other) override { return (other ? kResultOk : kInvalidArgument); } tresult PLUGIN_API notify (Vst::IMessage *message) override { return m_pPlugin->impl()->notify(message); } private: // Instance client. qtractorVst3Plugin *m_pPlugin; bool m_restarting; }; tresult PLUGIN_API qtractorVst3Plugin::Handler::queryInterface ( const char *_iid, void **obj ) { QUERY_INTERFACE(_iid, obj, FUnknown::iid, IComponentHandler) QUERY_INTERFACE(_iid, obj, IComponentHandler::iid, IComponentHandler) QUERY_INTERFACE(_iid, obj, IConnectionPoint::iid, IConnectionPoint) *obj = nullptr; return kNoInterface; } uint32 PLUGIN_API qtractorVst3Plugin::Handler::addRef (void) { return 1000; } uint32 PLUGIN_API qtractorVst3Plugin::Handler::release (void) { return 1000; } //---------------------------------------------------------------------- // class qtractorVst3Plugin::RunLoop -- VST3 plugin editor run-loop impl. // class qtractorVst3Plugin::RunLoop : public IRunLoop { public: //--- IRunLoop --- // tresult PLUGIN_API registerEventHandler (IEventHandler *handler, FileDescriptor fd) override { return g_hostContext.registerEventHandler(handler, fd); } tresult PLUGIN_API unregisterEventHandler (IEventHandler *handler) override { return g_hostContext.unregisterEventHandler(handler); } tresult PLUGIN_API registerTimer (ITimerHandler *handler, TimerInterval msecs) override { return g_hostContext.registerTimer(handler, msecs); } tresult PLUGIN_API unregisterTimer (ITimerHandler *handler) override { return g_hostContext.unregisterTimer(handler); } tresult PLUGIN_API queryInterface (const TUID _iid, void **obj) override { if (FUnknownPrivate::iidEqual(_iid, FUnknown::iid) || FUnknownPrivate::iidEqual(_iid, IRunLoop::iid)) { addRef(); *obj = this; return kResultOk; } *obj = nullptr; return kNoInterface; } uint32 PLUGIN_API addRef () override { return 1001; } uint32 PLUGIN_API release () override { return 1001; } }; //---------------------------------------------------------------------- // class qtractorVst3Plugin::EditorFrame -- VST3 plugin editor frame interface impl. // class qtractorVst3Plugin::EditorFrame : public IPlugFrame { public: // Constructor. EditorFrame (IPlugView *plugView, QWidget *widget) : m_plugView(plugView), m_widget(widget), m_runLoop(nullptr), m_resizing(false) { m_runLoop = owned(NEW RunLoop()); m_plugView->setFrame(this); ViewRect rect; if (m_plugView->getSize(&rect) == kResultOk) { m_resizing = true; const QSize size( rect.right - rect.left, rect.bottom - rect.top); m_widget->resize(size); m_resizing = false; } } // Destructor. virtual ~EditorFrame () { m_plugView->setFrame(nullptr); m_runLoop = nullptr; } // Accessors. IPlugView *plugView () const { return m_plugView; } RunLoop *runLoop () const { return m_runLoop; } //--- IPlugFrame --- // tresult PLUGIN_API resizeView (IPlugView *plugView, ViewRect *rect) override { if (!rect || !plugView || plugView != m_plugView) return kInvalidArgument; if (!m_widget) return kInternalError; if (m_resizing) return kResultFalse; m_resizing = true; const QSize size( rect->right - rect->left, rect->bottom - rect->top); #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::EditorFrame[%p]::resizeView(%p, %p) size=(%d, %d)", this, plugView, rect, size.width(), size.height()); #endif if (m_plugView->canResize() == kResultOk) m_widget->resize(size); else m_widget->setFixedSize(size); m_resizing = false; ViewRect rect0; if (m_plugView->getSize(&rect0) != kResultOk) return kInternalError; const QSize size0( rect0.right - rect0.left, rect0.bottom - rect0.top); if (size != size0) m_plugView->onSize(&rect0); return kResultOk; } tresult PLUGIN_API queryInterface (const TUID _iid, void **obj) override { if (FUnknownPrivate::iidEqual(_iid, FUnknown::iid) || FUnknownPrivate::iidEqual(_iid, IPlugFrame::iid)) { addRef(); *obj = this; return kResultOk; } return m_runLoop->queryInterface(_iid, obj); } uint32 PLUGIN_API addRef () override { return 1002; } uint32 PLUGIN_API release () override { return 1002; } private: // Instance members. IPlugView *m_plugView; QWidget *m_widget; IPtr m_runLoop; bool m_resizing; }; //------------------------------------------------------------------------ // qtractorVst3Plugin::Stream - Memory based stream for IBStream impl. class qtractorVst3Plugin::Stream : public IBStream { public: // Constructors. Stream () : m_pos(0) { FUNKNOWN_CTOR } Stream (const QByteArray& data) : m_data(data), m_pos(0) { FUNKNOWN_CTOR } // Destructor. virtual ~Stream () { FUNKNOWN_DTOR } DECLARE_FUNKNOWN_METHODS //--- IBStream --- // tresult PLUGIN_API read (void *buffer, int32 nbytes, int32 *nread) override { if (m_pos + nbytes > m_data.size()) { const int32 nsize = int32(m_data.size() - m_pos); if (nsize > 0) { nbytes = nsize; } else { nbytes = 0; m_pos = int64(m_data.size()); } } if (nbytes > 0) { ::memcpy(buffer, m_data.data() + m_pos, nbytes); m_pos += nbytes; } if (nread) *nread = nbytes; return kResultOk; } tresult PLUGIN_API write (void *buffer, int32 nbytes, int32 *nwrite) override { if (buffer == nullptr) return kInvalidArgument; const int32 nsize = m_pos + nbytes; if (nsize > m_data.size()) m_data.resize(nsize); if (m_pos >= 0 && nbytes > 0) { ::memcpy(m_data.data() + m_pos, buffer, nbytes); m_pos += nbytes; } else nbytes = 0; if (nwrite) *nwrite = nbytes; return kResultOk; } tresult PLUGIN_API seek (int64 pos, int32 mode, int64 *npos) override { if (mode == kIBSeekSet) m_pos = pos; else if (mode == kIBSeekCur) m_pos += pos; else if (mode == kIBSeekEnd) m_pos = m_data.size() - pos; if (m_pos < 0) m_pos = 0; else if (m_pos > m_data.size()) m_pos = m_data.size(); if (npos) *npos = m_pos; return kResultTrue; } tresult PLUGIN_API tell (int64 *npos) override { if (npos) { *npos = m_pos; return kResultOk; } else { return kInvalidArgument; } } // Other accessors. // const QByteArray& data() const { return m_data; } protected: // Instance members. QByteArray m_data; int64 m_pos; }; IMPLEMENT_FUNKNOWN_METHODS (qtractorVst3Plugin::Stream, IBStream, IBStream::iid) //---------------------------------------------------------------------- // class qtractorVst3Plugin::Impl -- VST3 plugin interface impl. // // Constructor. qtractorVst3Plugin::Impl::Impl ( qtractorVst3Plugin *pPlugin ) : m_pPlugin(pPlugin), m_handler(nullptr), m_plugView(nullptr), m_processor(nullptr), m_processing(false) { initialize(); } // Destructor. qtractorVst3Plugin::Impl::~Impl (void) { deinitialize(); } // Plugin module initializer. void qtractorVst3Plugin::Impl::initialize (void) { clear(); qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return; #if 0//HACK: Plugin-type might be already open via plugin-factory... if (!pType->open()) return; #endif Vst::IComponent *component = pType->impl()->component(); if (!component) return; Vst::IEditController *controller = pType->impl()->controller(); if (controller) { m_handler = owned(NEW Handler(m_pPlugin)); controller->setComponentHandler(m_handler); } m_processor = FUnknownPtr (component); if (controller) { const int32 nparams = controller->getParameterCount(); for (int32 i = 0; i < nparams; ++i) { Vst::ParameterInfo paramInfo; ::memset(¶mInfo, 0, sizeof(Vst::ParameterInfo)); if (controller->getParameterInfo(i, paramInfo) == kResultOk) { if (m_programParamInfo.unitId != Vst::UnitID(-1)) continue; if (paramInfo.flags & Vst::ParameterInfo::kIsProgramChange) m_programParamInfo = paramInfo; } } if (m_programParamInfo.unitId != Vst::UnitID(-1)) { Vst::IUnitInfo *unitInfos = pType->impl()->unitInfos(); if (unitInfos) { const int32 nunits = unitInfos->getUnitCount(); for (int32 i = 0; i < nunits; ++i) { Vst::UnitInfo unitInfo; if (unitInfos->getUnitInfo(i, unitInfo) != kResultOk) continue; if (unitInfo.id != m_programParamInfo.unitId) continue; const int32 nlists = unitInfos->getProgramListCount(); for (int32 j = 0; j < nlists; ++j) { Vst::ProgramListInfo programListInfo; if (unitInfos->getProgramListInfo(j, programListInfo) != kResultOk) continue; if (programListInfo.id != unitInfo.programListId) continue; const int32 nprograms = programListInfo.programCount; for (int32 k = 0; k < nprograms; ++k) { Vst::String128 name; if (unitInfos->getProgramName( programListInfo.id, k, name) == kResultOk) m_programs.append(fromTChar(name)); } break; } } } } if (m_programs.isEmpty() && m_programParamInfo.stepCount > 0) { const int32 nprograms = m_programParamInfo.stepCount + 1; for (int32 k = 0; k < nprograms; ++k) { const Vst::ParamValue value = Vst::ParamValue(k) / Vst::ParamValue(m_programParamInfo.stepCount); Vst::String128 name; if (controller->getParamStringByValue( m_programParamInfo.id, value, name) == kResultOk) m_programs.append(fromTChar(name)); } } } if (controller) { const int32 nports = pType->midiIns(); FUnknownPtr midiMapping(controller); if (midiMapping && nports > 0) { for (int16 i = 0; i < Vst::kCountCtrlNumber; ++i) { // controllers... for (int32 j = 0; j < nports; ++j) { // ports... for (int16 k = 0; k < 16; ++k) { // channels... Vst::ParamID id = Vst::kNoParamId; if (midiMapping->getMidiControllerAssignment( j, k, Vst::CtrlNumber(i), id) == kResultOk) { m_midiMap.insert(MidiMapKey(j, k, i), id); } } } } } } activate(component, Vst::kEvent, Vst::kOutput, true); activate(component, Vst::kEvent, Vst::kInput, true); activate(component, Vst::kAudio, Vst::kOutput, true); activate(component, Vst::kAudio, Vst::kInput, true); component->setActive(true); } // Plugin module (de)initializer. void qtractorVst3Plugin::Impl::deinitialize (void) { closeEditor(); deactivate(); qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType) { Vst::IComponent *component = pType->impl()->component(); if (component) { component->setActive(false); activate(component, Vst::kEvent, Vst::kOutput, false); activate(component, Vst::kEvent, Vst::kInput, false); activate(component, Vst::kAudio, Vst::kOutput, false); activate(component, Vst::kAudio, Vst::kInput, false); } Vst::IEditController *controller = pType->impl()->controller(); if (controller) controller->setComponentHandler(nullptr); } m_processor = nullptr; m_handler = nullptr; clear(); } // Do the actual (de)activation. void qtractorVst3Plugin::Impl::activate (void) { if (!m_processing && m_processor) { m_processor->setProcessing(true); g_hostContext.processAddRef(); m_processing = true; } qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType) { Vst::IComponent *component = pType->impl()->component(); if (component) component->setActive(true); } #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::Impl[%p]::activate() processing=%d", this, int(m_processing)); #endif } void qtractorVst3Plugin::Impl::deactivate (void) { qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType) { Vst::IComponent *component = pType->impl()->component(); if (component) component->setActive(false); } if (m_processing && m_processor) { g_hostContext.processReleaseRef(); m_processor->setProcessing(false); m_processing = false; } #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::Impl[%p]::deactivate() processing=%d", this, int(m_processing)); #endif } // Editor controller methods. bool qtractorVst3Plugin::Impl::openEditor (void) { closeEditor(); qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return false; #ifdef CONFIG_VST3_XCB g_hostContext.openXcbConnection(); #endif g_hostContext.startTimer(200); Vst::IEditController *controller = pType->impl()->controller(); if (controller) m_plugView = owned(controller->createView(Vst::ViewType::kEditor)); return (m_plugView != nullptr); } void qtractorVst3Plugin::Impl::closeEditor (void) { m_plugView = nullptr; g_hostContext.stopTimer(); #ifdef CONFIG_VST3_XCB g_hostContext.closeXcbConnection(); #endif } tresult qtractorVst3Plugin::Impl::notify ( Vst::IMessage *message ) { qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return kInternalError; #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::Impl[%p]::notify(%p)", this, message); #endif Vst::IComponent *component = pType->impl()->component(); FUnknownPtr component_cp(component); if (component_cp) component_cp->notify(message); Vst::IEditController *controller = pType->impl()->controller(); FUnknownPtr controller_cp(controller); if (controller_cp) controller_cp->notify(message); return kResultOk; } // Audio processor stuff... // bool qtractorVst3Plugin::Impl::process_reset ( qtractorAudioEngine *pAudioEngine ) { qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return false; if (!m_processor) return false; // deactivate(); // Initialize running state... m_params_in.clear(); // m_params_out.clear(); m_events_in.clear(); m_events_out.clear(); Vst::ProcessSetup setup; const bool bFreewheel = pAudioEngine->isFreewheel(); setup.processMode = (bFreewheel ? Vst::kOffline :Vst::kRealtime); setup.symbolicSampleSize = Vst::kSample32; setup.maxSamplesPerBlock = pAudioEngine->bufferSizeEx(); setup.sampleRate = float(pAudioEngine->sampleRate()); if (m_processor->setupProcessing(setup) != kResultOk) return false; // Setup processor audio I/O buffers... m_buffers_in.silenceFlags = 0; m_buffers_in.numChannels = pType->audioIns(); m_buffers_in.channelBuffers32 = nullptr; m_buffers_out.silenceFlags = 0; m_buffers_out.numChannels = pType->audioOuts(); m_buffers_out.channelBuffers32 = nullptr; // Setup processor data struct... m_process_data.numSamples = pAudioEngine->blockSize(); m_process_data.symbolicSampleSize = Vst::kSample32; if (pType->audioIns() > 0) { m_process_data.numInputs = 1; m_process_data.inputs = &m_buffers_in; } else { m_process_data.numInputs = 0; m_process_data.inputs = nullptr; } if (pType->audioOuts() > 0) { m_process_data.numOutputs = 1; m_process_data.outputs = &m_buffers_out; } else { m_process_data.numOutputs = 0; m_process_data.outputs = nullptr; } m_process_data.processContext = g_hostContext.processContext(); m_process_data.inputEvents = &m_events_in; m_process_data.outputEvents = &m_events_out; m_process_data.inputParameterChanges = &m_params_in; m_process_data.outputParameterChanges = nullptr; //&m_params_out; // activate(); return true; } void qtractorVst3Plugin::Impl::process_midi_in ( unsigned char *data, unsigned int size, unsigned long offset, unsigned short port ) { for (uint32_t i = 0; i < size; ++i) { // channel status const int channel = (data[i] & 0x0f);// + 1; const int status = (data[i] & 0xf0); // all system common/real-time ignored if (status == 0xf0) continue; // check data size (#1) if (++i >= size) break; // channel key const int key = (data[i] & 0x7f); // program change if (status == 0xc0) { // TODO: program-change... continue; } // after-touch if (status == 0xd0) { const MidiMapKey mkey(port, channel, Vst::kAfterTouch); const Vst::ParamID id = m_midiMap.value(mkey, Vst::kNoParamId); if (id != Vst::kNoParamId) { const float pre = float(key) / 127.0f; setParameter(id, Vst::ParamValue(pre), offset); } continue; } // check data size (#2) if (++i >= size) break; // channel value (normalized) const int value = (data[i] & 0x7f); Vst::Event event; ::memset(&event, 0, sizeof(Vst::Event)); event.busIndex = port; event.sampleOffset = offset; event.flags = Vst::Event::kIsLive; // note on if (status == 0x90) { event.type = Vst::Event::kNoteOnEvent; event.noteOn.noteId = -1; event.noteOn.channel = channel; event.noteOn.pitch = key; event.noteOn.velocity = float(value) / 127.0f; m_events_in.addEvent(event); } // note off else if (status == 0x80) { event.type = Vst::Event::kNoteOffEvent; event.noteOff.noteId = -1; event.noteOff.channel = channel; event.noteOff.pitch = key; event.noteOff.velocity = float(value) / 127.0f; m_events_in.addEvent(event); } // key pressure/poly.aftertouch else if (status == 0xa0) { event.type = Vst::Event::kPolyPressureEvent; event.polyPressure.channel = channel; event.polyPressure.pitch = key; event.polyPressure.pressure = float(value) / 127.0f; m_events_in.addEvent(event); } // control-change else if (status == 0xb0) { const MidiMapKey mkey(port, channel, key); const Vst::ParamID id = m_midiMap.value(mkey, Vst::kNoParamId); if (id != Vst::kNoParamId) { const float val = float(value) / 127.0f; setParameter(id, Vst::ParamValue(val), offset); } } // pitch-bend else if (status == 0xe0) { const MidiMapKey mkey(port, channel, Vst::kPitchBend); const Vst::ParamID id = m_midiMap.value(mkey, Vst::kNoParamId); if (id != Vst::kNoParamId) { const float pitchbend = float(key + (value << 7)) / float(0x3fff); setParameter(id, Vst::ParamValue(pitchbend), offset); } } } } void qtractorVst3Plugin::Impl::process ( float **ins, float **outs, unsigned int nframes ) { if (!m_processor) return; if (!m_processing) return; qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return; // m_params_out.clear(); m_events_out.clear(); m_buffers_in.channelBuffers32 = ins; m_buffers_out.channelBuffers32 = outs; m_process_data.numSamples = nframes; if (m_processor->process(m_process_data) != kResultOk) { qWarning("qtractorVst3Plugin::Impl[%p]::process() FAILED!", this); } m_events_in.clear(); m_params_in.clear(); } // Plugin current latency (in frames); unsigned long qtractorVst3Plugin::Impl::latency (void) const { if (m_processor) return m_processor->getLatencySamples(); else return 0; } // Set/add a parameter value/point. void qtractorVst3Plugin::Impl::setParameter ( Vst::ParamID id, Vst::ParamValue value, uint32 offset ) { int32 index = 0; Vst::IParamValueQueue *queue = m_params_in.addParameterData(id, index); if (queue && (queue->addPoint(offset, value, index) != kResultOk)) { qWarning("qtractorVst3Plugin::Impl[%p]::setParameter(%u, %g, %u) FAILED!", this, id, value, offset); } } // Get current parameter value. Vst::ParamValue qtractorVst3Plugin::Impl::getParameter ( Vst::ParamID id ) const { qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return 0.0; Vst::IEditController *controller = pType->impl()->controller(); if (controller) return controller->getParamNormalized(id); else return 0.0; } // Total parameter count. int32 qtractorVst3Plugin::Impl::parameterCount (void) const { qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return 0; Vst::IEditController *controller = pType->impl()->controller(); if (controller) return controller->getParameterCount(); else return 0; } // Parameter info accessor bool qtractorVst3Plugin::Impl::getParameterInfo ( int32 index, Vst::ParameterInfo& paramInfo ) const { qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return false; Vst::IEditController *controller = pType->impl()->controller(); if (controller) return (controller->getParameterInfo(index, paramInfo) == kResultOk); else return false; } // Program-change selector. void qtractorVst3Plugin::Impl::selectProgram ( int iBank, int iProg ) { if (iBank < 0 || iProg < 0) return; const Vst::ParamID id = m_programParamInfo.id; if (id == Vst::kNoParamId) return; if (m_programParamInfo.stepCount < 1) return; qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return; Vst::IEditController *controller = pType->impl()->controller(); if (!controller) return; #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::Impl[%p]::selectProgram(%d, %d)", this, iBank, iProg); #endif int iIndex = 0; if (iBank >= 0 && iProg >= 0) iIndex = (iBank << 7) + iProg; const Vst::ParamValue value = controller->plainParamToNormalized(id, float(iIndex)); // = Vst::ParamValue(iIndex) // / Vst::ParamValue(m_programParamInfo.stepCount); setParameter(id, value, 0); controller->setParamNormalized(id, value); // HACK: Make sure all displayed parameter values are in sync. m_pPlugin->resetParamValues(false); } // Plugin preset/state snapshot accessors. bool qtractorVst3Plugin::Impl::setState ( const QByteArray& data ) { qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return false; Vst::IComponent *component = pType->impl()->component(); if (!component) return false; Vst::IEditController *controller = pType->impl()->controller(); if (!controller) return false; Stream state(data); if (component->setState(&state) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::Impl[%p]::setState()" " IComponent::setState() FAILED!", this); #endif return false; } if (controller->setComponentState(&state) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::Impl[%p]::setState()" " IEditController::setComponentState() FAILED!", this); #endif return false; } return true; } bool qtractorVst3Plugin::Impl::getState ( QByteArray& data ) { qtractorVst3PluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return false; Vst::IComponent *component = pType->impl()->component(); if (!component) return false; Stream state; if (component->getState(&state) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::Impl[%p]::getState()" " Vst::IComponent::getState() FAILED!", this); #endif return false; } data = state.data(); return true; } // Cleanup. void qtractorVst3Plugin::Impl::clear (void) { ::memset(&m_programParamInfo, 0, sizeof(Vst::ParameterInfo)); m_programParamInfo.id = Vst::kNoParamId; m_programParamInfo.unitId = Vst::UnitID(-1); m_programs.clear(); m_midiMap.clear(); } void qtractorVst3Plugin::Impl::activate ( Vst::IComponent *component, Vst::MediaType type, Vst::BusDirection direction, bool state ) { const int32 nbuses = component->getBusCount(type, direction); for (int32 i = 0; i < nbuses; ++i) { Vst::BusInfo busInfo; if (component->getBusInfo(type, direction, i, busInfo) == kResultOk) { if (busInfo.flags & Vst::BusInfo::kDefaultActive) { component->activateBus(type, direction, i, state); } } } } //---------------------------------------------------------------------- // class qtractorVst3Plugin::EditorWidget -- VST3 plugin editor widget decl. // class qtractorVst3Plugin::EditorWidget : public QWidget { public: EditorWidget(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()); ~EditorWidget (); void setPlugin (qtractorVst3Plugin *pPlugin) { m_pPlugin = pPlugin; } qtractorVst3Plugin *plugin () const { return m_pPlugin; } WId parentWinId() const { return QWidget::winId(); } protected: void resizeEvent(QResizeEvent *pResizeEvent); void showEvent(QShowEvent *pShowEvent); void closeEvent(QCloseEvent *pCloseEvent); private: qtractorVst3Plugin *m_pPlugin; bool m_resizing; }; //---------------------------------------------------------------------- // class qtractorVst3Plugin::EditorWidget -- VST3 plugin editor widget impl. // qtractorVst3Plugin::EditorWidget::EditorWidget ( QWidget *pParent, Qt::WindowFlags wflags ) : QWidget(pParent, wflags), m_pPlugin(nullptr), m_resizing(false) { } qtractorVst3Plugin::EditorWidget::~EditorWidget (void) { } void qtractorVst3Plugin::EditorWidget::resizeEvent ( QResizeEvent *pResizeEvent ) { if (m_resizing) return; IPlugView *plugView = nullptr; EditorFrame *pEditorFrame = nullptr; if (m_pPlugin) pEditorFrame = m_pPlugin->editorFrame(); if (pEditorFrame) plugView = pEditorFrame->plugView(); if (plugView && plugView->canResize() == kResultOk) { const QSize& size = pResizeEvent->size(); #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::EditorWidget[%p]::resizeEvent(%d, %d)", this, size.width(), size.height()); #endif ViewRect rect; rect.left = 0; rect.top = 0; rect.right = size.width(); rect.bottom = size.height(); if (plugView->checkSizeConstraint(&rect) != kResultOk) plugView->getSize(&rect); const QSize size2( rect.right - rect.left, rect.bottom - rect.top); if (size2 != size) { m_resizing = true; QWidget::resize(size2); m_resizing = false; plugView->onSize(&rect); } } } void qtractorVst3Plugin::EditorWidget::showEvent ( QShowEvent *pShowEvent ) { QWidget::showEvent(pShowEvent); if (m_pPlugin) m_pPlugin->toggleFormEditor(true); } void qtractorVst3Plugin::EditorWidget::closeEvent ( QCloseEvent *pCloseEvent ) { if (m_pPlugin) m_pPlugin->toggleFormEditor(false); QWidget::closeEvent(pCloseEvent); if (m_pPlugin) m_pPlugin->closeEditor(); } //---------------------------------------------------------------------------- // qtractorVst3Plugin::Param::Impl -- VST3 plugin parameter interface impl. // class qtractorVst3Plugin::Param::Impl { public: // Constructor. Impl(const Vst::ParameterInfo& paramInfo) : m_paramInfo(paramInfo) {} // Accessors. const Vst::ParameterInfo& paramInfo() const { return m_paramInfo; } private: // Instance members. Vst::ParameterInfo m_paramInfo; }; //---------------------------------------------------------------------- // class qtractorVst3Plugin -- VST3 plugin interface impl. // // Buffer size large enough to hold a regular MIDI channel event. const long c_iMaxMidiData = 4; // Constructor. qtractorVst3Plugin::qtractorVst3Plugin ( qtractorPluginList *pList, qtractorVst3PluginType *pType ) : qtractorPlugin(pList, pType), m_pImpl(new Impl(this)), m_pEditorFrame(nullptr), m_pEditorWidget(nullptr), m_ppIBuffer(nullptr), m_ppOBuffer(nullptr), m_pfIDummy(nullptr), m_pfODummy(nullptr), m_pMidiParser(nullptr) { initialize(); } // Destructor. qtractorVst3Plugin::~qtractorVst3Plugin (void) { deinitialize(); delete m_pImpl; } // Plugin instance initializer. void qtractorVst3Plugin::initialize (void) { // Allocate I/O audio buffer pointers. const unsigned short iAudioIns = audioIns(); const unsigned short iAudioOuts = audioOuts(); if (iAudioIns > 0) m_ppIBuffer = new float * [iAudioIns]; if (iAudioOuts > 0) m_ppOBuffer = new float * [iAudioOuts]; const int32 nparams = m_pImpl->parameterCount(); #if CONFIG_DEBUG qDebug(" --- Parameters (nparams = %d) ---", nparams); #endif for (int32 i = 0; i < nparams; ++i) { Vst::ParameterInfo paramInfo; ::memset(¶mInfo, 0, sizeof(Vst::ParameterInfo)); if (m_pImpl->getParameterInfo(i, paramInfo)) { if ( (paramInfo.flags & Vst::ParameterInfo::kCanAutomate) && !(paramInfo.flags & Vst::ParameterInfo::kIsReadOnly)) { Param *pParam = new Param(this, i); m_paramIds.insert(int(paramInfo.id), pParam); addParam(pParam); } } } if (midiIns() > 0 && snd_midi_event_new(c_iMaxMidiData, &m_pMidiParser) == 0) snd_midi_event_no_status(m_pMidiParser, 1); freezeConfigs(); // Instantiate each instance properly... setChannels(channels()); } // Plugin instance de-initializer. void qtractorVst3Plugin::deinitialize (void) { // Cleanup all plugin instances... cleanup(); // setChannels(0); // Deallocate I/O audio buffer pointers. if (m_ppIBuffer) delete [] m_ppIBuffer; if (m_ppOBuffer) delete [] m_ppOBuffer; if (m_pfIDummy) delete [] m_pfIDummy; if (m_pfODummy) delete [] m_pfODummy; // Deallocate MIDI decoder. if (m_pMidiParser) snd_midi_event_free(m_pMidiParser); } // Channel/instance number accessors. void qtractorVst3Plugin::setChannels ( unsigned short iChannels ) { // Check our type... qtractorVst3PluginType *pType = static_cast (type()); if (pType == nullptr) return; // Estimate the (new) number of instances... const unsigned short iOldInstances = instances(); unsigned short iInstances = 0; if (iChannels > 0) { iInstances = pType->instances(iChannels, list()->isMidi()); // Now see if instance and channel count changed anyhow... if (iInstances == iOldInstances && iChannels == channels()) return; } // Gotta go for a while... const bool bActivated = isActivated(); setChannelsActivated(iChannels, false); // Set new instance number... setInstances(iInstances); // Bail out, if none are about to be created... if (iInstances < 1) { setChannelsActivated(iChannels, bActivated); return; } #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin[%p]::setChannels(%u) instances=%u", this, iChannels, iInstances); #endif qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; const unsigned int iBufferSizeEx = pAudioEngine->bufferSizeEx(); // Allocate the dummy audio I/O buffers... const unsigned short iAudioIns = audioIns(); const unsigned short iAudioOuts = audioOuts(); if (iChannels < iAudioIns) { if (m_pfIDummy) delete [] m_pfIDummy; m_pfIDummy = new float [iBufferSizeEx]; ::memset(m_pfIDummy, 0, iBufferSizeEx * sizeof(float)); } if (iChannels < iAudioOuts) { if (m_pfODummy) delete [] m_pfODummy; m_pfODummy = new float [iBufferSizeEx]; // ::memset(m_pfODummy, 0, iBufferSizeEx * sizeof(float)); } if (m_pMidiParser) snd_midi_event_reset_decode(m_pMidiParser); // Setup all those instances alright... m_pImpl->process_reset(pAudioEngine); // (Re)issue all configuration as needed... realizeConfigs(); realizeValues(); // But won't need it anymore. releaseConfigs(); releaseValues(); // (Re)activate instance if necessary... setChannelsActivated(iChannels, bActivated); } // Do the actual (de)activation. void qtractorVst3Plugin::activate (void) { m_pImpl->activate(); } void qtractorVst3Plugin::deactivate (void) { m_pImpl->deactivate(); } // Parameter update method. void qtractorVst3Plugin::updateParam ( qtractorPlugin::Param *pParam, float fValue, bool bUpdate ) { qtractorVst3PluginType *pType = static_cast (type()); if (pType == nullptr) return; Vst::IEditController *controller = pType->impl()->controller(); if (!controller) return; Param *pVst3Param = static_cast (pParam); if (pVst3Param == nullptr) return; if (pVst3Param->impl() == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorVst3Plugin[%p]::updateParam(%lu, %g, %d)", this, pParam->index(), fValue, int(bUpdate)); #endif const Vst::ParamID id = pVst3Param->impl()->paramInfo().id; const Vst::ParamValue value = Vst::ParamValue(fValue); m_pImpl->setParameter(id, value, 0); controller->setParamNormalized(id, value); pVst3Param->setValueEnabled(true); } // All parameters update method. void qtractorVst3Plugin::updateParamValues ( bool bUpdate ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorVst3Plugin[%p]::updateParamValues(%d)", this, int(bUpdate)); #endif int nupdate = 0; // Make sure all cached parameter values are in sync // with plugin parameter values; update cache otherwise. const qtractorPlugin::Params& params = qtractorPlugin::params(); qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator param_end = params.constEnd(); for ( ; param != param_end; ++param) { Param *pParam = static_cast (param.value()); if (pParam && pParam->impl() && pParam->isValueEnabled()) { const Vst::ParamID id = pParam->impl()->paramInfo().id; const float fValue = float(m_pImpl->getParameter(id)); if (pParam->value() != fValue) { pParam->setValue(fValue, bUpdate); ++nupdate; } } } if (nupdate > 0) updateFormDirtyCount(); } // Parameters enablement method. void qtractorVst3Plugin::resetParamValues ( bool bEnabled ) { const qtractorPlugin::Params& params = qtractorPlugin::params(); qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator param_end = params.constEnd(); for ( ; param != param_end; ++param) { Param *pParam = static_cast (param.value()); if (pParam) pParam->setValueEnabled(bEnabled); } updateFormDirtyCount(); } // Parameter finder (by id). qtractorPlugin::Param *qtractorVst3Plugin::findParamId ( int id ) const { return m_paramIds.value(id, nullptr); } // Configuration state stuff. void qtractorVst3Plugin::configure ( const QString& sKey, const QString& sValue ) { if (sKey == "state") { // Load the BLOB (base64 encoded)... const QByteArray data = qUncompress(QByteArray::fromBase64(sValue.toLatin1())); #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin[%p]::configure() data.size=%d", this, int(data.size())); #endif m_pImpl->setState(data); // HACK: Make sure all parameter values are in sync. resetParamValues(false); } } // Plugin configuration/state snapshot. void qtractorVst3Plugin::freezeConfigs (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorVst3Plugin[%p]::freezeConfigs()", this); #endif // HACK: Make sure all parameter values are in sync, // provided freezeConfigs() are always called when // saving plugin's state and before parameter values. updateParamValues(false); // Update current editor position... if (m_pEditorWidget && m_pEditorWidget->isVisible()) setEditorPos(m_pEditorWidget->pos()); if (!type()->isConfigure()) return; clearConfigs(); QByteArray data; if (!m_pImpl->getState(data)) return; #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin[%p]::freezeConfigs() data.size=%d", this, int(data.size())); #endif // Set special plugin configuration item (base64 encoded)... QByteArray cdata = qCompress(data).toBase64(); for (int i = cdata.size() - (cdata.size() % 72); i >= 0; i -= 72) cdata.insert(i, "\n "); // Indentation. setConfig("state", cdata.constData()); } void qtractorVst3Plugin::releaseConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin[%p]::releaseConfigs()", this); #endif qtractorPlugin::clearConfigs(); } // Open/close editor widget. void qtractorVst3Plugin::openEditor ( QWidget *pParent ) { qtractorVst3PluginType *pType = static_cast (type()); if (pType == nullptr) return; if (!pType->isEditor()) return; // Is it already there? if (m_pEditorWidget) { if (!m_pEditorWidget->isVisible()) { moveWidgetPos(m_pEditorWidget, editorPos()); m_pEditorWidget->show(); } m_pEditorWidget->raise(); m_pEditorWidget->activateWindow(); return; } // Tell the world we'll (maybe) take some time... qtractorPluginList::WaitCursor waiting; #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin[%p]::openEditor(%p)", this, pParent); #endif if (!m_pImpl->openEditor()) return; IPlugView *plugView = m_pImpl->plugView(); if (!plugView) return; if (plugView->isPlatformTypeSupported(kPlatformTypeX11EmbedWindowID) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::[%p]::openEditor(%p)" " *** X11 Window platform is not supported (%s).", this, pParent, kPlatformTypeX11EmbedWindowID); #endif return; } // What style do we create tool childs? Qt::WindowFlags wflags = Qt::Window; #if 0//QTRACTOR_VST3_EDITOR_TOOL qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bKeepToolsOnTop) { wflags |= Qt::Tool; // wflags |= Qt::WindowStaysOnTopHint; // Make sure it has a parent... if (pParent == nullptr) pParent = qtractorMainForm::getInstance(); } #endif m_pEditorWidget = new EditorWidget(pParent, wflags); m_pEditorWidget->setAttribute(Qt::WA_QuitOnClose, false); m_pEditorWidget->setWindowTitle(pType->name()); m_pEditorWidget->setWindowIcon(QIcon::fromTheme("qtractorPlugin")); m_pEditorWidget->setPlugin(this); m_pEditorFrame = new EditorFrame(plugView, m_pEditorWidget); void *wid = (void *) m_pEditorWidget->parentWinId(); if (plugView->attached(wid, kPlatformTypeX11EmbedWindowID) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::[%p]::openEditor(%p)" " *** Failed to create/attach editor window.", this, pParent); #endif closeEditor(); return; } // Final stabilization... updateEditorTitle(); moveWidgetPos(m_pEditorWidget, editorPos()); setEditorVisible(true); } void qtractorVst3Plugin::closeEditor (void) { if (m_pEditorWidget == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin[%p]::closeEditor()", this); #endif setEditorVisible(false); IPlugView *plugView = m_pImpl->plugView(); if (plugView && plugView->removed() != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin::[%p]::closeEditor()" " *** Failed to remove/detach window.", this); #endif } delete m_pEditorWidget; m_pEditorWidget = nullptr; if (m_pEditorFrame) { delete m_pEditorFrame; m_pEditorFrame = nullptr; } m_pImpl->closeEditor(); } // GUI editor visibility state. void qtractorVst3Plugin::setEditorVisible ( bool bVisible ) { if (m_pEditorWidget) { if (!bVisible) setEditorPos(m_pEditorWidget->pos()); m_pEditorWidget->setVisible(bVisible); if (bVisible) { m_pEditorWidget->raise(); m_pEditorWidget->activateWindow(); } } } bool qtractorVst3Plugin::isEditorVisible (void) const { return (m_pEditorWidget ? m_pEditorWidget->isVisible() : false); } // Update editor widget caption. void qtractorVst3Plugin::setEditorTitle ( const QString& sTitle ) { qtractorPlugin::setEditorTitle(sTitle); if (m_pEditorWidget) m_pEditorWidget->setWindowTitle(sTitle); } // Our own editor widget/frame accessors. QWidget *qtractorVst3Plugin::editorWidget (void) const { return m_pEditorWidget; } qtractorVst3Plugin::EditorFrame *qtractorVst3Plugin::editorFrame (void) const { return m_pEditorFrame; } // Processor stuff... // void qtractorVst3Plugin::process_midi_in ( unsigned char *data, unsigned int size, unsigned long offset, unsigned short port ) { m_pImpl->process_midi_in(data, size, offset, port); } void qtractorVst3Plugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { // To process MIDI events, if any... qtractorMidiManager *pMidiManager = nullptr; const unsigned short iMidiIns = midiIns(); const unsigned short iMidiOuts = midiOuts(); if (iMidiIns > 0 || iMidiOuts > 0) pMidiManager = list()->midiManager(); // Process MIDI input stream, if any... if (pMidiManager && m_pMidiParser) { qtractorMidiBuffer *pMidiBuffer = pMidiManager->buffer_in(); const unsigned int iEventCount = pMidiBuffer->count(); for (unsigned int i = 0; i < iEventCount; ++i) { snd_seq_event_t *pEv = pMidiBuffer->at(i); unsigned char midiData[c_iMaxMidiData]; unsigned char *pMidiData = &midiData[0]; long iMidiData = sizeof(midiData); iMidiData = snd_midi_event_decode(m_pMidiParser, pMidiData, iMidiData, pEv); if (iMidiData < 0) break; m_pImpl->process_midi_in(pMidiData, iMidiData, pEv->time.tick, 0); } } const unsigned short iChannels = channels(); const unsigned short iAudioIns = audioIns(); const unsigned short iAudioOuts = audioOuts(); unsigned short iIChannel = 0; unsigned short iOChannel = 0; unsigned short i; // For each audio input port... for (i = 0; i < iAudioIns; ++i) { if (iIChannel < iChannels) m_ppIBuffer[i] = ppIBuffer[iIChannel++]; else m_ppIBuffer[i] = m_pfIDummy; // dummy input! } // For each audio output port... for (i = 0; i < iAudioOuts; ++i) { if (iOChannel < iChannels) m_ppOBuffer[i] = ppOBuffer[iOChannel++]; else m_ppOBuffer[i] = m_pfODummy; // dummy output! } // Run the main processor routine... // m_pImpl->process(m_ppIBuffer, m_ppOBuffer, nframes); // Wrap dangling output channels?... for (i = iOChannel; i < iChannels; ++i) ::memset(ppOBuffer[i], 0, nframes * sizeof(float)); // Process MIDI output stream, if any... if (pMidiManager) { if (iMidiOuts > 0) { qtractorMidiBuffer *pMidiBuffer = pMidiManager->buffer_out(); EventList& events_out = m_pImpl->events_out(); const int32 nevents = events_out.getEventCount(); for (int32 i = 0; i < nevents; ++i) { Vst::Event event; if (events_out.getEvent(i, event) == kResultOk) { snd_seq_event_t ev; snd_seq_ev_clear(&ev); switch (event.type) { case Vst::Event::kNoteOnEvent: ev.type = SND_SEQ_EVENT_NOTEON; ev.data.note.channel = event.noteOn.channel; ev.data.note.note = event.noteOn.pitch; ev.data.note.velocity = event.noteOn.velocity; break; case Vst::Event::kNoteOffEvent: ev.type = SND_SEQ_EVENT_NOTEOFF; ev.data.note.channel = event.noteOff.channel; ev.data.note.note = event.noteOff.pitch; ev.data.note.velocity = event.noteOff.velocity; break; case Vst::Event::kPolyPressureEvent: ev.type = SND_SEQ_EVENT_KEYPRESS; ev.data.note.channel = event.polyPressure.channel; ev.data.note.note = event.polyPressure.pitch; ev.data.note.velocity = event.polyPressure.pressure; break; } if (ev.type != SND_SEQ_EVENT_NONE) pMidiBuffer->push(&ev, event.sampleOffset); } } pMidiManager->swapOutputBuffers(); } else { pMidiManager->resetOutputBuffers(); } } } // Plugin current latency (in frames); unsigned long qtractorVst3Plugin::latency (void) const { return m_pImpl->latency(); } // Provisional program/patch accessor. bool qtractorVst3Plugin::getProgram ( int iIndex, Program& program ) const { const QList& programs = m_pImpl->programs(); if (iIndex < 0 || iIndex >= programs.count()) return false; // Map this to that... program.bank = 0; program.prog = iIndex; program.name = programs.at(iIndex); return true; } // Specific MIDI instrument selector. void qtractorVst3Plugin::selectProgram ( int iBank, int iProg ) { // HACK: We don't change program-preset when // we're supposed to be multi-timbral... if (list()->isMidiBus()) return; m_pImpl->selectProgram(iBank, iProg); // HACK: Make sure all displayed parameter values are in sync. updateParamValues(true); } // Plugin preset i/o (configuration from/to state files). bool qtractorVst3Plugin::loadPresetFile ( const QString& sFilename ) { const QString& sExt = QFileInfo(sFilename).suffix().toLower(); if (sExt == "qtx") return qtractorPlugin::loadPresetFile(sFilename); #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin[%p]::loadPresetFile(\"%s\")", this, sFilename.toUtf8().constData()); #endif QFile file(sFilename); if (!file.open(QFile::ReadOnly)) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin[%p]::loadPresetFile(\"%s\")" " QFile::open(QFile::ReadOnly) FAILED!", this, sFilename.toUtf8().constData()); #endif return false; } const bool bResult = m_pImpl->setState(file.readAll()); file.close(); // HACK: Make sure all displayed parameter values are in sync. resetParamValues(false); return bResult; } bool qtractorVst3Plugin::savePresetFile ( const QString& sFilename ) { const QString& sExt = QFileInfo(sFilename).suffix().toLower(); if (sExt == "qtx") return qtractorPlugin::savePresetFile(sFilename); #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin[%p]::savePresetFile(\"%s\")", this, sFilename.toUtf8().constData()); #endif QByteArray data; if (!m_pImpl->getState(data)) return false; QFile file(sFilename); if (!file.open(QFile::WriteOnly | QFile::Truncate)) { #ifdef CONFIG_DEBUG qDebug("qtractorVst3Plugin[%p]::savePresetFile(\"%s\")" " QFile::open(QFile::WriteOnly|QFile::Truncate) FAILED!", this, sFilename.toUtf8().constData()); #endif return false; } file.write(data); file.close(); return true; } // Make up some others dirty... void qtractorVst3Plugin::updateDirtyCount (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->dirtyNotifySlot(); updateFormDirtyCount(); } // Common host-time keeper (static) void qtractorVst3Plugin::updateTime ( qtractorAudioEngine *pAudioEngine ) { g_hostContext.updateProcessContext(pAudioEngine); } // Host cleanup (static). void qtractorVst3Plugin::clearAll (void) { g_hostContext.clear(); } //---------------------------------------------------------------------------- // qtractorVst3PluginParam -- VST3 plugin parameter interface decl. // // Constructor. qtractorVst3Plugin::Param::Param ( qtractorVst3Plugin *pPlugin, unsigned long iIndex ) : qtractorPlugin::Param(pPlugin, iIndex), m_pImpl(nullptr), m_bValueEnabled(false) { qtractorVst3PluginType *pType = static_cast (pPlugin->type()); if (pType) { Vst::IEditController *controller = pType->impl()->controller(); if (controller) { const unsigned long iMaxIndex = controller->getParameterCount(); if (iIndex < iMaxIndex) { Vst::ParameterInfo paramInfo; ::memset(¶mInfo, 0, sizeof(Vst::ParameterInfo)); if (controller->getParameterInfo(iIndex, paramInfo) == kResultOk) { m_pImpl = new Impl(paramInfo); setName(fromTChar(paramInfo.title)); setMinValue(0.0f); setMaxValue(paramInfo.stepCount > 1 ? float(paramInfo.stepCount) : 1.0f); setDefaultValue(float(controller->getParamNormalized(paramInfo.id))); } } } } } // Destructor. qtractorVst3Plugin::Param::~Param (void) { if (m_pImpl) delete m_pImpl; } // Port range hints predicate methods. bool qtractorVst3Plugin::Param::isBoundedBelow (void) const { return true; } bool qtractorVst3Plugin::Param::isBoundedAbove (void) const { return true; } bool qtractorVst3Plugin::Param::isDefaultValue (void) const { return true; } bool qtractorVst3Plugin::Param::isLogarithmic (void) const { return false; } bool qtractorVst3Plugin::Param::isSampleRate (void) const { return false; } bool qtractorVst3Plugin::Param::isInteger (void) const { if (m_pImpl) return (m_pImpl->paramInfo().stepCount > 1); else return false; } bool qtractorVst3Plugin::Param::isToggled (void) const { if (m_pImpl) return (m_pImpl->paramInfo().stepCount == 1); else return false; } bool qtractorVst3Plugin::Param::isDisplay (void) const { return true; } // Current display value. QString qtractorVst3Plugin::Param::display (void) const { qtractorVst3PluginType *pType = nullptr; if (plugin()) pType = static_cast (plugin()->type()); if (pType && m_pImpl) { Vst::IEditController *controller = pType->impl()->controller(); if (controller) { const Vst::ParamID id = m_pImpl->paramInfo().id; const Vst::ParamValue val = Vst::ParamValue(value()); Vst::String128 str; if (controller->getParamStringByValue(id, val, str) == kResultOk) return fromTChar(str); } } // Default parameter display value... return qtractorPlugin::Param::display(); } #endif // CONFIG_VST3 // end of qtractorVst3Plugin.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiRpn.h0000644000000000000000000000013215124701674016605 xustar0030 mtime=1767080892.794263449 30 atime=1767080892.794263449 30 ctime=1767080892.794263449 qtractor-1.5.11/src/qtractorMidiRpn.h0000644000175000001440000000314315124701674016576 0ustar00rncbcusers// qtractorMidiRpn.h // /**************************************************************************** Copyright (C) 2005-2019, 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 __qtractorMidiRpn_h #define __qtractorMidiRpn_h //--------------------------------------------------------------------- // qtractorMidiRpn - decl. class qtractorMidiRpn { public: qtractorMidiRpn(); ~qtractorMidiRpn(); enum Type { None = 0, CC = 0x10, RPN = 0x20, NRPN = 0x30, CC14 = 0x40 }; struct Event { unsigned long time; int port; unsigned char status; unsigned short param; unsigned short value; }; bool isPending() const; bool process(const Event& event); bool dequeue(Event& event); void flush(); private: class Impl; Impl *m_pImpl; }; #endif // __qtractorMidiRpn_h // end of qtractorMidiRpn.h qtractor-1.5.11/src/PaxHeaders/qtractorVst2Plugin.h0000644000000000000000000000013215124701674017260 xustar0030 mtime=1767080892.804263407 30 atime=1767080892.804263407 30 ctime=1767080892.804263407 qtractor-1.5.11/src/qtractorVst2Plugin.h0000644000175000001440000001623315124701674017255 0ustar00rncbcusers// qtractorVst2Plugin.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorVst2Plugin_h #define __qtractorVst2Plugin_h #include "qtractorPlugin.h" // Allow VST 2.3 compability mode. // #define VST_2_4_EXTENSIONS 0 // #define VST_FORCE_DEPRECATED 1 #include #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32) #define __cdecl #endif #ifdef CONFIG_VESTIGE #include #else #include #endif #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #if defined(Q_WS_X11) #define CONFIG_VST2_X11 #endif #endif // Forward decls. class QFile; //---------------------------------------------------------------------------- // qtractorVst2PluginType -- VST2 plugin type instance. // class qtractorVst2PluginType : public qtractorPluginType { public: // Forward declarations. class Effect; // Constructor. qtractorVst2PluginType(qtractorPluginFile *pFile, unsigned long iIndex, Effect *pEffect = nullptr) : qtractorPluginType(pFile, iIndex, qtractorPluginType::Vst2), m_pEffect(pEffect), m_iFlagsEx(0) {} // Destructor. ~qtractorVst2PluginType() { close(); } // Derived methods. bool open(); void close(); // Specific accessors. Effect *effect() const { return m_pEffect; } // Factory method (static) static qtractorVst2PluginType *createType( qtractorPluginFile *pFile, unsigned long iIndex); // Effect instance method (static) static AEffect *vst2_effect(qtractorPluginFile *pFile); // VST2 host dispatcher. int vst2_dispatch( long opcode, long index, long value, void *ptr, float opt) const; // Instance cached-deferred accessors. const QString& aboutText(); protected: // VST2 flag inquirer. bool vst2_canDo(const char *pszCanDo) const; private: // VST2 descriptor reference. Effect *m_pEffect; unsigned int m_iFlagsEx; }; //---------------------------------------------------------------------------- // qtractorVst2Plugin -- VST2 plugin instance. // class qtractorVst2Plugin : public qtractorPlugin { public: // Constructor. qtractorVst2Plugin(qtractorPluginList *pList, qtractorVst2PluginType *pVst2Type); // Destructor. ~qtractorVst2Plugin(); // Forward decl. class Param; // Channel/intsance number accessors. void setChannels(unsigned short iChannels); // Do the actual (de)activation. void activate(); void deactivate(); // The main plugin processing procedure. void process(float **ppIBuffer, float **ppOBuffer, unsigned int nframes); // Parameter update method. void updateParam(qtractorPlugin::Param *pParam, float fValue, bool bUpdate); // Bank/program selector override. void selectProgram(int iBank, int iProg); // Provisional program/patch accessor. bool getProgram(int iIndex, Program& program) const; // Configuration (CLOB) stuff. void configure(const QString& sKey, const QString& sValue); // Plugin configuration/state snapshot. void freezeConfigs(); void releaseConfigs(); // Plugin current latency (in frames); unsigned long latency() const; // Plugin preset i/o (configuration from/to (fxp/fxb files). bool loadPresetFile(const QString& sFilename); bool savePresetFile(const QString& sFilename); // GUI Editor stuff. void openEditor(QWidget *pParent = nullptr); void closeEditor(); void idleEditor(); // GUI editor visibility state. void setEditorVisible(bool bVisible); bool isEditorVisible() const; void setEditorTitle(const QString& sTitle); // Specific accessors. AEffect *vst2_effect(unsigned short iInstance) const; // VST2 host dispatcher. int vst2_dispatch(unsigned short iInstance, long opcode, long index, long value, void *ptr, float opt) const; // Our own editor widget accessor. QWidget *editorWidget() const; // Our own editor widget size accessor. void resizeEditor(int w, int h); // Global VST2 plugin lookup. static qtractorVst2Plugin *findPlugin(AEffect *pVst2Effect); // Idle editor (static). static void idleEditorAll(); // Editor widget forward decls. class EditorWidget; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_VST2_X11 // Global X11 event filter. static bool x11EventFilter(void *pvEvent); #endif // CONFIG_VST2_X11 #endif // All parameters update method. void updateParamValues(bool bUpdate); // Make up some others dirty... void updateDirtyCount(); private: // Instance variables. qtractorVst2PluginType::Effect **m_ppEffects; // Audio I/O buffer pointers. float **m_ppIBuffer; float **m_ppOBuffer; // Dummy I/O buffers. float *m_pfIDummy; float *m_pfODummy; // Our own editor widget (parent frame). EditorWidget *m_pEditorWidget; volatile bool m_bEditorClosed; }; //---------------------------------------------------------------------------- // qtractorVst2Plugin::Param -- VST2 plugin control input port instance. // class qtractorVst2Plugin::Param : public qtractorPlugin::Param { public: // Constructors. Param(qtractorVst2Plugin *pVst2Plugin, unsigned long iIndex); // Port range hints predicate methods. bool isBoundedBelow() const; bool isBoundedAbove() const; bool isDefaultValue() const; bool isLogarithmic() const; bool isSampleRate() const; bool isInteger() const; bool isToggled() const; bool isDisplay() const; // Current display value. QString display() const; private: VstParameterProperties m_props; }; //---------------------------------------------------------------------- // class qtractorVst2Preset -- VST2 preset file interface. // class qtractorVst2Preset { public: // Constructor. qtractorVst2Preset(qtractorVst2Plugin *pVst2Plugin); // File loader/saver. bool load(const QString& sFilename); bool save(const QString& sFilename); protected: // Forward decls. struct BaseHeader; struct BankHeader; struct ProgHeader; struct Chunk; // Loader methods. bool load_bank_progs(QFile& file); bool load_prog_params(QFile& file); bool load_bank_chunk(QFile& file); bool load_prog_chunk(QFile& file); bool load_chunk(QFile& file, int preset); // Saver methods. bool save_bank_progs(QFile& file); bool save_prog_params(QFile& file); bool save_bank_chunk(QFile& file, const Chunk& chunk); bool save_prog_chunk(QFile& file, const Chunk& chunk); bool save_chunk(QFile& file, const Chunk& chunk); bool get_chunk(Chunk& chunk, int preset); private: // Instance variables. qtractorVst2Plugin *m_pVst2Plugin; }; #endif // __qtractorVst2Plugin_h // end of qtractorVst2Plugin.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiManager.cpp0000644000000000000000000000013215124701674017753 xustar0030 mtime=1767080892.794263449 30 atime=1767080892.794263449 30 ctime=1767080892.794263449 qtractor-1.5.11/src/qtractorMidiManager.cpp0000644000175000001440000012771315124701674017756 0ustar00rncbcusers// qtractorMidiManager.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorMidiManager.h" #include "qtractorSession.h" #include "qtractorSessionCursor.h" #include "qtractorPlugin.h" #include "qtractorMidiEngine.h" #include "qtractorMidiMonitor.h" #include "qtractorAudioEngine.h" #include "qtractorAudioMonitor.h" #include "qtractorMainForm.h" #include "qtractorTracks.h" #include "qtractorMixer.h" #include #include #include // Specific controller definitions #define BANK_SELECT_MSB 0x00 #define BANK_SELECT_LSB 0x20 #define ALL_SOUND_OFF 0x78 #define ALL_CONTROLLERS_OFF 0x79 #define ALL_NOTES_OFF 0x7b //---------------------------------------------------------------------- // class qtractorMidiSyncThread -- MIDI sync thread decl. // class qtractorMidiSyncThread : public QThread { public: // Constructor. qtractorMidiSyncThread(unsigned int iSyncSize = 128); // Destructor. ~qtractorMidiSyncThread(); // Thread run state accessors. void setRunState(bool bRunState); bool runState() const; // Wake from executive wait condition. void sync(qtractorMidiSyncItem *pSyncItem = nullptr); protected: // The main thread executive. void run(); private: // The thread launcher queue instance reference. unsigned int m_iSyncSize; unsigned int m_iSyncMask; qtractorMidiSyncItem **m_ppSyncItems; volatile unsigned int m_iSyncRead; volatile unsigned int m_iSyncWrite; // Whether the thread is logically running. volatile bool m_bRunState; // Thread synchronization objects. QMutex m_mutex; QWaitCondition m_cond; }; //---------------------------------------------------------------------- // class qtractorMidiSyncThread -- MIDI sync thread decl. // // Constructor. qtractorMidiSyncThread::qtractorMidiSyncThread ( unsigned int iSyncSize ) { m_iSyncSize = (1 << 7); while (m_iSyncSize < iSyncSize) m_iSyncSize <<= 1; m_iSyncMask = (m_iSyncSize - 1); m_ppSyncItems = new qtractorMidiSyncItem * [m_iSyncSize]; m_iSyncRead = 0; m_iSyncWrite = 0; ::memset(m_ppSyncItems, 0, m_iSyncSize * sizeof(qtractorMidiSyncItem *)); m_bRunState = false; } // Destructor. qtractorMidiSyncThread::~qtractorMidiSyncThread (void) { delete [] m_ppSyncItems; } // Thread run state accessors. void qtractorMidiSyncThread::setRunState ( bool bRunState ) { QMutexLocker locker(&m_mutex); m_bRunState = bRunState; } bool qtractorMidiSyncThread::runState (void) const { return m_bRunState; } // The main thread executive. void qtractorMidiSyncThread::run (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiSyncThread[%p]::run(): started...", this); #endif m_mutex.lock(); m_bRunState = true; while (m_bRunState) { // Wait for sync... m_cond.wait(&m_mutex); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiSyncThread[%p]::run(): waked.", this); #endif // Call control process cycle. unsigned int r = m_iSyncRead; unsigned int w = m_iSyncWrite; while (r != w) { qtractorMidiSyncItem *pSyncItem = m_ppSyncItems[r]; if (pSyncItem->isWaitSync()) { pSyncItem->processSync(); pSyncItem->setWaitSync(false); } ++r &= m_iSyncMask; w = m_iSyncWrite; } m_iSyncRead = r; } m_mutex.unlock(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiSyncThread[%p]::run(): stopped.", this); #endif } // Wake from executive wait condition. void qtractorMidiSyncThread::sync ( qtractorMidiSyncItem *pSyncItem ) { if (pSyncItem == nullptr) { unsigned int r = m_iSyncRead; unsigned int w = m_iSyncWrite; while (r != w) { qtractorMidiSyncItem *pSyncItem = m_ppSyncItems[r]; if (pSyncItem) pSyncItem->setWaitSync(false); ++r &= m_iSyncMask; w = m_iSyncWrite; } m_iSyncRead = r; } else { // !pSyncItem->isWaitSync() unsigned int n; unsigned int r = m_iSyncRead; unsigned int w = m_iSyncWrite; if (w > r) { n = ((r - w + m_iSyncSize) & m_iSyncMask) - 1; } else if (r > w) { n = (r - w) - 1; } else { n = m_iSyncSize - 1; } if (n > 0) { pSyncItem->setWaitSync(true); m_ppSyncItems[w] = pSyncItem; m_iSyncWrite = (w + 1) & m_iSyncMask; } } if (m_mutex.tryLock()) { m_cond.wakeAll(); m_mutex.unlock(); } #ifdef CONFIG_DEBUG_0 else qDebug("qtractorMidiSyncThread[%p]::sync(): tryLock() failed.", this); #endif } //---------------------------------------------------------------------- // class qtractorMidiSyncItem -- MIDI sync item impl. // qtractorMidiSyncThread *qtractorMidiSyncItem::g_pSyncThread = nullptr; unsigned int qtractorMidiSyncItem::g_iSyncThreadRefCount = 0; // Constructor. qtractorMidiSyncItem::qtractorMidiSyncItem (void) : m_bWaitSync(false) { if (++g_iSyncThreadRefCount == 1 && g_pSyncThread == nullptr) { g_pSyncThread = new qtractorMidiSyncThread(); g_pSyncThread->start(QThread::HighestPriority); } } // Destructor. qtractorMidiSyncItem::~qtractorMidiSyncItem (void) { if (--g_iSyncThreadRefCount == 0 && g_pSyncThread != nullptr) { // Try to wake and terminate executive thread, // but give it a bit of time to cleanup... if (g_pSyncThread->isRunning()) do { g_pSyncThread->setRunState(false); // g_pSyncThread->terminate(); g_pSyncThread->sync(); } while (!g_pSyncThread->wait(100)); delete g_pSyncThread; g_pSyncThread = nullptr; } } // Sync thread state flags accessors. void qtractorMidiSyncItem::setWaitSync ( bool bWaitSync ) { m_bWaitSync = bWaitSync; } bool qtractorMidiSyncItem::isWaitSync (void) const { return m_bWaitSync; } // Post/schedule item for process sync. (static) void qtractorMidiSyncItem::syncItem ( qtractorMidiSyncItem *pSyncItem ) { if (g_pSyncThread) g_pSyncThread->sync(pSyncItem); } //---------------------------------------------------------------------- // class qtractorMidiInputBuffer -- MIDI input buffer impl. // // Input event enqueuer. bool qtractorMidiInputBuffer::enqueue ( snd_seq_event_t *pEv, unsigned long iTime ) { if (pEv->type == SND_SEQ_EVENT_NOTE || // Unlikely real-time input... pEv->type == SND_SEQ_EVENT_NOTEON) { if (m_pWetGainSubject) { const float fWetGain = m_pWetGainSubject->value(); int val = int(fWetGain * float(pEv->data.note.velocity)); if (val < 1) val = 1; else if (val > 127) val = 127; pEv->data.note.velocity = val; } } return qtractorMidiBuffer::push(pEv, iTime); } //---------------------------------------------------------------------- // class qtractorMidiOutputBuffer -- MIDI output buffer impl. // // Process buffer (in asynchronous controller thread). void qtractorMidiOutputBuffer::processSync (void) { if (m_pMidiBus == nullptr) return; if (!(m_pMidiBus->busMode() & qtractorBus::Output)) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; const unsigned long iTimeStart = pMidiEngine->timeStartEx(); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorMidiManager *pMidiManager = nullptr; if (m_pMidiBus->pluginList_out()) pMidiManager = (m_pMidiBus->pluginList_out())->midiManager(); qtractorMidiMonitor *pMidiMonitor = m_pMidiBus->midiMonitor_out(); snd_seq_event_t *pEv = m_outputBuffer.peek(); while (pEv) { qtractorTimeScale::Node *pNode = cursor.seekFrame(pEv->time.tick); const unsigned long iTime = pNode->tickFromFrame(pEv->time.tick); const unsigned long tick = (iTime > iTimeStart ? iTime - iTimeStart : 0); qtractorMidiEvent::EventType type = qtractorMidiEvent::EventType(0); unsigned short val = 0; switch (pEv->type) { case SND_SEQ_EVENT_NOTE: case SND_SEQ_EVENT_NOTEON: type = qtractorMidiEvent::NOTEON; val = pEv->data.note.velocity; if (m_pGainSubject) { val = (unsigned short) (m_pGainSubject->value() * float(val)); if (val < 1) val = 1; else if (val > 127) val = 127; pEv->data.note.velocity = val; } // Fall thru... default: break; } #ifdef CONFIG_DEBUG_0 // - show event for debug purposes... fprintf(stderr, "MIDI Out %06lu 0x%02x", tick, pEv->type); if (pEv->type == SND_SEQ_EVENT_SYSEX) { fprintf(stderr, " sysex {"); unsigned char *data = (unsigned char *) pEv->data.ext.ptr; for (unsigned int i = 0; i < pEv->data.ext.len; ++i) fprintf(stderr, " %02x", data[i]); fprintf(stderr, " }\n"); } else { for (unsigned int i = 0; i < sizeof(pEv->data.raw8.d); ++i) fprintf(stderr, " %3d", pEv->data.raw8.d[i]); fprintf(stderr, "\n"); } #endif // Schedule into sends/output bus... snd_seq_ev_set_source(pEv, m_pMidiBus->alsaPort()); snd_seq_ev_set_subs(pEv); snd_seq_ev_schedule_tick(pEv, pMidiEngine->alsaQueue(), 0, tick); snd_seq_event_output(pMidiEngine->alsaSeq(), pEv); if (pMidiManager) pMidiManager->queued(pEv, pEv->time.tick); if (pMidiMonitor) pMidiMonitor->enqueue(type, val, tick); // And next... pEv = m_outputBuffer.next(); } pMidiEngine->flush(); } //---------------------------------------------------------------------- // class qtractorMidiManager -- MIDI internal plugin list manager. // bool qtractorMidiManager::g_bAudioOutputBus = false; bool qtractorMidiManager::g_bAudioOutputAutoConnect = true; // AG: Buffer size large enough to hold some sysex events. const long c_iMaxMidiData = 512; // Constructor. qtractorMidiManager::qtractorMidiManager ( qtractorPluginList *pPluginList, unsigned int iBufferSize ) : m_pSyncItem(new SyncItem(this)), m_pPluginList(pPluginList), m_directBuffer(iBufferSize >> 1), m_queuedBuffer(iBufferSize), m_postedBuffer(iBufferSize), m_controllerBuffer(iBufferSize >> 2), m_iEventBuffer(0), #ifdef CONFIG_MIDI_PARSER m_pMidiParser(nullptr), #endif m_bAudioOutputBus(pPluginList->isAudioOutputBus()), m_sAudioOutputBusName(pPluginList->audioOutputBusName()), m_pAudioOutputBus(nullptr), m_bAudioOutputAutoConnect(pPluginList->isAudioOutputAutoConnect()), m_bAudioOutputMonitor(pPluginList->isAudioOutputMonitor()), m_pAudioOutputMonitor(nullptr), m_iCurrentBank(pPluginList->midiBank()), m_iCurrentProg(pPluginList->midiProg()), m_iPendingBankMSB(-1), m_iPendingBankLSB(-1), m_iPendingProg(-1) { const unsigned int MaxMidiEvents = bufferSize(); #ifdef CONFIG_MIDI_PARSER if (snd_midi_event_new(c_iMaxMidiData, &m_pMidiParser) == 0) snd_midi_event_no_status(m_pMidiParser, 1); #endif // Create_event buffers... #ifdef CONFIG_DSSI m_pDssiEvents = new snd_seq_event_t [MaxMidiEvents]; m_iDssiEvents = 0; #endif #ifdef CONFIG_VST2 const unsigned int Vst2BufferSize = sizeof(VstEvents) + MaxMidiEvents * sizeof(VstMidiEvent *); #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT const unsigned int Lv2EventBufferSize = (sizeof(LV2_Event) + 4) * MaxMidiEvents; #endif #ifdef CONFIG_LV2_ATOM m_iLv2AtomBufferSize = (sizeof(LV2_Atom_Event) + 4) * MaxMidiEvents; #endif #endif for (unsigned short i = 0; i < 2; ++i) { m_ppEventBuffers[i] = new qtractorMidiBuffer(MaxMidiEvents); #ifdef CONFIG_VST2 m_ppVst2Buffers[i] = new unsigned char [Vst2BufferSize]; m_ppVst2MidiBuffers[i] = new VstMidiEvent [MaxMidiEvents]; #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT m_ppLv2EventBuffers[i] = lv2_event_buffer_new(Lv2EventBufferSize, LV2_EVENT_AUDIO_STAMP); #endif #ifdef CONFIG_LV2_ATOM m_ppLv2AtomBuffers[i] = lv2_atom_buffer_new(m_iLv2AtomBufferSize, qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Chunk), qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Sequence), (i & 1) == 0); #endif #endif } createAudioOutputBus(); } // Destructor. qtractorMidiManager::~qtractorMidiManager (void) { deleteAudioOutputBus(); if (m_pAudioOutputMonitor) delete m_pAudioOutputMonitor; // Destroy event_buffers... for (unsigned short i = 0; i < 2; ++i) { #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_ATOM lv2_atom_buffer_free(m_ppLv2AtomBuffers[i]); #endif #ifdef CONFIG_LV2_EVENT ::free(m_ppLv2EventBuffers[i]); #endif #endif #ifdef CONFIG_VST2 delete [] m_ppVst2MidiBuffers[i]; delete [] m_ppVst2Buffers[i]; #endif delete m_ppEventBuffers[i]; } #ifdef CONFIG_DSSI delete [] m_pDssiEvents; m_pDssiEvents = nullptr; m_iDssiEvents = 0; #endif #ifdef CONFIG_MIDI_PARSER if (m_pMidiParser) { snd_midi_event_free(m_pMidiParser); m_pMidiParser = nullptr; } #endif delete m_pSyncItem; } // Direct buffering. bool qtractorMidiManager::direct ( snd_seq_event_t *pEvent ) { if (pEvent->type == SND_SEQ_EVENT_CONTROLLER) { switch (pEvent->data.control.param) { case BANK_SELECT_MSB: m_iPendingBankMSB = pEvent->data.control.value; break; case BANK_SELECT_LSB: m_iPendingBankLSB = pEvent->data.control.value; break; default: m_controllerBuffer.push(pEvent); break; } } else if (pEvent->type == SND_SEQ_EVENT_PGMCHANGE) m_iPendingProg = pEvent->data.control.value; return m_directBuffer.push(pEvent); } // Queued buffering. bool qtractorMidiManager::queued ( snd_seq_event_t *pEvent, unsigned long iTime, unsigned long iTimeOff ) { if (pEvent->type == SND_SEQ_EVENT_NOTE && iTime < iTimeOff) { snd_seq_event_t ev = *pEvent; ev.type = SND_SEQ_EVENT_NOTEON; if (!m_queuedBuffer.insert(&ev, iTime)) return false; ev.type = SND_SEQ_EVENT_NOTEOFF; ev.data.note.velocity = 0; ev.data.note.duration = 0; return m_postedBuffer.insert(&ev, iTimeOff); } if (pEvent->type == SND_SEQ_EVENT_NOTEOFF) return m_postedBuffer.insert(pEvent, iTime); else return m_queuedBuffer.insert(pEvent, iTime); } // Clears buffers for processing. void qtractorMidiManager::clear (void) { // Reset event buffers... for (unsigned short i = 0; i < 2; ++i) { m_ppEventBuffers[i]->clear(); #ifdef CONFIG_VST2 ::memset(m_ppVst2Buffers[i], 0, sizeof(VstEvents)); #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT LV2_Event_Buffer *pLv2EventBuffer = m_ppLv2EventBuffers[i]; lv2_event_buffer_reset(pLv2EventBuffer, LV2_EVENT_AUDIO_STAMP, (unsigned char *) (pLv2EventBuffer + 1)); #endif #ifdef CONFIG_LV2_ATOM lv2_atom_buffer_reset(m_ppLv2AtomBuffers[i], (i & 1) == 0); #endif #endif } m_iEventBuffer = 0; } // Process buffers (merge). void qtractorMidiManager::process ( unsigned long iTimeStart, unsigned long iTimeEnd ) { clear(); // Address the MIDI input buffer this way... const unsigned short iInputBuffer = m_iEventBuffer & 1; qtractorMidiBuffer *pEventBuffer = m_ppEventBuffers[iInputBuffer]; // Check for program changes and controller messages... if (m_iPendingProg >= 0 || !m_controllerBuffer.isEmpty()) qtractorMidiSyncItem::syncItem(m_pSyncItem); // Merge events in buffer for plugin processing... snd_seq_event_t *pEv0 = m_directBuffer.peek(); snd_seq_event_t *pEv1 = m_queuedBuffer.peek(); snd_seq_event_t *pEv2 = m_postedBuffer.peek(); // Direct events... while (pEv0) { pEventBuffer->push(pEv0, pEv0->time.tick); pEv0 = m_directBuffer.next(); } // Queued/posted events... while ((pEv1 && pEv1->time.tick < iTimeEnd) || (pEv2 && pEv2->time.tick < iTimeEnd)) { while (pEv1 && pEv1->time.tick < iTimeEnd && ((pEv2 && pEv2->time.tick >= pEv1->time.tick) || !pEv2)) { pEventBuffer->push(pEv1, (pEv1->time.tick > iTimeStart ? pEv1->time.tick - iTimeStart : 0)); pEv1 = m_queuedBuffer.next(); } while (pEv2 && pEv2->time.tick < iTimeEnd && ((pEv1 && pEv1->time.tick > pEv2->time.tick) || !pEv1)) { pEventBuffer->push(pEv2, (pEv2->time.tick > iTimeStart ? pEv2->time.tick - iTimeStart : 0)); pEv2 = m_postedBuffer.next(); } } #ifdef CONFIG_DEBUG_0 const unsigned int iEventCount = pEventBuffer->count(); for (unsigned int i = 0; i < iEventCount; ++i) { snd_seq_event_t *pEv = pEventBuffer->at(i); // - show event for debug purposes... const unsigned long iTime = iTimeStart + pEv->time.tick; fprintf(stderr, "MIDI Seq %06lu 0x%02x", iTime, pEv->type); if (pEv->type == SND_SEQ_EVENT_SYSEX) { fprintf(stderr, " sysex {"); unsigned char *data = (unsigned char *) pEv->data.ext.ptr; for (unsigned int i = 0; i < pEv->data.ext.len; ++i) fprintf(stderr, " %02x", data[i]); fprintf(stderr, " }\n"); } else { for (unsigned int i = 0; i < sizeof(pEv->data.raw8.d); ++i) fprintf(stderr, " %3d", pEv->data.raw8.d[i]); fprintf(stderr, "\n"); } } #endif // Process/decode into other/plugin event buffers... processEventBuffers(); // Now's time to process the plugins as usual... if (m_pAudioOutputBus) { const unsigned int nframes = iTimeEnd - iTimeStart; if (m_bAudioOutputBus) { m_pAudioOutputBus->process_prepare(nframes); m_pPluginList->process(m_pAudioOutputBus->out(), nframes); if (m_bAudioOutputMonitor) m_pAudioOutputMonitor->process_meter( m_pAudioOutputBus->out(), nframes); m_pAudioOutputBus->process_commit(nframes); } else { m_pAudioOutputBus->buffer_prepare(nframes); m_pPluginList->process(m_pAudioOutputBus->buffer(), nframes); if (m_bAudioOutputMonitor) m_pAudioOutputMonitor->process_meter( m_pAudioOutputBus->buffer(), nframes); m_pAudioOutputBus->buffer_commit(nframes); } } } // Process buffers (in asynchronous controller thread). void qtractorMidiManager::processSync (void) { // Check for programn change... if (m_iPendingProg >= 0) { m_iCurrentBank = 0; m_iCurrentProg = m_iPendingProg; if (m_iPendingBankLSB >= 0) { if (m_iPendingBankMSB >= 0) m_iCurrentBank = (m_iPendingBankMSB << 7) + m_iPendingBankLSB; else m_iCurrentBank = m_iPendingBankLSB; } else if (m_iPendingBankMSB >= 0) m_iCurrentBank = m_iPendingBankMSB; // Make the change (should be RT safe...) qtractorPlugin *pPlugin = m_pPluginList->first(); while (pPlugin) { pPlugin->selectProgram(m_iCurrentBank, m_iCurrentProg); pPlugin = pPlugin->next(); } // Reset pending status. m_iPendingBankMSB = -1; m_iPendingBankLSB = -1; m_iPendingProg = -1; } // Have all controller events sent to plugin(s), // mostly for mere GUI update purposes... snd_seq_event_t *pEv = m_controllerBuffer.peek(); while (pEv) { if (pEv->type == SND_SEQ_EVENT_CONTROLLER) { qtractorPlugin *pPlugin = m_pPluginList->first(); while (pPlugin) { pPlugin->setController( pEv->data.control.param, pEv->data.control.value); pPlugin = pPlugin->next(); } } pEv = m_controllerBuffer.next(); } m_controllerBuffer.clear(); } // Resets all buffering. void qtractorMidiManager::reset (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->lock(); m_directBuffer.clear(); m_queuedBuffer.clear(); m_postedBuffer.reset(); // formerly .clear(); clear(); #ifdef CONFIG_MIDI_PARSER if (m_pMidiParser) { snd_midi_event_reset_decode(m_pMidiParser); snd_midi_event_reset_encode(m_pMidiParser); } #endif m_pPluginList->resetLatency(); m_pPluginList->resetBuffers(); m_controllerBuffer.clear(); m_iPendingBankMSB = -1; m_iPendingBankLSB = -1; m_iPendingProg = -1; pSession->unlock(); } // Direct MIDI controller helper. void qtractorMidiManager::setController ( unsigned short iChannel, int iController, int iValue ) { snd_seq_event_t ev; snd_seq_ev_clear(&ev); ev.type = SND_SEQ_EVENT_CONTROLLER; ev.data.control.channel = iChannel; ev.data.control.param = iController; ev.data.control.value = iValue; direct(&ev); } // Shut-off MIDI channel (panic)... void qtractorMidiManager::shutOff ( unsigned short iChannel ) { setController(iChannel, ALL_SOUND_OFF, 0); setController(iChannel, ALL_NOTES_OFF, 0); setController(iChannel, ALL_CONTROLLERS_OFF, 0); } // Factory (proxy) methods. qtractorMidiManager *qtractorMidiManager::createMidiManager ( qtractorPluginList *pPluginList ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return nullptr; qtractorMidiManager *pMidiManager = new qtractorMidiManager(pPluginList); pSession->addMidiManager(pMidiManager); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiManager::createMidiManager(%p)", pMidiManager); #endif return pMidiManager; } void qtractorMidiManager::deleteMidiManager ( qtractorMidiManager *pMidiManager ) { if (pMidiManager == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiManager::deleteMidiManager(%p)", pMidiManager); #endif pSession->removeMidiManager(pMidiManager); delete pMidiManager; } // Process specific MIDI input buffer (eg. insert/merge). void qtractorMidiManager::processInputBuffer ( qtractorMidiInputBuffer *pMidiInputBuffer, unsigned long t0 ) { snd_seq_event_t *pEv; qtractorSubject *pDryGainSubject = pMidiInputBuffer->dryGainSubject(); // Address the MIDI input buffer this way... const unsigned short iInputBuffer = m_iEventBuffer & 1; qtractorMidiBuffer *pEventBuffer = m_ppEventBuffers[iInputBuffer]; const unsigned int iEventCount = pEventBuffer->count(); for (unsigned int i = 0; i < iEventCount; ++i) { pEv = pEventBuffer->at(i); // Apply gain (through/dry)... if (pDryGainSubject && (pEv->type == SND_SEQ_EVENT_NOTE || // Unlikely real-time input... pEv->type == SND_SEQ_EVENT_NOTEON)) { const float fDryGain = pDryGainSubject->value(); int val = int(fDryGain * float(pEv->data.note.velocity)); if (val < 1) val = 1; else if (val > 127) val = 127; pEv->data.note.velocity = val; } // Merge input through... if (!pMidiInputBuffer->insert(pEv, t0 + pEv->time.tick)) break; } resetInputBuffers(); pEv = pMidiInputBuffer->peek(); while (pEv) { const unsigned long t1 = pEv->time.tick; pEv->time.tick = (t1 > t0 ? t1 - t0 : 0); pEventBuffer->push(pEv, pEv->time.tick); pEv = pMidiInputBuffer->next(); } processEventBuffers(); } // Process/decode into other/plugin event buffers... void qtractorMidiManager::processEventBuffers (void) { #ifdef CONFIG_MIDI_PARSER if (m_pMidiParser == nullptr) return; const unsigned short iInputBuffer = m_iEventBuffer & 1; qtractorMidiBuffer *pEventBuffer = m_ppEventBuffers[iInputBuffer]; const unsigned int iEventCount = pEventBuffer->count(); #ifdef CONFIG_VST2 VstEvents *pVst2Events = (VstEvents *) m_ppVst2Buffers[iInputBuffer]; #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT LV2_Event_Buffer *pLv2EventBuffer = m_ppLv2EventBuffers[iInputBuffer]; LV2_Event_Iterator eiter; lv2_event_begin(&eiter, pLv2EventBuffer); #endif #ifdef CONFIG_LV2_ATOM LV2_Atom_Buffer *pLv2AtomBuffer = m_ppLv2AtomBuffers[iInputBuffer]; LV2_Atom_Buffer_Iterator aiter; lv2_atom_buffer_begin(&aiter, pLv2AtomBuffer); #endif #endif const unsigned int MaxMidiEvents = (bufferSize() << 1); unsigned int iMidiEvents = 0; #ifdef CONFIG_DSSI m_iDssiEvents = 0; #endif // AG: Untangle treatment of VST2 and LV2 plugins, // so that we can use a larger buffer for the latter... #ifdef CONFIG_VST2 unsigned int iVst2MidiEvents = 0; #endif for (unsigned int i = 0; i < iEventCount; ++i) { snd_seq_event_t *pEv = pEventBuffer->at(i); unsigned char midiData[c_iMaxMidiData]; unsigned char *pMidiData = &midiData[0]; long iMidiData = sizeof(midiData); iMidiData = snd_midi_event_decode(m_pMidiParser, pMidiData, iMidiData, pEv); if (iMidiData < 0) break; #ifdef CONFIG_DEBUG_0 // - show event for debug purposes... fprintf(stderr, "MIDI Raw %06u {", pEv->time.tick); for (long i = 0; i < iMidiData; ++i) fprintf(stderr, " %02x", pMidiData[i]); fprintf(stderr, " }\n"); #endif #ifdef CONFIG_DSSI m_pDssiEvents[m_iDssiEvents++] = *pEv; #endif #ifdef CONFIG_VST2 VstMidiEvent *pVst2MidiBuffer = m_ppVst2MidiBuffers[iInputBuffer]; VstMidiEvent *pVst2MidiEvent = &pVst2MidiBuffer[iVst2MidiEvents]; if (iMidiData < long(sizeof(pVst2MidiEvent->midiData))) { ::memset(pVst2MidiEvent, 0, sizeof(VstMidiEvent)); pVst2MidiEvent->type = kVstMidiType; pVst2MidiEvent->byteSize = sizeof(VstMidiEvent); pVst2MidiEvent->deltaFrames = pEv->time.tick; ::memcpy(&pVst2MidiEvent->midiData[0], pMidiData, iMidiData); pVst2Events->events[iVst2MidiEvents++] = (VstEvent *) pVst2MidiEvent; } #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT lv2_event_write(&eiter, pEv->time.tick, 0, QTRACTOR_LV2_MIDI_EVENT_ID, iMidiData, pMidiData); #endif #ifdef CONFIG_LV2_ATOM lv2_atom_buffer_write(&aiter, pEv->time.tick, 0, QTRACTOR_LV2_MIDI_EVENT_ID, iMidiData, pMidiData); #endif #endif if (++iMidiEvents >= MaxMidiEvents) break; } #ifdef CONFIG_VST2 pVst2Events->numEvents = iVst2MidiEvents; // pVst2Events->reserved = 0; #endif #endif // CONFIG_MIDI_PARSER } // Reset event buffers (input only) void qtractorMidiManager::resetInputBuffers (void) { const unsigned short iInputBuffer = m_iEventBuffer & 1; qtractorMidiBuffer *pEventBuffer = m_ppEventBuffers[iInputBuffer]; pEventBuffer->reset(); #ifdef CONFIG_DSSI m_iDssiEvents = 0; #endif #ifdef CONFIG_VST2 ::memset(m_ppVst2Buffers[iInputBuffer], 0, sizeof(VstEvents)); #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT LV2_Event_Buffer *pLv2EventBuffer = m_ppLv2EventBuffers[iInputBuffer]; lv2_event_buffer_reset(pLv2EventBuffer, LV2_EVENT_AUDIO_STAMP, (unsigned char *) (pLv2EventBuffer + 1)); #endif #ifdef CONFIG_LV2_ATOM lv2_atom_buffer_reset(m_ppLv2AtomBuffers[iInputBuffer], true); #endif #endif } // Reset event buffers (output only) void qtractorMidiManager::resetOutputBuffers (void) { const unsigned short iOutputBuffer = (m_iEventBuffer + 1) & 1; qtractorMidiBuffer *pEventBuffer = m_ppEventBuffers[iOutputBuffer]; pEventBuffer->reset(); #ifdef CONFIG_VST2 ::memset(m_ppVst2Buffers[iOutputBuffer], 0, sizeof(VstEvents)); #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT LV2_Event_Buffer *pLv2EventBuffer = m_ppLv2EventBuffers[iOutputBuffer]; lv2_event_buffer_reset(pLv2EventBuffer, LV2_EVENT_AUDIO_STAMP, (unsigned char *) (pLv2EventBuffer + 1)); #endif #ifdef CONFIG_LV2_ATOM lv2_atom_buffer_reset(m_ppLv2AtomBuffers[iOutputBuffer], false); #endif #endif } // Swap event buffers (in for out and vice-versa) void qtractorMidiManager::swapEventBuffers (void) { const unsigned short iInputBuffer = m_iEventBuffer & 1; qtractorMidiBuffer *pEventBuffer = m_ppEventBuffers[iInputBuffer]; pEventBuffer->reset(); #ifdef CONFIG_DSSI m_iDssiEvents = 0; #endif #ifdef CONFIG_VST2 ::memset(m_ppVst2Buffers[iInputBuffer], 0, sizeof(VstEvents)); #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT LV2_Event_Buffer *pLv2EventBuffer = m_ppLv2EventBuffers[iInputBuffer]; lv2_event_buffer_reset(pLv2EventBuffer, LV2_EVENT_AUDIO_STAMP, (unsigned char *) (pLv2EventBuffer + 1)); #endif #ifdef CONFIG_LV2_ATOM lv2_atom_buffer_reset(m_ppLv2AtomBuffers[iInputBuffer], false); #endif #endif ++m_iEventBuffer; } #ifdef CONFIG_VST2 // Copy VST2 event buffer (output)... void qtractorMidiManager::vst2_events_copy ( VstEvents *pVst2Buffer ) { const unsigned short iOutputBuffer = (m_iEventBuffer + 1) & 1; VstMidiEvent *pVst2MidiBuffer = m_ppVst2MidiBuffers[iOutputBuffer]; VstEvents *pVst2Events = (VstEvents *) m_ppVst2Buffers[iOutputBuffer]; ::memset(pVst2Events, 0, sizeof(VstEvents)); const unsigned int MaxMidiEvents = (bufferSize() << 1); unsigned int iMidiEvents = pVst2Buffer->numEvents; if (iMidiEvents > MaxMidiEvents) iMidiEvents = MaxMidiEvents; for (unsigned int i = 0; i < iMidiEvents; ++i) { VstMidiEvent *pOldMidiEvent = (VstMidiEvent *) pVst2Buffer->events[i]; VstMidiEvent *pVst2MidiEvent = &pVst2MidiBuffer[i]; ::memcpy(pVst2MidiEvent, pOldMidiEvent, sizeof(VstMidiEvent)); pVst2Events->events[i] = (VstEvent *) pVst2MidiEvent; } pVst2Events->numEvents = iMidiEvents; } // Swap VST2 event buffers... void qtractorMidiManager::vst2_events_swap (void) { const unsigned short iOutputBuffer = (m_iEventBuffer + 1) & 1; qtractorMidiBuffer *pEventBuffer = m_ppEventBuffers[iOutputBuffer]; VstMidiEvent *pVst2MidiBuffer = m_ppVst2MidiBuffers[iOutputBuffer]; VstEvents *pVst2Events = (VstEvents *) m_ppVst2Buffers[iOutputBuffer]; #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT LV2_Event_Buffer *pLv2EventBuffer = m_ppLv2EventBuffers[iOutputBuffer]; lv2_event_buffer_reset(pLv2EventBuffer, LV2_EVENT_AUDIO_STAMP, (unsigned char *) (pLv2EventBuffer + 1)); LV2_Event_Iterator eiter; lv2_event_begin(&eiter, pLv2EventBuffer); #endif #ifdef CONFIG_LV2_ATOM LV2_Atom_Buffer *pLv2AtomBuffer = m_ppLv2AtomBuffers[iOutputBuffer]; lv2_atom_buffer_reset(pLv2AtomBuffer, true); LV2_Atom_Buffer_Iterator aiter; lv2_atom_buffer_begin(&aiter, pLv2AtomBuffer); #endif #endif unsigned int iMidiEvents = 0; const unsigned int MaxMidiEvents = (bufferSize() << 1); while (iMidiEvents < MaxMidiEvents && int(iMidiEvents) < pVst2Events->numEvents) { VstMidiEvent *pVst2MidiEvent = &pVst2MidiBuffer[iMidiEvents]; unsigned char *pMidiData = (unsigned char *) &pVst2MidiEvent->midiData[0]; long iMidiData = sizeof(pVst2MidiEvent->midiData); snd_seq_event_t ev; snd_seq_ev_clear(&ev); #ifdef CONFIG_MIDI_PARSER if (m_pMidiParser) { iMidiData = snd_midi_event_encode(m_pMidiParser, pMidiData, iMidiData, &ev); if (iMidiData < 1 || ev.type == SND_SEQ_EVENT_NONE) break; ev.time.tick = pVst2MidiEvent->deltaFrames; } #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT lv2_event_write(&eiter, pVst2MidiEvent->deltaFrames, 0, QTRACTOR_LV2_MIDI_EVENT_ID, iMidiData, pMidiData); #endif #ifdef CONFIG_LV2_ATOM lv2_atom_buffer_write(&aiter, pVst2MidiEvent->deltaFrames, 0, QTRACTOR_LV2_MIDI_EVENT_ID, iMidiData, pMidiData); #endif #endif pEventBuffer->push(&ev, ev.time.tick); ++iMidiEvents; } swapEventBuffers(); } #endif // CONFIG_VST2 #ifdef CONFIG_MIDI_PARSER // Parse MIDI output and swap event buffers. // (esp. used by VST3 and CLAP) void qtractorMidiManager::swapOutputBuffers (void) { if (m_pMidiParser == nullptr) { swapEventBuffers(); return; } const unsigned short iOutputBuffer = (m_iEventBuffer + 1) & 1; qtractorMidiBuffer *pEventBuffer = m_ppEventBuffers[iOutputBuffer]; const unsigned int iEventCount = pEventBuffer->count(); #ifdef CONFIG_VST2 VstMidiEvent *pVst2MidiBuffer = m_ppVst2MidiBuffers[iOutputBuffer]; VstEvents *pVst2Events = (VstEvents *) m_ppVst2Buffers[iOutputBuffer]; ::memset(pVst2Events, 0, sizeof(VstEvents)); #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT LV2_Event_Buffer *pLv2EventBuffer = m_ppLv2EventBuffers[iOutputBuffer]; lv2_event_buffer_reset(pLv2EventBuffer, LV2_EVENT_AUDIO_STAMP, (unsigned char *) (pLv2EventBuffer + 1)); LV2_Event_Iterator eiter; lv2_event_begin(&eiter, pLv2EventBuffer); #endif #ifdef CONFIG_LV2_ATOM LV2_Atom_Buffer *pLv2AtomBuffer = m_ppLv2AtomBuffers[iOutputBuffer]; lv2_atom_buffer_reset(pLv2AtomBuffer, true); LV2_Atom_Buffer_Iterator aiter; lv2_atom_buffer_begin(&aiter, pLv2AtomBuffer); #endif #endif unsigned int iMidiEvents = 0; const unsigned int MaxMidiEvents = (bufferSize() << 1); for (unsigned int i = 0; i < iEventCount; ++i) { snd_seq_event_t *pEv = pEventBuffer->at(i); unsigned char midiData[4]; unsigned char *pMidiData = &midiData[0]; long iMidiData = sizeof(midiData); iMidiData = snd_midi_event_decode(m_pMidiParser, pMidiData, iMidiData, pEv); if (iMidiData < 1) break; #ifdef CONFIG_VST2 VstMidiEvent *pVst2MidiEvent = &pVst2MidiBuffer[iMidiEvents]; if (iMidiData >= long(sizeof(pVst2MidiEvent->midiData))) break; ::memset(pVst2MidiEvent, 0, sizeof(VstMidiEvent)); pVst2MidiEvent->type = kVstMidiType; pVst2MidiEvent->byteSize = sizeof(VstMidiEvent); pVst2MidiEvent->deltaFrames = pEv->time.tick; ::memcpy(&pVst2MidiEvent->midiData[0], pMidiData, iMidiData); pVst2Events->events[iMidiEvents] = (VstEvent *) pVst2MidiEvent; #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT lv2_event_write(&eiter, pEv->time.tick, 0, QTRACTOR_LV2_MIDI_EVENT_ID, iMidiData, pMidiData); #endif #ifdef CONFIG_LV2_ATOM lv2_atom_buffer_write(&aiter, pEv->time.tick, 0, QTRACTOR_LV2_MIDI_EVENT_ID, iMidiData, pMidiData); #endif #endif if (++iMidiEvents >= MaxMidiEvents) break; } #ifdef CONFIG_VST2 pVst2Events->numEvents = iMidiEvents; #endif swapEventBuffers(); } #endif // CONFIG_MIDI_PARSER #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT // Swap LV2 event buffers... void qtractorMidiManager::lv2_events_swap (void) { const unsigned short iOutputBuffer = (m_iEventBuffer + 1) & 1; qtractorMidiBuffer *pEventBuffer = m_ppEventBuffers[iOutputBuffer]; LV2_Event_Buffer *pLv2EventBuffer = m_ppLv2EventBuffers[iOutputBuffer]; #ifdef CONFIG_VST2 VstMidiEvent *pVst2MidiBuffer = m_ppVst2MidiBuffers[iOutputBuffer]; VstEvents *pVst2Events = (VstEvents *) m_ppVst2Buffers[iOutputBuffer]; ::memset(pVst2Events, 0, sizeof(VstEvents)); #endif #ifdef CONFIG_LV2_ATOM LV2_Atom_Buffer *pLv2AtomBuffer = m_ppLv2AtomBuffers[iOutputBuffer]; lv2_atom_buffer_reset(pLv2AtomBuffer, true); LV2_Atom_Buffer_Iterator aiter; lv2_atom_buffer_begin(&aiter, pLv2AtomBuffer); #endif LV2_Event_Iterator eiter; lv2_event_begin(&eiter, pLv2EventBuffer); unsigned int iMidiEvents = 0; const unsigned int MaxMidiEvents = (bufferSize() << 1); while (iMidiEvents < MaxMidiEvents && lv2_event_is_valid(&eiter)) { unsigned char *pMidiData; LV2_Event *pLv2Event = lv2_event_get(&eiter, &pMidiData); if (pLv2Event == nullptr) break; if (pLv2Event->type == QTRACTOR_LV2_MIDI_EVENT_ID) { long iMidiData = pLv2Event->size; if (iMidiData < 1) break; #ifdef CONFIG_VST2 VstMidiEvent *pVst2MidiEvent = &pVst2MidiBuffer[iMidiEvents]; if (iMidiData >= long(sizeof(pVst2MidiEvent->midiData))) break; #endif snd_seq_event_t ev; snd_seq_ev_clear(&ev); #ifdef CONFIG_MIDI_PARSER if (m_pMidiParser) { iMidiData = snd_midi_event_encode(m_pMidiParser, pMidiData, iMidiData, &ev); if (iMidiData < 1 || ev.type == SND_SEQ_EVENT_NONE) break; ev.time.tick = pLv2Event->frames; } #endif #ifdef CONFIG_VST2 ::memset(pVst2MidiEvent, 0, sizeof(VstMidiEvent)); pVst2MidiEvent->type = kVstMidiType; pVst2MidiEvent->byteSize = sizeof(VstMidiEvent); pVst2MidiEvent->deltaFrames = pLv2Event->frames; ::memcpy(&pVst2MidiEvent->midiData[0], pMidiData, iMidiData); pVst2Events->events[iMidiEvents] = (VstEvent *) pVst2MidiEvent; #endif #ifdef CONFIG_LV2_ATOM lv2_atom_buffer_write(&aiter, pLv2Event->frames, 0, QTRACTOR_LV2_MIDI_EVENT_ID, iMidiData, pMidiData); #endif pEventBuffer->push(&ev, ev.time.tick); ++iMidiEvents; } lv2_event_increment(&eiter); } #ifdef CONFIG_VST2 pVst2Events->numEvents = iMidiEvents; #endif swapEventBuffers(); } #endif // CONFIG_LV2_EVENT #ifdef CONFIG_LV2_ATOM // Swap LV2 atom buffers... void qtractorMidiManager::lv2_atom_buffer_swap (void) { const unsigned short iOutputBuffer = (m_iEventBuffer + 1) & 1; qtractorMidiBuffer *pEventBuffer = m_ppEventBuffers[iOutputBuffer]; LV2_Atom_Buffer *pLv2AtomBuffer = m_ppLv2AtomBuffers[iOutputBuffer]; #ifdef CONFIG_VST2 VstMidiEvent *pVst2MidiBuffer = m_ppVst2MidiBuffers[iOutputBuffer]; VstEvents *pVst2Events = (VstEvents *) m_ppVst2Buffers[iOutputBuffer]; ::memset(pVst2Events, 0, sizeof(VstEvents)); #endif #ifdef CONFIG_LV2_EVENT LV2_Event_Buffer *pLv2EventBuffer = m_ppLv2EventBuffers[iOutputBuffer]; lv2_event_buffer_reset(pLv2EventBuffer, LV2_EVENT_AUDIO_STAMP, (unsigned char *) (pLv2EventBuffer + 1)); LV2_Event_Iterator eiter; lv2_event_begin(&eiter, pLv2EventBuffer); #endif LV2_Atom_Buffer_Iterator aiter; lv2_atom_buffer_begin(&aiter, pLv2AtomBuffer); unsigned int iMidiEvents = 0; const unsigned int MaxMidiEvents = (bufferSize() << 1); while (iMidiEvents < MaxMidiEvents) { unsigned char *pMidiData; LV2_Atom_Event *pLv2AtomEvent = lv2_atom_buffer_get(&aiter, &pMidiData); if (pLv2AtomEvent == nullptr) break; if (pLv2AtomEvent->body.type == QTRACTOR_LV2_MIDI_EVENT_ID) { long iMidiData = pLv2AtomEvent->body.size; if (iMidiData < 1) break; #ifdef CONFIG_VST2 VstMidiEvent *pVst2MidiEvent = &pVst2MidiBuffer[iMidiEvents]; if (iMidiData >= long(sizeof(pVst2MidiEvent->midiData))) break; #endif snd_seq_event_t ev; snd_seq_ev_clear(&ev); #ifdef CONFIG_MIDI_PARSER if (m_pMidiParser) { // snd_seq_ev_clear(pEv); iMidiData = snd_midi_event_encode(m_pMidiParser, pMidiData, iMidiData, &ev); if (iMidiData < 1 || ev.type == SND_SEQ_EVENT_NONE) break; ev.time.tick = pLv2AtomEvent->time.frames; } #endif #ifdef CONFIG_VST2 ::memset(pVst2MidiEvent, 0, sizeof(VstMidiEvent)); pVst2MidiEvent->type = kVstMidiType; pVst2MidiEvent->byteSize = sizeof(VstMidiEvent); pVst2MidiEvent->deltaFrames = pLv2AtomEvent->time.frames; ::memcpy(&pVst2MidiEvent->midiData[0], pMidiData, iMidiData); pVst2Events->events[iMidiEvents] = (VstEvent *) pVst2MidiEvent; #endif #ifdef CONFIG_LV2_EVENT lv2_event_write(&eiter, pLv2AtomEvent->time.frames, 0, QTRACTOR_LV2_MIDI_EVENT_ID, iMidiData, pMidiData); #endif pEventBuffer->push(&ev, ev.time.tick); ++iMidiEvents; } lv2_atom_buffer_increment(&aiter); } #ifdef CONFIG_VST2 pVst2Events->numEvents = iMidiEvents; #endif swapEventBuffers(); } // Resize LV2 atom buffers if necessary. void qtractorMidiManager::lv2_atom_buffer_resize ( unsigned int iMinBufferSize ) { if (iMinBufferSize < m_iLv2AtomBufferSize) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const bool bPlaying = pSession->isPlaying(); if (bPlaying) pSession->lock(); m_iLv2AtomBufferSize += iMinBufferSize; for (unsigned short i = 0; i < 2; ++i) { if (m_ppLv2AtomBuffers[i]) lv2_atom_buffer_free(m_ppLv2AtomBuffers[i]); m_ppLv2AtomBuffers[i] = lv2_atom_buffer_new(m_iLv2AtomBufferSize, qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Chunk), qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Sequence), (i & 1) == 0); } if (bPlaying) pSession->unlock(); } #endif // CONFIG_LV2_ATOM #endif // CONFIG_LV2 // Some default factory options. void qtractorMidiManager::setDefaultAudioOutputBus ( bool bAudioOutputBus ) { g_bAudioOutputBus = bAudioOutputBus; } bool qtractorMidiManager::isDefaultAudioOutputBus (void) { return g_bAudioOutputBus; } void qtractorMidiManager::setDefaultAudioOutputAutoConnect ( bool bAudioOutputAutoConnect ) { g_bAudioOutputAutoConnect = bAudioOutputAutoConnect; } bool qtractorMidiManager::isDefaultAudioOutputAutoConnect (void) { return g_bAudioOutputAutoConnect; } // Output bus mode accessors. void qtractorMidiManager::setAudioOutputBus ( bool bAudioOutputBus ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->lock(); deleteAudioOutputBus(); m_bAudioOutputBus = bAudioOutputBus; createAudioOutputBus(); if (m_pAudioOutputBus) { const unsigned short iChannels = m_pAudioOutputBus->channels(); m_pPluginList->setChannelsEx(iChannels); setAudioOutputMonitorEx( m_pPluginList->resetChannels(iChannels, true)); } pSession->unlock(); } void qtractorMidiManager::resetAudioOutputBus (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->lock(); qtractorBus::ConnectList outputs; if (m_bAudioOutputBus && m_pAudioOutputBus) outputs.copy(m_pAudioOutputBus->outputs()); createAudioOutputBus(); if (m_bAudioOutputBus && m_pAudioOutputBus) m_pAudioOutputBus->outputs().copy(outputs); pSession->unlock(); } // Create audio output stuff... void qtractorMidiManager::createAudioOutputBus (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; deleteAudioOutputBus(); // Whether audio output bus is here owned, or... if (m_bAudioOutputBus) { // Owned, not part of audio engine... m_pAudioOutputBus = new qtractorAudioBus(pAudioEngine, m_pPluginList->name(), qtractorBus::BusMode(qtractorBus::Output | qtractorBus::Ex), false, 2); // FIXME: Make it always stereo (2ch). m_pAudioOutputBus->setAutoConnect(m_bAudioOutputAutoConnect); if (pAudioEngine->isActivated()) { pAudioEngine->addBusEx(m_pAudioOutputBus); if (m_pAudioOutputBus->open()) m_pAudioOutputBus->autoConnect(); } } else { // Find named audio output bus, if any... if (!m_sAudioOutputBusName.isEmpty()) m_pAudioOutputBus = static_cast ( pAudioEngine->findOutputBus(m_sAudioOutputBusName)); // Otherwise bus gets to be the first available output bus... if (m_pAudioOutputBus == nullptr) { QListIterator iter(pAudioEngine->buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { m_pAudioOutputBus = static_cast (pBus); break; } } } } // Whether audio output bus monitoring is on... if (m_bAudioOutputMonitor && m_pAudioOutputBus) { // Owned, not part of audio engine... if (m_pAudioOutputMonitor) { m_pAudioOutputMonitor->setChannels(m_pAudioOutputBus->channels()); } else { m_pAudioOutputMonitor = new qtractorAudioOutputMonitor(m_pAudioOutputBus->channels()); } } } // Destroy audio Outputnome stuff. void qtractorMidiManager::deleteAudioOutputBus (void) { if (m_bAudioOutputBus && m_pAudioOutputBus) { m_pAudioOutputBus->close(); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) pAudioEngine->removeBusEx(m_pAudioOutputBus); } delete m_pAudioOutputBus; } // Done. m_pAudioOutputBus = nullptr; } // Output monitor mode accessors. void qtractorMidiManager::setAudioOutputMonitor ( bool bAudioOutputMonitor ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->lock(); if (m_pAudioOutputMonitor && !bAudioOutputMonitor) m_pAudioOutputMonitor->setChannels(0); m_bAudioOutputMonitor = bAudioOutputMonitor; if (m_bAudioOutputMonitor && m_pAudioOutputBus) { // Owned, not part of audio engine... if (m_pAudioOutputMonitor) { m_pAudioOutputMonitor->setChannels(m_pAudioOutputBus->channels()); } else { m_pAudioOutputMonitor = new qtractorAudioOutputMonitor(m_pAudioOutputBus->channels()); } } pSession->unlock(); } void qtractorMidiManager::setAudioOutputMonitorEx ( bool bAudioOutputMonitor ) { if (( m_bAudioOutputMonitor && !bAudioOutputMonitor) || (!m_bAudioOutputMonitor && bAudioOutputMonitor)) { // Only do this if really necessary... setAudioOutputMonitor(bAudioOutputMonitor); } // Update all tracks anyway... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { // Meters on tracks list... qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) pTracks->updateMidiTrackItem(this); // Meters on mixer strips... qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) pMixer->updateMidiManagerStrip(this); } } // Instrument map builder. void qtractorMidiManager::updateInstruments (void) { m_instruments.clearAll(); for (qtractorPlugin *pPlugin = m_pPluginList->first(); pPlugin; pPlugin = pPlugin->next()) { int iIndex = 0; int iBanks = 0; qtractorPlugin::Program program; const QString& sInstrumentName = pPlugin->title(); qtractorInstrument& instr = m_instruments[sInstrumentName]; instr.setInstrumentName(sInstrumentName); while (pPlugin->getProgram(iIndex++, program)) { QString sBankName = instr.bankName(program.bank); if (sBankName.isEmpty()) { sBankName = QObject::tr("%1 - Bank %2") .arg(program.bank) .arg(iBanks++); instr.setBankName(program.bank, sBankName); } instr.setProgName( program.bank, program.prog, program.name.simplified()); } iIndex = 0; qtractorPlugin::NoteName note; while (pPlugin->getNoteName(iIndex++, note)) { qtractorInstrumentData& notes = instr.notes(note.bank, note.prog); notes[note.note] = note.name; } } } // end of qtractorMidiManager.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMessages.cpp0000644000000000000000000000013215124701674017345 xustar0030 mtime=1767080892.788263475 30 atime=1767080892.788263475 30 ctime=1767080892.788263475 qtractor-1.5.11/src/qtractorMessages.cpp0000644000175000001440000002422015124701674017335 0ustar00rncbcusers// qtractorMessages.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorMessages.h" #include "qtractorMainForm.h" #include #include #include #include #include #include #include #include #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32) #include #include #endif // The default maximum number of message lines. #define QTRACTOR_MESSAGES_MAXLINES 1000 // Notification pipe descriptors #define QTRACTOR_MESSAGES_FDNIL -1 #define QTRACTOR_MESSAGES_FDREAD 0 #define QTRACTOR_MESSAGES_FDWRITE 1 // Deprecated QTextStreamFunctions/Qt namespaces workaround. #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) #define endl Qt::endl #endif //------------------------------------------------------------------------- // qtractorMessagesTextView - Messages log dockable child window. // class qtractorMessagesTextView : public QTextBrowser { public: // Constructor. qtractorMessagesTextView(QWidget *pParent) : QTextBrowser(pParent) {} protected: // Minimum recommended. QSize sizeHint() const { return QTextBrowser::minimumSize(); } }; //------------------------------------------------------------------------- // qtractorMessages - Messages log dockable window. // // Constructor. qtractorMessages::qtractorMessages ( QWidget *pParent ) : QDockWidget(pParent) { // Surely a name is crucial (e.g.for storing geometry settings) QDockWidget::setObjectName("qtractorMessages"); // Initialize stdout capture stuff. m_pStdoutNotifier = nullptr; m_fdStdout[QTRACTOR_MESSAGES_FDREAD] = QTRACTOR_MESSAGES_FDNIL; m_fdStdout[QTRACTOR_MESSAGES_FDWRITE] = QTRACTOR_MESSAGES_FDNIL; // Create local text view widget. m_pMessagesTextView = new qtractorMessagesTextView(this); // QFont font(m_pMessagesTextView->font()); // font.setFamily("Fixed"); // m_pMessagesTextView->setFont(font); m_pMessagesTextView->setLineWrapMode(QTextEdit::NoWrap); // m_pMessagesTextView->setReadOnly(true); // m_pMessagesTextView->setUndoRedoEnabled(false); // m_pMessagesTextView->setTextFormat(Qt::LogText); // Initialize default message limit. m_iMessagesLines = 0; setMessagesLimit(QTRACTOR_MESSAGES_MAXLINES); m_pMessagesLog = nullptr; // Prepare the dockable window stuff. QDockWidget::setWidget(m_pMessagesTextView); // QDockWidget::setFeatures(QDockWidget::AllDockWidgetFeatures); QDockWidget::setAllowedAreas( Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); // Some specialties to this kind of dock window... QDockWidget::setMinimumHeight(120); // Finally set the default caption and tooltip. const QString& sCaption = tr("Messages"); QDockWidget::setWindowTitle(sCaption); QDockWidget::setWindowIcon(QIcon::fromTheme("viewMessages")); QDockWidget::setToolTip(sCaption); } // Destructor. qtractorMessages::~qtractorMessages (void) { // Turn off and close logging. setLogging(false); // No more notifications. if (m_pStdoutNotifier) delete m_pStdoutNotifier; // No need to delete child widgets, Qt does it all for us. } // Just about to notify main-window that we're closing. void qtractorMessages::closeEvent ( QCloseEvent * /*pCloseEvent*/ ) { QDockWidget::hide(); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->stabilizeForm(); } #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif // Set stdout/stderr blocking mode. bool qtractorMessages::stdoutBlock ( int fd, bool bBlock ) const { #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32) const int iFlags = ::fcntl(fd, F_GETFL, 0); const bool bNonBlock = bool(iFlags & O_NONBLOCK); if (bBlock && bNonBlock) bBlock = (::fcntl(fd, F_SETFL, iFlags & ~O_NONBLOCK) == 0); else if (!bBlock && !bNonBlock) bBlock = (::fcntl(fd, F_SETFL, iFlags | O_NONBLOCK) != 0); #endif return bBlock; } // Own stdout/stderr socket notifier slot. void qtractorMessages::stdoutNotify ( int fd ) { #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32) // Set non-blocking reads, if not already... const bool bBlock = stdoutBlock(fd, false); // Read as much as is available... QString sTemp; char achBuffer[1024]; const int cchBuffer = sizeof(achBuffer) - 1; int cchRead = ::read(fd, achBuffer, cchBuffer); while (cchRead > 0) { achBuffer[cchRead] = (char) 0; sTemp.append(achBuffer); cchRead = (bBlock ? 0 : ::read(fd, achBuffer, cchBuffer)); } // Needs to be non-empty... if (!sTemp.isEmpty()) appendStdoutBuffer(sTemp); #endif } #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic pop #endif // Stdout buffer handler -- now splitted by complete new-lines... void qtractorMessages::appendStdoutBuffer ( const QString& s ) { m_sStdoutBuffer.append(s); processStdoutBuffer(); } void qtractorMessages::processStdoutBuffer (void) { const int iLength = m_sStdoutBuffer.lastIndexOf('\n'); if (iLength > 0) { QStringListIterator iter(m_sStdoutBuffer.left(iLength).split('\n')); while (iter.hasNext()) appendMessagesText(iter.next()); m_sStdoutBuffer.remove(0, iLength + 1); } } // Stdout flusher -- show up any unfinished line... void qtractorMessages::flushStdoutBuffer (void) { processStdoutBuffer(); if (!m_sStdoutBuffer.isEmpty()) { appendMessagesText(m_sStdoutBuffer); m_sStdoutBuffer.clear(); } } // Stdout capture accessors. bool qtractorMessages::isCaptureEnabled (void) const { return (m_pStdoutNotifier != nullptr); } void qtractorMessages::setCaptureEnabled ( bool bCapture ) { // Flush current buffer. flushStdoutBuffer(); #ifdef CONFIG_DEBUG bCapture = false; #endif #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32) // Destroy if already enabled. if (!bCapture && m_pStdoutNotifier) { delete m_pStdoutNotifier; m_pStdoutNotifier = nullptr; // Close the notification pipes. if (m_fdStdout[QTRACTOR_MESSAGES_FDREAD] != QTRACTOR_MESSAGES_FDNIL) { ::close(m_fdStdout[QTRACTOR_MESSAGES_FDREAD]); m_fdStdout[QTRACTOR_MESSAGES_FDREAD] = QTRACTOR_MESSAGES_FDNIL; } } // Are we going to make up the capture? if (bCapture && m_pStdoutNotifier == nullptr && ::pipe(m_fdStdout) == 0) { ::dup2(m_fdStdout[QTRACTOR_MESSAGES_FDWRITE], STDOUT_FILENO); ::dup2(m_fdStdout[QTRACTOR_MESSAGES_FDWRITE], STDERR_FILENO); stdoutBlock(m_fdStdout[QTRACTOR_MESSAGES_FDWRITE], false); m_pStdoutNotifier = new QSocketNotifier( m_fdStdout[QTRACTOR_MESSAGES_FDREAD], QSocketNotifier::Read, this); QObject::connect(m_pStdoutNotifier, SIGNAL(activated(int)), SLOT(stdoutNotify(int))); } #endif } // Message font accessors. QFont qtractorMessages::messagesFont (void) const { return m_pMessagesTextView->font(); } void qtractorMessages::setMessagesFont( const QFont& font ) { m_pMessagesTextView->setFont(font); } // Maximum number of message lines accessors. int qtractorMessages::messagesLimit (void) const { return m_iMessagesLimit; } void qtractorMessages::setMessagesLimit ( int iMessagesLimit ) { m_iMessagesLimit = iMessagesLimit; m_iMessagesHigh = iMessagesLimit + (iMessagesLimit >> 2); } // Messages logging stuff. bool qtractorMessages::isLogging (void) const { return (m_pMessagesLog != nullptr); } void qtractorMessages::setLogging ( bool bEnabled, const QString& sFilename ) { if (m_pMessagesLog) { appendMessages(tr("Logging stopped --- %1 ---") .arg(QDateTime::currentDateTime().toString())); m_pMessagesLog->close(); delete m_pMessagesLog; m_pMessagesLog = nullptr; } if (bEnabled) { m_pMessagesLog = new QFile(sFilename); if (m_pMessagesLog->open(QIODevice::Text | QIODevice::Append)) { appendMessages(tr("Logging started --- %1 ---") .arg(QDateTime::currentDateTime().toString())); } else { delete m_pMessagesLog; m_pMessagesLog = nullptr; } } } // Messages log output method. void qtractorMessages::appendMessagesLog ( const QString& s ) { if (m_pMessagesLog) { QTextStream(m_pMessagesLog) << QTime::currentTime().toString("hh:mm:ss.zzz") << ' ' << s << endl; m_pMessagesLog->flush(); } } // Messages widget output method. void qtractorMessages::appendMessagesLine ( const QString& s ) { // Check for message line limit... if (m_iMessagesLines > m_iMessagesHigh) { m_pMessagesTextView->setUpdatesEnabled(false); QTextCursor textCursor(m_pMessagesTextView->document()->begin()); while (m_iMessagesLines > m_iMessagesLimit) { // Move cursor extending selection // from start to next line-block... textCursor.movePosition( QTextCursor::NextBlock, QTextCursor::KeepAnchor); --m_iMessagesLines; } // Remove the excessive line-blocks... textCursor.removeSelectedText(); m_pMessagesTextView->setUpdatesEnabled(true); } m_pMessagesTextView->append(s); ++m_iMessagesLines; } // The main utility methods. void qtractorMessages::appendMessages ( const QString& s ) { appendMessagesColor(s, Qt::gray); } void qtractorMessages::appendMessagesColor ( const QString& s, const QColor& rgb ) { appendMessagesLine("" + s + ""); appendMessagesLog(s); } void qtractorMessages::appendMessagesText ( const QString& s ) { appendMessagesLine(s); appendMessagesLog(s); } // History reset. void qtractorMessages::clear (void) { m_iMessagesLines = 0; m_pMessagesTextView->clear(); } // end of qtractorMessages.cpp qtractor-1.5.11/src/PaxHeaders/qtractorTrackView.cpp0000644000000000000000000000013215124701674017475 xustar0030 mtime=1767080892.803263411 30 atime=1767080892.803263411 30 ctime=1767080892.803263411 qtractor-1.5.11/src/qtractorTrackView.cpp0000644000175000001440000052077015124701674017500 0ustar00rncbcusers// qtractorTrackView.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorTrackView.h" #include "qtractorTrackTime.h" #include "qtractorTrackList.h" #include "qtractorSession.h" #include "qtractorTracks.h" #include "qtractorFiles.h" #include "qtractorAudioClip.h" #include "qtractorAudioFile.h" #include "qtractorAudioPeak.h" #include "qtractorMidiClip.h" #include "qtractorMidiFile.h" #include "qtractorSessionCursor.h" #include "qtractorFileListView.h" #include "qtractorClipSelect.h" #include "qtractorCurveSelect.h" #include "qtractorOptions.h" #include "qtractorClipCommand.h" #include "qtractorCurveCommand.h" #include "qtractorMainForm.h" #include "qtractorThumbView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #endif #ifdef CONFIG_GRADIENT #include #endif #include // Follow-playhead: maximum iterations on hold. #define QTRACTOR_SYNC_VIEW_HOLD 46 //---------------------------------------------------------------------------- // qtractorTrackView::ClipBoard - Local clipaboard singleton. // Singleton declaration. qtractorTrackView::ClipBoard qtractorTrackView::g_clipboard; //---------------------------------------------------------------------------- // qtractorTrackView -- Track view widget. // Constructor. qtractorTrackView::qtractorTrackView ( qtractorTracks *pTracks, QWidget *pParent ) : qtractorScrollView(pParent) { m_pTracks = pTracks; m_pClipSelect = new qtractorClipSelect(); m_pCurveSelect = new qtractorCurveSelect(); m_pSessionCursor = nullptr; m_pRubberBand = nullptr; m_selectMode = SelectClip; m_bDropSpan = true; m_bSnapZebra = true; m_bSnapGrid = true; m_bToolTips = true; m_bCurveEdit = false; m_pCurveEditCommand = nullptr; m_bSyncViewHold = false; m_pEditCurve = nullptr; m_pEditCurveNode = nullptr; m_pEditCurveNodeSpinBox = nullptr; m_iEditCurveNodeDirty = 0; clear(); // Zoom tool widgets m_pHzoomIn = new QToolButton(this); m_pHzoomOut = new QToolButton(this); m_pVzoomIn = new QToolButton(this); m_pVzoomOut = new QToolButton(this); m_pXzoomReset = new QToolButton(this); const QIcon& iconZoomIn = QIcon::fromTheme("viewZoomIn"); m_pHzoomIn->setIcon(iconZoomIn); m_pVzoomIn->setIcon(iconZoomIn); const QIcon& iconZoomOut = QIcon::fromTheme("viewZoomOut"); m_pHzoomOut->setIcon(iconZoomOut); m_pVzoomOut->setIcon(iconZoomOut); m_pXzoomReset->setIcon(QIcon::fromTheme("viewZoomReset")); m_pHzoomIn->setAutoRepeat(true); m_pHzoomOut->setAutoRepeat(true); m_pVzoomIn->setAutoRepeat(true); m_pVzoomOut->setAutoRepeat(true); m_pHzoomIn->setToolTip(tr("Zoom in (horizontal)")); m_pHzoomOut->setToolTip(tr("Zoom out (horizontal)")); m_pVzoomIn->setToolTip(tr("Zoom in (vertical)")); m_pVzoomOut->setToolTip(tr("Zoom out (vertical)")); m_pXzoomReset->setToolTip(tr("Zoom reset")); int iScrollBarExtent = qtractorScrollView::style()->pixelMetric(QStyle::PM_ScrollBarExtent); m_pHzoomIn->setFixedWidth(iScrollBarExtent); m_pHzoomOut->setFixedWidth(iScrollBarExtent); qtractorScrollView::addScrollBarWidget(m_pHzoomIn, Qt::AlignRight); qtractorScrollView::addScrollBarWidget(m_pHzoomOut, Qt::AlignRight); m_pVzoomOut->setFixedHeight(iScrollBarExtent); m_pVzoomIn->setFixedHeight(iScrollBarExtent); qtractorScrollView::addScrollBarWidget(m_pVzoomOut, Qt::AlignBottom); qtractorScrollView::addScrollBarWidget(m_pVzoomIn, Qt::AlignBottom); QObject::connect(m_pHzoomIn, SIGNAL(clicked()), m_pTracks, SLOT(horizontalZoomInSlot())); QObject::connect(m_pHzoomOut, SIGNAL(clicked()), m_pTracks, SLOT(horizontalZoomOutSlot())); QObject::connect(m_pVzoomIn, SIGNAL(clicked()), m_pTracks, SLOT(verticalZoomInSlot())); QObject::connect(m_pVzoomOut, SIGNAL(clicked()), m_pTracks, SLOT(verticalZoomOutSlot())); QObject::connect(m_pXzoomReset, SIGNAL(clicked()), m_pTracks, SLOT(viewZoomResetSlot())); qtractorScrollView::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); qtractorScrollView::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); qtractorScrollView::viewport()->setFocusPolicy(Qt::StrongFocus); // qtractorScrollView::viewport()->setFocusProxy(this); qtractorScrollView::viewport()->setAcceptDrops(true); // qtractorScrollView::setDragAutoScroll(false); qtractorScrollView::setMouseTracking(true); const QFont& font = qtractorScrollView::font(); qtractorScrollView::setFont(QFont(font.family(), font.pointSize() - 1)); // QObject::connect(this, SIGNAL(contentsMoving(int,int)), // this, SLOT(updatePixmap(int,int))); // Trap for help/tool-tips events. qtractorScrollView::viewport()->installEventFilter(this); } // Destructor. qtractorTrackView::~qtractorTrackView (void) { clear(); delete m_pCurveSelect; delete m_pClipSelect; } // Track view state reset. void qtractorTrackView::clear (void) { g_clipboard.clear(); m_pClipSelect->clear(); m_pCurveSelect->clear(); m_dropType = qtractorTrack::None; m_dragState = DragNone; m_dragCursor = DragNone; m_iDragClipX = 0; m_pClipDrag = nullptr; m_bDragTimer = false; m_iPlayHeadX = 0; m_iEditHeadX = 0; m_iEditTailX = 0; m_iPlayHeadAutoBackwardX = 0; m_iLastRecordX = 0; m_iPasteCount = 0; m_iPastePeriod = 0; m_iSyncViewHold = 0; if (m_pSessionCursor) delete m_pSessionCursor; m_pSessionCursor = nullptr; if (m_pRubberBand) delete m_pRubberBand; m_pRubberBand = nullptr; if (m_pCurveEditCommand) delete m_pCurveEditCommand; m_pCurveEditCommand = nullptr; m_bDragSingleTrack = false; m_iDragSingleTrackY = 0; m_iDragSingleTrackHeight = 0; m_pDragCurve = nullptr; m_pDragCurveNode = nullptr; m_iDragCurveX = 0; qtractorScrollView::setContentsPos(0, 0); } // Update track view content height. void qtractorTrackView::updateContentsHeight (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Allways give some room to drop something at the bottom... int iContentsHeight = (qtractorTrack::HeightMin << 2); // Compute total track height... qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { iContentsHeight += pTrack->zoomHeight(); pTrack = pTrack->next(); } #ifdef CONFIG_DEBUG_0 qDebug("qtractorTrackView::updateContentsHeight(%d)", iContentsHeight); #endif // Do the contents resize thing... qtractorScrollView::resizeContents( qtractorScrollView::contentsWidth(), iContentsHeight); // Keep selection (we'll update all contents anyway)... updateSelect(); } // Update track view content width. void qtractorTrackView::updateContentsWidth ( int iContentsWidth ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { const unsigned long iSessionLength = pSession->sessionEnd(); const int iSessionWidth = pSession->pixelFromFrame(iSessionLength); if (iContentsWidth < iSessionWidth) iContentsWidth = iSessionWidth; qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekPixel(iContentsWidth); iContentsWidth += pNode->pixelFromBeat( pNode->beat + (pNode->beatsPerBar << 1)) - pNode->pixel; if (iContentsWidth < qtractorScrollView::width()) iContentsWidth += qtractorScrollView::width(); // HACK: Try and check whether we need to change // current (global) audio-peak period resolution... qtractorAudioPeakFactory *pPeakFactory = pSession->audioPeakFactory(); if (pPeakFactory) { const unsigned short iPeakPeriod = pPeakFactory->peakPeriod(); // Should we change resolution? const int p2 = ((iSessionLength / iPeakPeriod) >> 1) + 1; int q2 = (iSessionWidth / p2); if (q2 > 4) { pPeakFactory->setPeakPeriod(iPeakPeriod >> 3); #ifdef CONFIG_DEBUG qDebug("qtractorTrackView::updateContentsWidth() " "iSessionLength=%lu iSessionWidth=%d " "++ peakPeriod=%u (%u) p2=%d q2=%d.", iSessionLength, iSessionWidth, pPeakFactory->peakPeriod(), iPeakPeriod, p2, q2); #endif } else if (q2 < 2 && iSessionWidth > 1) { q2 = (p2 / iSessionWidth); if (q2 > 4) { pPeakFactory->setPeakPeriod(iPeakPeriod << 3); #ifdef CONFIG_DEBUG qDebug("qtractorTrackView::updateContentsWidth() " "iSessionLength=%lu iSessionWidth=%d " "-- peakPeriod=%u (%u) p2=%d q2=%d.", iSessionLength, iSessionWidth, pPeakFactory->peakPeriod(), iPeakPeriod, p2, q2); #endif } } } #if 0 m_iPlayHeadX = pSession->pixelFromFrame(pSession->playHead()); m_iEditHeadX = pSession->pixelFromFrame(pSession->editHead()); m_iEditTailX = pSession->pixelFromFrame(pSession->editTail()); #endif } #ifdef CONFIG_DEBUG_0 qDebug("qtractorTrackView::updateContentsWidth(%d)", iContentsWidth); #endif // Do the contents resize thing... qtractorScrollView::resizeContents( iContentsWidth, qtractorScrollView::contentsHeight()); // Force an update on the track time line too... m_pTracks->trackTime()->resizeContents( iContentsWidth + 100, m_pTracks->trackTime()->viewport()->height()); m_pTracks->trackTime()->updateContents(); } // Local rectangular contents update. void qtractorTrackView::updateContents ( const QRect& rect ) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(rect); } // Overall contents update. void qtractorTrackView::updateContents (void) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(); } // Special recording visual feedback. void qtractorTrackView::updateContentsRecord (void) { QWidget *pViewport = qtractorScrollView::viewport(); const int cx = qtractorScrollView::contentsX(); int w = m_iPlayHeadX - cx; if (w > 0 && w < pViewport->width()) { int x = 0, dx = 8; if (m_iPlayHeadX > m_iLastRecordX) { dx += (m_iPlayHeadX - m_iLastRecordX); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession && pSession->midiRecord() < 1) { w = dx; if (m_iLastRecordX > cx + dx) x = m_iLastRecordX - (cx + dx); } pViewport->update(QRect(x, 0, w + dx, pViewport->height())); } else updateContents(); m_iLastRecordX = m_iPlayHeadX; } } // Draw the track view. void qtractorTrackView::drawContents ( QPainter *pPainter, const QRect& rect ) { // Draw viewport canvas... pPainter->drawPixmap(rect, m_pixmap, rect); // Lines a-head... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const int cx = qtractorScrollView::contentsX(); const int cy = qtractorScrollView::contentsY(); const int ch = qtractorScrollView::contentsHeight(); int x, w; // Draw track clip selection... if (isClipSelected()) { const QColor rgbaSelect(0, 0, 255, 120); const QRect& rectView = qtractorScrollView::viewport()->rect(); const qtractorClipSelect::ItemList& items = m_pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorClip *pClip = iter.key(); if (pClip->track() == nullptr) continue; qtractorClipSelect::Item *pClipItem = iter.value(); // Make sure it's a legal selection... if (pClip->isClipSelected()) { QRect rectClip(pClipItem->rect); rectClip.moveTopLeft( qtractorScrollView::contentsToViewport(rectClip.topLeft())); rectClip = rectClip.intersected(rectView); if (!rectClip.isEmpty()) pPainter->fillRect(rectClip, rgbaSelect); } // Draw clip contents on the fly... if (pClipItem->rubberBand) { pPainter->save(); QRect rectClip(pClipItem->rect); if (m_bDragSingleTrack) { rectClip.setY(m_iDragSingleTrackY); rectClip.setHeight(m_iDragSingleTrackHeight); } rectClip.translate(m_iDragClipX, 0); rectClip.moveTopLeft( qtractorScrollView::contentsToViewport(rectClip.topLeft())); unsigned long offset = pClipItem->offset; if (rectView.x() > rectClip.x()) offset += pSession->frameFromPixel(rectView.x() - rectClip.x()); rectClip = rectClip.intersected(rectView); QColor bg = pClip->track()->background(); bg.setAlpha(120); // somewhat-translucent... pPainter->setPen(bg.darker()); pPainter->setBrush(bg); pPainter->drawRect(rectClip); pClip->draw(pPainter, rectClip, offset); pPainter->restore(); } } } // Draw outline highlight on current clip... if (m_pClipDrag && m_pClipDrag->track()) { // Highlight current clip... const QRect& rectView = qtractorScrollView::viewport()->rect().adjusted(-4, -4, +4, +4); const QPen old_pen = pPainter->pen(); QPen pen = old_pen; pen.setColor(QColor(60, 120, 255, 120)); pen.setStyle(Qt::SolidLine); pen.setWidth(5); pPainter->setPen(pen); QRect rectClip; TrackViewInfo tvi; qtractorClip *pCurrentClip = m_pClipDrag; qtractorTrack *pCurrentTrack = pCurrentClip->track(); trackInfo(pCurrentTrack, &tvi); clipInfo(pCurrentClip, &rectClip, &tvi); rectClip.moveTopLeft( qtractorScrollView::contentsToViewport(rectClip.topLeft())); rectClip = rectClip.intersected(rectView); if (!rectClip.isEmpty()) pPainter->drawRect(rectClip); // Highlight all hash-linked MIDI clips... if (pCurrentClip->track()->trackType() == qtractorTrack::Midi) { qtractorMidiClip *pCurrentMidiClip = static_cast (pCurrentClip); if (pCurrentMidiClip && pCurrentMidiClip->isHashLinked()) { pen.setStyle(Qt::DotLine); pen.setWidth(3); pPainter->setPen(pen); const QList& list = pCurrentMidiClip->linkedClips(); QListIterator iter(list); while (iter.hasNext()) { qtractorMidiClip *pLinkedMidiClip = iter.next(); if (!pLinkedMidiClip->isActive() || pCurrentMidiClip == pLinkedMidiClip) continue; if (pCurrentTrack != pLinkedMidiClip->track()) { pCurrentTrack = pLinkedMidiClip->track(); trackInfo(pCurrentTrack, &tvi); } clipInfo(pLinkedMidiClip, &rectClip, &tvi); rectClip.moveTopLeft( qtractorScrollView::contentsToViewport(rectClip.topLeft())); rectClip = rectClip.intersected(rectView); if (!rectClip.isEmpty()) pPainter->drawRect(rectClip.adjusted(+2, +2, -2, -2)); } } } // Restore previous drawing pen... pPainter->setPen(old_pen); } // Common stuff for the job(s) ahead... unsigned long iTrackStart = 0; unsigned long iTrackEnd = 0; int y1 = 0; int y2 = 0; // On-the-fly recording clip drawing... if (pSession->isRecording() && pSession->isPlaying()) { const unsigned long iPlayHead = pSession->playHead(); iTrackStart = pSession->frameFromPixel(cx + rect.x()); iTrackEnd = iPlayHead; // Care of punch-in/out... const unsigned long iPunchIn = pSession->punchIn(); const unsigned long iPunchOut = pSession->punchOut(); const bool bPunching = (iPunchIn < iPunchOut); if (bPunching) { if (iTrackStart < iPunchIn/* && iTrackEnd > iPunchIn*/) iTrackStart = iPunchIn; if (iTrackEnd > iPunchOut/*&& iTrackStart < iPunchOut*/) iTrackEnd = iPunchOut; } // Care of each track... if (iTrackStart < iTrackEnd) { const unsigned long iFrameTime = pSession->frameTimeEx(); const unsigned long iLoopStart = pSession->loopStart(); const unsigned long iLoopEnd = pSession->loopEnd(); const bool bLooping = (iLoopStart < iLoopEnd && iPlayHead > iLoopStart && iPlayHead < iLoopEnd); qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack && y2 < ch) { y1 = y2; y2 += pTrack->zoomHeight(); // Dispatch to paint this track... qtractorClip *pClipRecord = pTrack->clipRecord(); if (pClipRecord && y2 > cy) { const int h = y2 - y1 - 2; const QRect trackRect( rect.left() - 1, y1 - cy + 1, rect.width() + 2, h); // Track/clip background colors... QColor bg = pTrack->background(); pPainter->setPen(bg.darker()); bg.setAlpha(192); // translucency... #ifdef CONFIG_GRADIENT const int y = trackRect.y(); QLinearGradient grad(0, y, 0, y + h); grad.setColorAt(0.4, bg); grad.setColorAt(1.0, bg.darker(130)); pPainter->setBrush(grad); #else pPainter->setBrush(bg); #endif unsigned long iClipStart = pClipRecord->clipStart(); unsigned long iClipOffset = pClipRecord->clipOffset(); // Care for loop-recording/take offsets... if (pTrack->isClipRecordEx()) { const unsigned long iClipEnd = iClipStart + pClipRecord->clipLength(); if (iTrackEnd > iClipEnd) iTrackEnd = iClipEnd; if (bPunching && iPunchIn > iClipStart && iPunchIn < iClipEnd) { iClipOffset += (iPunchIn - iClipStart); iClipStart = iPunchIn; } } else if (bLooping) { // Clip recording started within loop range: // -- adjust turn-around clip offset... if (iClipStart > iLoopStart && iClipStart < iLoopEnd) { const unsigned long iHeadLength = (iLoopEnd - iClipStart); const unsigned long iLoopLength = (iLoopEnd - iLoopStart); const unsigned long iClipLength = (iFrameTime - pTrack->clipRecordStart()); if (iClipLength > iHeadLength) { const unsigned long iLoopCount = (iClipLength - iHeadLength) / iLoopLength; iClipOffset += iHeadLength; iClipOffset += iLoopCount * iLoopLength; if (bPunching && iPunchIn > iLoopStart && iPunchIn < iLoopEnd) { iClipOffset += (iPunchIn - iLoopStart); iClipStart = iPunchIn; } else { iClipStart = iLoopStart; } } } else { // Clip recording is rolling within loop range: // -- redraw leading/head clip segment... unsigned long iHeadOffset = 0; if (iClipStart < iTrackStart) iHeadOffset += iTrackStart - iClipStart; x = pSession->pixelFromFrame(iClipStart) - cx; w = 0; if (iClipStart < iLoopStart) { w += pSession->pixelFromFrame(iLoopStart) - cx - x; iClipOffset += iLoopStart - iClipStart; } const QRect& headRect = QRect(x, y1 - cy + 1, w, h).intersected(trackRect); if (!headRect.isEmpty()) { const QBrush brush(pPainter->brush()); pClipRecord->drawClipRecord( pPainter, headRect, iHeadOffset); pPainter->setBrush(brush); } if (iPlayHead < iFrameTime) iClipOffset += (iFrameTime - iPlayHead); iClipStart = iLoopStart; } } // Careless to punch-in/out... if (!bPunching || iTrackStart < iPunchOut) { // Clip recording rolling: // -- redraw current clip segment... if (iClipStart < iTrackStart) iClipOffset += iTrackStart - iClipStart; x = pSession->pixelFromFrame(iClipStart) - cx; w = 0; if (iClipStart < iTrackEnd) w += pSession->pixelFromFrame(iTrackEnd) - cx - x; const QRect& clipRect = QRect(x, y1 - cy + 1, w, h).intersected(trackRect); if (!clipRect.isEmpty()) pClipRecord->drawClipRecord( pPainter, clipRect, iClipOffset); } } pTrack = pTrack->next(); } } // Make-up for next job... y1 = y2 = 0; iTrackEnd = 0; } // Automation curve drawing... pPainter->setRenderHint(QPainter::Antialiasing, true); x = rect.left(); w = rect.width(); if (w < 8) { x -= 4; if (x < 0) x = 0; w += 8; } qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack && y2 < ch) { y1 = y2; y2 += pTrack->zoomHeight(); qtractorCurve *pCurve = pTrack->currentCurve(); if (pCurve && y2 > cy) { const int h = y2 - y1 - 2; const QRect trackRect(x, y1 - cy + 1, w, h); if (iTrackStart == 0) iTrackStart = pSession->frameFromPixel(cx + x); if (iTrackEnd == 0) iTrackEnd = pSession->frameFromPixel(cx + x + w); unsigned long frame = iTrackStart; qtractorCurve::Cursor cursor(pCurve); qtractorCurve::Node *pNode = cursor.seek(frame); qtractorCurve::Mode mode = pCurve->mode(); const bool bLogarithmic = pCurve->isLogarithmic(); const bool bLocked = pCurve->isLocked(); int xc2, xc1 = trackRect.x(); int yc2, yc1 = y2 - int(cursor.scale(pNode, frame) * float(h)) - cy; int yc3, xc3 = xc1 + 4; QColor rgbCurve(pCurve->color()); QPen pen(rgbCurve); pPainter->setPen(pen); QPainterPath path; path.moveTo(xc1, yc1); while (pNode && pNode->frame < iTrackEnd) { xc2 = pSession->pixelFromFrame(pNode->frame) - cx; yc2 = y2 - int(cursor.scale(pNode) * float(h)) - cy; if (!bLocked) pPainter->drawRect(QRect(xc2 - 4, yc2 - 4, 8, 8)); switch (mode) { case qtractorCurve::Hold: path.lineTo(xc2, yc1); yc1 = yc2; break; case qtractorCurve::Linear: if (!bLogarithmic) break; // Fall thru... case qtractorCurve::Spline: default: for ( ; xc3 < xc2 - 4; xc3 += 4) { frame = pSession->frameFromPixel(cx + xc3); yc3 = y2 - int(cursor.scale(frame) * float(h)) - cy; path.lineTo(xc3, yc3); } xc3 = xc2 + 4; break; } path.lineTo(xc2, yc2); pNode = pNode->next(); } xc2 = rect.right(); frame = pSession->frameFromPixel(cx + xc2); yc2 = y2 - int(cursor.scale(frame) * float(h)) - cy; switch (mode) { case qtractorCurve::Hold: path.lineTo(xc2, yc1); break; case qtractorCurve::Linear: if (!bLogarithmic) break; // Fall thru... case qtractorCurve::Spline: default: for ( ; xc3 < xc2 - 4; xc3 += 4) { frame = pSession->frameFromPixel(cx + xc3); yc3 = y2 - int(cursor.scale(frame) * float(h)) - cy; path.lineTo(xc3, yc3); } break; } path.lineTo(xc2, yc2); // Draw line... //pen.setWidth(2); pPainter->strokePath(path, pen); // Fill semi-transparent area... rgbCurve.setAlpha(60); path.lineTo(xc2, y2 - cy); path.lineTo(xc1, y2 - cy); pPainter->fillPath(path, rgbCurve); if (m_bCurveEdit && m_pCurveSelect->isCurrentCurve(pCurve)) { const qtractorCurveSelect::ItemList& items = m_pCurveSelect->items(); qtractorCurveSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorCurveSelect::ItemList::ConstIterator iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorCurveSelect::Item *pItem = iter.value(); if (pItem->flags & 1) { QRect rectNode(pItem->rectNode); rectNode.moveTopLeft(contentsToViewport( rectNode.topLeft() + QPoint(m_iDragCurveX, 0))); pPainter->fillRect(rectNode, QColor(0, 0, 255, 80)); } } } } pTrack = pTrack->next(); } pPainter->setRenderHint(QPainter::Antialiasing, false); #ifdef CONFIG_GRADIENT // Draw canvas edge-border shadows... const int ws = 22; const int xs = qtractorScrollView::viewport()->width() - ws; if (rect.left() < ws) pPainter->fillRect(0, rect.top(), ws, rect.bottom(), m_gradLeft); if (rect.right() > xs) pPainter->fillRect(xs, rect.top(), xs + ws, rect.bottom(), m_gradRight); #endif // Draw edit-head line... //m_iEditHeadX = pSession->pixelFromFrame(pSession->editHead()); x = m_iEditHeadX - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(Qt::blue); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } // Draw edit-tail line... //m_iEditTailX = pSession->pixelFromFrame(pSession->editTail()); x = m_iEditTailX - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(Qt::blue); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } // Draw auto-backward play-head line... //m_iPlayHeadAutobackwardX = pSession->pixelFromFrame(pSession->playHeadAutobackward()); x = m_iPlayHeadAutoBackwardX - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(QColor(240, 0, 0, 60)); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } // Draw play-head line... //m_iPlayHeadX = pSession->pixelFromFrame(pSession->playHead()); x = m_iPlayHeadX - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(Qt::red); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } // Show/hide a moving clip fade in/out slope lines... if (m_dragState == DragClipFadeIn || m_dragState == DragClipFadeOut) { QRect rectHandle(m_rectHandle); // Horizontal adjust... rectHandle.translate(m_iDragClipX, 0); // Convert rectangle into view coordinates... rectHandle.moveTopLeft(contentsToViewport(rectHandle.topLeft())); // Draw envelope line... QPoint vpos; QPen pen(Qt::DotLine); pen.setColor(Qt::blue); pPainter->setPen(pen); if (m_dragState == DragClipFadeIn) { vpos = contentsToViewport(m_rectDrag.bottomLeft()); pPainter->drawLine( vpos.x(), vpos.y(), rectHandle.left(), rectHandle.top()); } else if (m_dragState == DragClipFadeOut) { vpos = contentsToViewport(m_rectDrag.bottomRight()); pPainter->drawLine( rectHandle.right(), rectHandle.top(), vpos.x(), vpos.y()); } } } // Resize event handler. void qtractorTrackView::resizeEvent ( QResizeEvent *pResizeEvent ) { qtractorScrollView::resizeEvent(pResizeEvent); #ifdef CONFIG_GRADIENT // Update canvas edge-border shadow gradients... const int ws = 22; const QColor rgba0(0, 0, 0, 0); const QColor rgba1(0, 0, 0, 30); const QColor rgba2(0, 0, 0, 120); QLinearGradient gradLeft(0, 0, ws, 0); gradLeft.setColorAt(0.0f, rgba2); gradLeft.setColorAt(0.5f, rgba1); gradLeft.setColorAt(1.0f, rgba0); m_gradLeft = gradLeft; const int xs = qtractorScrollView::viewport()->width() - ws; QLinearGradient gradRight(xs, 0, xs + ws, 0); gradRight.setColorAt(0.0f, rgba0); gradRight.setColorAt(0.5f, rgba1); gradRight.setColorAt(1.0f, rgba2); m_gradRight = gradRight; #endif // Corner tool widget layout management... if (m_pXzoomReset) { const QSize& size = qtractorScrollView::size(); int h = size.height(); int w = qtractorScrollView::style()->pixelMetric( QStyle::PM_ScrollBarExtent); int x = size.width() - w - 2; m_pXzoomReset->setGeometry(x, h - w - 2, w, w); } updateContents(); // HACK: let our (single) thumb view get notified... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && pMainForm->thumbView()) pMainForm->thumbView()->updateThumb(); } // (Re)create the complete track view pixmap. void qtractorTrackView::updatePixmap ( int cx, int cy ) { QWidget *pViewport = qtractorScrollView::viewport(); const int w = pViewport->width(); const int h = pViewport->height(); const QPalette& pal = qtractorScrollView::palette(); if (w < 1 || h < 1) return; const QColor& rgbMid = pal.mid().color(); m_pixmap = QPixmap(w, h); m_pixmap.fill(rgbMid); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return; QPainter painter(&m_pixmap); // painter.initFrom(this); painter.setFont(qtractorScrollView::font()); // Update view session cursor location, // so that we'll start drawing clips from there... const unsigned long iTrackStart = pTimeScale->frameFromPixel(cx); const unsigned long iTrackEnd = iTrackStart + pTimeScale->frameFromPixel(w); // Create cursor now if applicable... if (m_pSessionCursor == nullptr) { m_pSessionCursor = pSession->createSessionCursor(iTrackStart); } else { m_pSessionCursor->seek(iTrackStart); } const QColor& rgbLight = pal.midlight().color(); const QColor& rgbDark = rgbMid.darker(120); // Draw vertical grid lines... if (m_bSnapGrid || m_bSnapZebra) { const QBrush zebra(QColor(0, 0, 0, 20)); qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekPixel(cx); unsigned short iPixelsPerBeat = pNode->pixelsPerBeat(); unsigned int iBeat = pNode->beatFromPixel(cx); if (iBeat > 0) pNode = cursor.seekBeat(--iBeat); unsigned short iBar = pNode->barFromBeat(iBeat); int x = pNode->pixelFromBeat(iBeat) - cx; int x2 = x; while (x < w) { bool bBeatIsBar = pNode->beatIsBar(iBeat); if (bBeatIsBar) { if (m_bSnapGrid) { painter.setPen(rgbLight); painter.drawLine(x, 0, x, h); } if (m_bSnapZebra && (x > x2) && (++iBar & 1)) painter.fillRect(QRect(x2, 0, x - x2 + 1, h), zebra); x2 = x; if (iBeat == pNode->beat) iPixelsPerBeat = pNode->pixelsPerBeat(); } if (m_bSnapGrid && (bBeatIsBar || iPixelsPerBeat > 16)) { painter.setPen(rgbDark); painter.drawLine(x - 1, 0, x - 1, h); } pNode = cursor.seekBeat(++iBeat); x = pNode->pixelFromBeat(iBeat) - cx; } if (m_bSnapZebra && (x > x2) && (++iBar & 1)) painter.fillRect(QRect(x2, 0, x - x2 + 1, h), zebra); } // Draw track and horizontal lines... int y1, y2; y1 = y2 = 0; int iTrack = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack && y2 < cy + h) { y1 = y2; y2 += pTrack->zoomHeight(); if (y2 > cy) { // Dispatch to paint this track... if (y1 > cy) { painter.setPen(rgbLight); painter.drawLine(0, y1 - cy, w, y1 - cy); } const QRect trackRect(0, y1 - cy + 1, w, y2 - y1 - 2); // painter.fillRect(trackRect, rgbMid); pTrack->drawTrack(&painter, trackRect, iTrackStart, iTrackEnd, m_pSessionCursor->clip(iTrack)); painter.setPen(rgbDark); painter.drawLine(0, y2 - cy - 1, w, y2 - cy - 1); } pTrack = pTrack->next(); ++iTrack; } // Fill the empty area... if (y2 < cy + h) { painter.setPen(rgbMid); painter.drawLine(0, y2 - cy, w, y2 - cy); painter.fillRect(0, y2 - cy + 1, w, h, pal.dark().color()); } // Draw location marker lines... qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekPixel(cx); while (pMarker) { int x = pTimeScale->pixelFromFrame(pMarker->frame) - cx; if (x > w) break; painter.setPen(pMarker->color); painter.drawLine(x, 0, x, h); pMarker = pMarker->next(); } // Draw loop boundaries, if applicable... if (pSession->isLooping()) { const QBrush shade(QColor(0, 0, 0, 60)); painter.setPen(Qt::darkCyan); int x = pTimeScale->pixelFromFrame(pSession->loopStart()) - cx; if (x >= w) painter.fillRect(QRect(0, 0, w, h), shade); else if (x >= 0) { painter.fillRect(QRect(0, 0, x, h), shade); painter.drawLine(x, 0, x, h); } x = pTimeScale->pixelFromFrame(pSession->loopEnd()) - cx; if (x < 0) painter.fillRect(QRect(0, 0, w, h), shade); else if (x < w) { painter.fillRect(QRect(x, 0, w - x, h), shade); painter.drawLine(x, 0, x, h); } } // Draw punch boundaries, if applicable... if (pSession->isPunching()) { const QBrush shade(QColor(0, 0, 0, 60)); painter.setPen(Qt::darkMagenta); int x = pTimeScale->pixelFromFrame(pSession->punchIn()) - cx; if (x >= w) painter.fillRect(QRect(0, 0, w, h), shade); else if (x >= 0) { painter.fillRect(QRect(0, 0, x, h), shade); painter.drawLine(x, 0, x, h); } x = pTimeScale->pixelFromFrame(pSession->punchOut()) - cx; if (x < 0) painter.fillRect(QRect(0, 0, w, h), shade); else if (x < w) { painter.fillRect(QRect(x, 0, w - x, h), shade); painter.drawLine(x, 0, x, h); } } } // To have track view in v-sync with track list. void qtractorTrackView::contentsYMovingSlot ( int /*cx*/, int cy ) { if (qtractorScrollView::contentsY() != cy) qtractorScrollView::setContentsPos(qtractorScrollView::contentsX(), cy); } // Get track from given contents vertical position. qtractorTrack *qtractorTrackView::trackAt ( const QPoint& pos, bool bSelectTrack, TrackViewInfo *pTrackViewInfo ) const { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr || m_pSessionCursor == nullptr) return nullptr; int y1 = 0; int y2 = 0; int iTrack = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); if (y2 > pos.y()) break; pTrack = pTrack->next(); ++iTrack; } if (bSelectTrack) m_pTracks->trackList()->setCurrentTrackRow(pTrack ? iTrack : -1); if (pTrackViewInfo) { const int x = qtractorScrollView::contentsX(); const int w = qtractorScrollView::width();// View width, not contents. if (pTrack == nullptr) { // Below all tracks. y1 = y2; y2 = y1 + (qtractorTrack::HeightBase * pSession->verticalZoom()) / 100; } pTrackViewInfo->trackIndex = iTrack; pTrackViewInfo->trackStart = m_pSessionCursor->frame(); pTrackViewInfo->trackEnd = pTrackViewInfo->trackStart + pSession->frameFromPixel(w); pTrackViewInfo->trackRect.setRect(x, y1 + 1, w, y2 - y1 - 2); } return pTrack; } // Get clip from given contents position. qtractorClip *qtractorTrackView::clipAtTrack ( const QPoint& pos, QRect *pClipRect, qtractorTrack *pTrack, TrackViewInfo *pTrackViewInfo ) const { if (pTrack == nullptr) return nullptr; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr || m_pSessionCursor == nullptr) return nullptr; qtractorClip *pClip = m_pSessionCursor->clip(pTrackViewInfo->trackIndex); if (pClip == nullptr) pClip = pTrack->clips().first(); if (pClip == nullptr) return nullptr; qtractorClip *pClipAt = nullptr; while (pClip && pClip->clipStart() < pTrackViewInfo->trackEnd) { const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipEnd = iClipStart + pClip->clipLength(); const int x1 = pSession->pixelFromFrame(iClipStart); const int x2 = pSession->pixelFromFrame(iClipEnd); if (pos.x() >= x1 && x2 >= pos.x()) { pClipAt = pClip; if (pClipRect) { pClipRect->setRect( x1, pTrackViewInfo->trackRect.y(), x2 - x1, pTrackViewInfo->trackRect.height()); } } pClip = pClip->next(); } return pClipAt; } qtractorClip *qtractorTrackView::clipAt ( const QPoint& pos, bool bSelectTrack, QRect *pClipRect ) const { TrackViewInfo tvi; qtractorTrack *pTrack = trackAt(pos, bSelectTrack, &tvi); return clipAtTrack(pos, pClipRect, pTrack, &tvi); } // Get automation curve node from given contents position. qtractorCurve::Node *qtractorTrackView::nodeAtTrack ( const QPoint& pos, qtractorTrack *pTrack, TrackViewInfo *pTrackViewInfo ) const { if (pTrack == nullptr) return nullptr; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr || m_pSessionCursor == nullptr) return nullptr; qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList == nullptr) return nullptr; qtractorCurve *pCurve = pCurveList->currentCurve(); if (pCurve == nullptr) return nullptr; if (pCurve->isLocked()) return nullptr; const int w = pTrackViewInfo->trackRect.width(); const int h = pTrackViewInfo->trackRect.height(); const int y2 = pTrackViewInfo->trackRect.bottom() + 1; const int cx = qtractorScrollView::contentsX(); const unsigned long frame = pSession->frameFromPixel(cx); qtractorCurve::Cursor cursor(pCurve); qtractorCurve::Node *pNode = cursor.seek(frame); while (pNode) { const int x = pSession->pixelFromFrame(pNode->frame); if (x > cx + w) // No use.... break; const int y = y2 - int(cursor.scale(pNode) * float(h)); if (QRect(x - 4, y - 4, 8, 8).contains(pos)) return pNode; // Test next... pNode = pNode->next(); } // Not found. return nullptr; } qtractorCurve::Node *qtractorTrackView::nodeAt ( const QPoint& pos ) const { TrackViewInfo tvi; qtractorTrack *pTrack = trackAt(pos, false, &tvi); return nodeAtTrack(pos, pTrack, &tvi); } // Get contents visible rectangle from given track. bool qtractorTrackView::trackInfo ( qtractorTrack *pTrack, TrackViewInfo *pTrackViewInfo ) const { qtractorSession *pSession = pTrack->session(); if (pSession == nullptr || m_pSessionCursor == nullptr) return false; int y1, y2 = 0; int iTrack = 0; qtractorTrack *pTrackEx = pSession->tracks().first(); while (pTrackEx) { y1 = y2; y2 += pTrackEx->zoomHeight(); if (pTrackEx == pTrack) { const int x = qtractorScrollView::contentsX(); const int w = qtractorScrollView::width(); // View width, not contents. pTrackViewInfo->trackIndex = iTrack; pTrackViewInfo->trackStart = m_pSessionCursor->frame(); pTrackViewInfo->trackEnd = pSession->frameFromPixel(x + w); pTrackViewInfo->trackRect.setRect(x, y1 + 1, w, y2 - y1 - 2); return true; } pTrackEx = pTrackEx->next(); ++iTrack; } return false; } // Get contents rectangle from given clip. bool qtractorTrackView::clipInfo ( qtractorClip *pClip, QRect *pClipRect, TrackViewInfo *pTrackViewInfo ) const { qtractorTrack *pTrack = pClip->track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipEnd = iClipStart + pClip->clipLength(); const int x1 = pSession->pixelFromFrame(iClipStart); const int x2 = pSession->pixelFromFrame(iClipEnd); pClipRect->setRect( x1, pTrackViewInfo->trackRect.y(), x2 - x1, pTrackViewInfo->trackRect.height()); return true; } // Selection-dragging, following the current mouse position. qtractorTrack *qtractorTrackView::dragClipMove ( const QPoint& pos, bool bKeyStep ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return nullptr; // Which track we're pointing at? TrackViewInfo tvi; qtractorTrack *pTrack = trackAt(pos, true, &tvi); // May change vertically, if we've only one track selected, // and only between same track type... qtractorTrack *pSingleTrack = m_pClipSelect->singleTrack(); if (pSingleTrack && (pTrack == nullptr || pSingleTrack->trackType() == pTrack->trackType())) { m_bDragSingleTrack = true; m_iDragSingleTrackY = tvi.trackRect.y(); m_iDragSingleTrackHeight = tvi.trackRect.height(); } else { m_bDragSingleTrack = false; m_iDragSingleTrackY = 0; m_iDragSingleTrackHeight = 0; } // Special update on keyboard vertical drag-stepping... if (bKeyStep) m_posStep.setY(m_posStep.y() - pos.y() + tvi.trackRect.y()); // Always change horizontally wise... const int x = m_pClipSelect->rect().x(); int dx = (pos.x() - m_posDrag.x()); if (x + dx < 0) dx = -(x); // Force to origin (x=0). m_iDragClipX = pixelSnap(x + dx) - x; ensureVisible(pos.x(), pos.y(), 24, 24); showClipSelect(); // OK, we've moved it... return pTrack; } qtractorTrack *qtractorTrackView::dragClipDrop ( const QPoint& pos, bool bKeyStep, const QMimeData *pMimeData ) { // It must be a valid session... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return nullptr; // Find the current pointer track... TrackViewInfo tvi; qtractorTrack *pTrack = trackAt(pos, true, &tvi); // Special update on keyboard vertical drag-stepping... if (bKeyStep) m_posStep.setY(m_posStep.y() - pos.y() + tvi.trackRect.y() + (pTrack ? (tvi.trackRect.height() >> 1) : 0)); // If we're already dragging something, if (!m_dropItems.isEmpty()) { // Adjust to target track... updateClipDropRects(tvi.trackRect.y() + 1, tvi.trackRect.height() - 2); // Always change horizontally wise... const int x = m_rectDrag.x(); int dx = (pos.x() - m_posDrag.x()); if (x + dx < 0) dx = -(x); // Force to origin (x=0). m_iDragClipX = pixelSnap(x + dx) - x; // showDropRects(); // OK, we've moved it... return pTrack; } // Let's start from scratch... qDeleteAll(m_dropItems); m_dropItems.clear(); m_dropType = qtractorTrack::None; // Nothing more? if (pMimeData == nullptr) return nullptr; // Can it be single track channel (MIDI for sure)? if (qtractorFileChannelDrag::canDecode(pMimeData)) { // Let's see how many track-channels are there... const qtractorFileChannelDrag::List& items = qtractorFileChannelDrag::decode(pMimeData); QListIterator iter(items); while (iter.hasNext()) { const qtractorFileChannelDrag::Item& item = iter.next(); m_dropItems.append(new DropItem(item.path, item.channel)); } } else // Can we decode it as Audio/MIDI files? if (pMimeData->hasUrls()) { // Let's see how many files there are... QList list = pMimeData->urls(); QListIterator iter(list); while (iter.hasNext()) { const QString& sPath = iter.next().toLocalFile(); // Close current session and try to load the new one... if (!sPath.isEmpty()) m_dropItems.append(new DropItem(sPath)); } } // Nice, now we'll try to set a preview selection rectangle set... m_posDrag.setX(pixelSnap(pos.x() > 8 ? pos.x() - 8 : 0)); m_posDrag.setY(tvi.trackRect.y() + 1); m_rectDrag.setRect( m_posDrag.x(), m_posDrag.y(), 0, tvi.trackRect.height() - 2); // Now's time to add those rectangles... QMutableListIterator iter(m_dropItems); while (iter.hasNext()) { DropItem *pDropItem = iter.next(); // First test as a MIDI file... if (m_dropType == qtractorTrack::None || m_dropType == qtractorTrack::Midi) { const int x0 = m_posDrag.x(); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekPixel(x0); unsigned long t1, t0 = pNode->tickFromPixel(x0); qtractorMidiFile file; if (file.open(pDropItem->path)) { const unsigned short p = pSession->ticksPerBeat(); const unsigned short q = file.ticksPerBeat(); if (pDropItem->channel < 0) { t1 = t0; const int iTracks = (file.format() == 1 ? file.tracks() : 16); for (int iTrackChannel = 0; iTrackChannel < iTracks; ++iTrackChannel) { const unsigned long iTrackDuration = file.readTrackDuration(iTrackChannel); if (iTrackDuration > 0) { const unsigned long duration = uint64_t(iTrackDuration * p) / q; const unsigned long t2 = t0 + duration; if (t1 < t2) t1 = t2; } } pNode = cursor.seekTick(t1); m_rectDrag.setWidth(pNode->pixelFromTick(t1) - x0); pDropItem->rect = m_rectDrag; m_rectDrag.translate(0, m_rectDrag.height() + 4); if (m_dropType == qtractorTrack::None) m_dropType = qtractorTrack::Midi; } else { const unsigned long iTrackDuration = file.readTrackDuration(pDropItem->channel); if (iTrackDuration > 0) { const unsigned long duration = uint64_t(iTrackDuration * p) / q; t1 = t0 + duration; pNode = cursor.seekTick(t1); m_rectDrag.setWidth(pNode->pixelFromTick(t1) - x0); pDropItem->rect = m_rectDrag; m_rectDrag.translate(0, m_rectDrag.height() + 4); if (m_dropType == qtractorTrack::None) m_dropType = qtractorTrack::Midi; } else /*if (m_dropType == qtractorTrack::Midi)*/ { iter.remove(); delete pDropItem; } } file.close(); continue; } else if (m_dropType == qtractorTrack::Midi) { iter.remove(); delete pDropItem; } } // Then as an Audio file ? if (m_dropType == qtractorTrack::None || m_dropType == qtractorTrack::Audio) { qtractorAudioFile *pFile = qtractorAudioFileFactory::createAudioFile(pDropItem->path); if (pFile) { if (pFile->open(pDropItem->path)) { unsigned long iFrames = pFile->frames(); if (pFile->sampleRate() > 0 && pFile->sampleRate() != pSession->sampleRate()) { iFrames = (unsigned long) (iFrames * float(pSession->sampleRate()) / float(pFile->sampleRate())); } m_rectDrag.setWidth(pSession->pixelFromFrame(iFrames)); pDropItem->rect = m_rectDrag; m_rectDrag.translate(0, m_rectDrag.height() + 4); if (m_dropType == qtractorTrack::None) m_dropType = qtractorTrack::Audio; } else if (m_dropType == qtractorTrack::Audio) { iter.remove(); delete pDropItem; } delete pFile; continue; } else if (m_dropType == qtractorTrack::Audio) { iter.remove(); delete pDropItem; } } } // Are we still here? if (m_dropItems.isEmpty() || m_dropType == qtractorTrack::None) { m_dropType = qtractorTrack::None; return nullptr; } // Ok, sure we're into some drag&drop state... m_dragState = DragClipDrop; m_iDragClipX = 0; // Finally, show it to the world... updateClipDropRects(tvi.trackRect.y() + 1, tvi.trackRect.height() - 2); // showClipDropRects(); // Done. return pTrack; } qtractorTrack *qtractorTrackView::dragClipDropEvent ( QDropEvent *pDropEvent ) { return dragClipDrop( #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) viewportToContents(pDropEvent->position().toPoint()), #else viewportToContents(pDropEvent->pos()), #endif false, pDropEvent->mimeData()); } bool qtractorTrackView::canClipDropEvent ( QDropEvent *pDropEvent ) { // have one existing track on target? qtractorTrack *pTrack = dragClipDropEvent(pDropEvent); // Can only drop if anything... if (m_dropItems.isEmpty()) return false; // Can only drop on same type tracks... if (pTrack && pTrack->trackType() != m_dropType) return false; // Special MIDI track-channel cases... if (m_dropType == qtractorTrack::Midi) { if (m_dropItems.count() == 1 && m_dropItems.first()->channel >= 0) return true; else return (pTrack == nullptr); } // Drop in the blank... return (pTrack == nullptr || m_dropItems.count() == 1 || m_bDropSpan); } // Selection-dragging, following the current mouse position. void qtractorTrackView::dragCurveMove ( const QPoint& pos, bool /*bKeyStep*/ ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const QRect& rect = m_pCurveSelect->rect(); QRect rectUpdate(rect.translated(m_iDragCurveX, 0)); // Always change horizontally wise... const int x = rect.x(); int dx = (pos.x() - m_posDrag.x()); if (x + dx < 0) dx = -(x); // Force to origin (x=0). m_iDragCurveX = pixelSnap(x + dx) - x; ensureVisible(pos.x(), pos.y(), 24, 24); updateRect(rectUpdate.united(rect.translated(m_iDragCurveX, 0))); } // Drag enter event handler. void qtractorTrackView::dragEnterEvent ( QDragEnterEvent *pDragEnterEvent ) { #if 0 if (canDropEvent(pDragEnterEvent)) { showDropRects(); if (!pDragEnterEvent->isAccepted()) { pDragEnterEvent->setDropAction(Qt::CopyAction); pDragEnterEvent->accept(); m_bDragTimer = false; } } else { pDragEnterEvent->ignore(); hideDropRects(); } #else // Always accept the drag-enter event, // so let we deal with it during move later... pDragEnterEvent->accept(); m_bDragTimer = false; #endif } // Drag move event handler. void qtractorTrackView::dragMoveEvent ( QDragMoveEvent *pDragMoveEvent ) { if (canClipDropEvent(pDragMoveEvent)) { showClipDropRects(); if (!pDragMoveEvent->isAccepted()) { pDragMoveEvent->setDropAction(Qt::CopyAction); pDragMoveEvent->accept(); m_bDragTimer = false; } } else { pDragMoveEvent->ignore(); hideClipDropRects(); } // Kind of auto-scroll... #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QPoint& pos = viewportToContents(pDragMoveEvent->position().toPoint()); #else const QPoint& pos = viewportToContents(pDragMoveEvent->pos()); #endif ensureVisible(pos.x(), pos.y(), 24, 24); } // Drag leave event handler. void qtractorTrackView::dragLeaveEvent ( QDragLeaveEvent *pDragLeaveEvent ) { // Maybe we have something currently going on? if (pDragLeaveEvent->isAccepted()) { if (!m_bDragTimer) { m_bDragTimer = true; QTimer::singleShot(100, this, SLOT(dragTimeout())); } } else { // Nothing's being accepted... m_bDragTimer = false; resetDragState(); } } // Drag timeout slot. void qtractorTrackView::dragTimeout (void) { if (m_bDragTimer) { resetDragState(); m_bDragTimer = false; } } // Drop event handler. bool qtractorTrackView::dropClip ( const QPoint& pos, const QMimeData *pMimeData ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Add new clips on proper and consecutive track locations... unsigned long iClipStart = frameSnap( pSession->frameFromPixel(m_rectDrag.x() + m_iDragClipX)); // Now check whether the drop is intra-track... qtractorTrack *pTrack = dragClipDrop(pos, false, pMimeData); // And care if we're not spanning horizontally... if (pTrack == nullptr && (!m_bDropSpan || m_dropType == qtractorTrack::Midi)) { // Do we have something to drop anyway? // if yes, this is a extra-track drop... int iAddTrack = 0; if (!m_dropItems.isEmpty()) { // Prepare file list for import... QStringList files; QListIterator iter(m_dropItems); while (iter.hasNext()) { DropItem *pDropItem = iter.next(); if (m_dropType == qtractorTrack::Midi && pDropItem->channel >= 0) { m_pTracks->addMidiTrackChannel( pDropItem->path, pDropItem->channel, iClipStart); ++iAddTrack; } else { files.append(pDropItem->path); } } // Depending on import type... if (!files.isEmpty()) { switch (m_dropType) { case qtractorTrack::Audio: m_pTracks->addAudioTracks(files, iClipStart); ++iAddTrack; break; case qtractorTrack::Midi: m_pTracks->addMidiTracks(files, iClipStart); ++iAddTrack; break; default: break; } } } resetDragState(); return (iAddTrack > 0); } // Check whether we can really drop it. if (pTrack && pTrack->trackType() != m_dropType) { resetDragState(); return false; } // We'll build a composite command... QList clips; qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("add clip")); // If dropping spanned we'll need a track, sure... int iTrackClip = 0; const bool bAddTrack = (pTrack == nullptr); if (bAddTrack) { pTrack = new qtractorTrack(pSession, m_dropType); // Create a new track right away... int iTrack = pSession->tracks().count() + 1; const QColor& color = qtractorTrack::trackColor(iTrack); pTrack = new qtractorTrack(pSession, m_dropType); pTrack->setBackground(color); pTrack->setForeground(color.darker()); // pTrack->setTrackName(QString("Track %1").arg(iTrack)); pClipCommand->addTrack(pTrack); } // Now's time to create the clip(s)... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); QListIterator iter(m_dropItems); while (iter.hasNext()) { DropItem *pDropItem = iter.next(); switch (pTrack->trackType()) { case qtractorTrack::Audio: { qtractorAudioClip *pAudioClip = new qtractorAudioClip(pTrack); if (pAudioClip) { pAudioClip->setFilename(pDropItem->path); pAudioClip->setClipStart(iClipStart); pClipCommand->addClip(pAudioClip, pTrack); clips.append(pAudioClip); // Don't forget to add this one to local repository. if (pMainForm) pMainForm->addAudioFile(pDropItem->path); } break; } case qtractorTrack::Midi: { qtractorMidiClip *pMidiClip = new qtractorMidiClip(pTrack); if (pMidiClip) { pMidiClip->setFilename(pDropItem->path); pMidiClip->setTrackChannel(pDropItem->channel); pMidiClip->setClipStart(iClipStart); pClipCommand->addClip(pMidiClip, pTrack); clips.append(pMidiClip); // Don't forget to add this one to local repository. if (pMainForm) pMainForm->addMidiFile(pDropItem->path); } break; } case qtractorTrack::None: default: break; } // If track's new it will need a name... if (bAddTrack && iTrackClip == 0) { pTrack->setTrackName( pSession->uniqueTrackName( QFileInfo(pDropItem->path).baseName())); } // If multiple items, just snap/concatenate them... iClipStart = frameSnap(iClipStart + pSession->frameFromPixel(pDropItem->rect.width())); ++iTrackClip; } // Clean up. resetDragState(); // Put it in the form of an undoable command... const bool bResult = pSession->execute(pClipCommand); // Redo selection as new... if (!clips.isEmpty()) { QListIterator clip_iter(clips); while (clip_iter.hasNext()) clip_iter.next()->setClipSelected(true); updateClipSelect(); m_pTracks->selectionChangeNotify(); } return bResult; } void qtractorTrackView::dropEvent ( QDropEvent *pDropEvent ) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (!dropClip(viewportToContents(pDropEvent->position().toPoint()), pDropEvent->mimeData())) { #else if (!dropClip(viewportToContents(pDropEvent->pos()), pDropEvent->mimeData())) { #endif qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->dropEvent(pDropEvent); } } // Handle item selection/dragging -- mouse button press. void qtractorTrackView::mousePressEvent ( QMouseEvent *pMouseEvent ) { // Which mouse state? #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QPoint& pos = viewportToContents(pMouseEvent->position().toPoint()); #else const QPoint& pos = viewportToContents(pMouseEvent->pos()); #endif const Qt::KeyboardModifiers& modifiers = pMouseEvent->modifiers(); bool bModifier = (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)); // Are we already step-moving or pasting something? switch (m_dragState) { case DragClipStep: // One-click change from drag-step to drag-move... m_dragState = DragClipMove; m_posDrag = m_rectDrag.center(); m_posStep = QPoint(0, 0); dragClipMove(pos + m_posStep); // Fall thru... case DragClipPaste: case DragClipPasteDrop: // qtractorScrollView::mousePressEvent(pMouseEvent); return; case DragCurveStep: // One-click change from drag-step to drag-move... m_dragState = DragCurveMove; m_posDrag = m_rectDrag.center(); m_posStep = QPoint(0, 0); dragCurveMove(pos + m_posStep); // Fall thru... case DragCurvePaste: // qtractorScrollView::mousePressEvent(pMouseEvent); return; default: break; } // Automation curve editing modes... if (pMouseEvent->button() == Qt::LeftButton) { if (m_dragCursor == DragCurveNode || (m_bCurveEdit && m_dragCursor == DragNone)) { TrackViewInfo tvi; qtractorTrack *pTrack = trackAt(pos, true, &tvi); if (pTrack && m_dragCursor == DragCurveNode) { qtractorCurve::Node *pNode = nodeAtTrack(pos, pTrack, &tvi); if (pNode) { m_pDragCurve = pTrack->currentCurve(); m_pDragCurveNode = pNode; } } } if (m_bCurveEdit && !bModifier) clearSelect(); if (m_dragCursor == DragCurveNode || (m_bCurveEdit && m_dragCursor == DragNone)) { m_dragState = DragStart;//DragCurveNode; m_posDrag = pos; m_pClipDrag = clipAt(pos, true); qtractorScrollView::viewport()->update(); // qtractorScrollView::mousePressEvent(pMouseEvent); return; } } // Force null state. m_pClipDrag = nullptr; resetDragState(); // We'll need options somehow... qtractorOptions *pOptions = qtractorOptions::getInstance(); // We need a session and a location... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { // Direct snap positioning... unsigned long iFrame = frameSnap( pSession->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); // Which button is being pressed? switch (pMouseEvent->button()) { case Qt::LeftButton: // Remember what and where we'll be dragging/selecting... m_dragState = DragStart; m_posDrag = pos; m_pClipDrag = dragClipStart(pos, modifiers, true, &m_rectDrag); // Should it be selected(toggled)? if (m_pClipDrag && m_dragCursor == DragNone) { // Show that we're about to something... m_dragCursor = m_dragState; setEditCursor(QCursor(Qt::PointingHandCursor)); // Make it (un)selected, right on the file view too... if (!m_bCurveEdit && m_selectMode == SelectClip) selectClip(!bModifier); } // Something got it started?... if (m_pClipDrag == nullptr || (m_pClipDrag && !m_pClipDrag->isClipSelected())) { // Clear any selection out there? if (!m_bCurveEdit && !bModifier) clearSelect(); } qtractorScrollView::viewport()->update(); break; case Qt::MiddleButton: // Mid-button positioning... clearSelect(); if (pOptions && pOptions->bMidButtonModifier) bModifier = !bModifier; // Reverse mid-button role... if (bModifier) { // Play-head positioning... pSession->setPlayHead(iFrame); setPlayHead(iFrame); } else { // Edit cursor (merge) positioning... setEditHead(iFrame); setEditTail(iFrame); } // Not quite a selection, but some visual feedback... m_pTracks->selectionChangeNotify(); break; case Qt::RightButton: // Have sense if pointer falls over a clip... m_pClipDrag = clipAt(pos, true); #if 0 if (m_pClipDrag == nullptr) { // Right-button edit-tail positioning... setEditTail(iFrame); // Not quite a selection, but some visual feedback... m_pTracks->selectionChangeNotify(); } #endif qtractorScrollView::viewport()->update(); // Fall thru... default: break; } } // qtractorScrollView::mousePressEvent(pMouseEvent); } // Handle item selection/dragging -- mouse pointer move. void qtractorTrackView::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { // Are we already moving/dragging something? #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QPoint& pos = viewportToContents(pMouseEvent->position().toPoint()); #else const QPoint& pos = viewportToContents(pMouseEvent->pos()); #endif const Qt::KeyboardModifiers& modifiers = pMouseEvent->modifiers(); switch (m_dragState) { case DragNone: // Try to catch mouse over the fade or resize handles... dragClipStart(pos, modifiers); break; case DragClipMove: case DragClipPaste: dragClipMove(pos + m_posStep); break; case DragClipPasteDrop: dragClipDrop(pos + m_posStep); showClipDropRects(); break; case DragClipFadeIn: case DragClipFadeOut: dragClipFadeMove(pos); break; case DragClipSelectLeft: case DragClipSelectRight: dragClipSelectMove(pos); break; case DragClipRepeatLeft: case DragClipRepeatRight: case DragClipResizeLeft: case DragClipResizeRight: dragClipResizeMove(pos); break; case DragCurveMove: case DragCurvePaste: dragCurveMove(pos + m_posStep); break; case DragCurveNode: dragCurveNode(pos, modifiers); break; case DragSelect: m_rectDrag.setBottomRight(pos); moveRubberBand(&m_pRubberBand, m_rectDrag); ensureVisible(pos.x(), pos.y(), 24, 24); if (m_bCurveEdit) { // Select all current track curve/automation // nodes that fall inside the rubber-band... selectCurveRect(m_rectDrag, SelectRect, selectFlags(modifiers)); } else { // Here we're mainly supposed to select a few bunch // of clips that fall inside the rubber-band... selectClipRect(m_rectDrag, m_selectMode, selectFlags(modifiers)); } showToolTip(m_rectDrag.normalized(), m_iDragClipX); break; case DragStart: if ((m_posDrag - pos).manhattanLength() > QApplication::startDragDistance()) { // Check if we're pointing in some fade-in/out, // resize handle or a automation curve node... m_pClipDrag = dragClipStart(m_posDrag, modifiers, false, &m_rectDrag); if (m_dragCursor != DragNone) { m_dragState = m_dragCursor; if (m_dragState == DragCurveMove) { // DragCurveMove... m_iDragCurveX = (pos.x() - m_posDrag.x()); setEditCursor(QCursor(Qt::SizeHorCursor)); } else if (m_dragState == DragClipFadeIn || m_dragState == DragClipFadeOut) { // DragClipFade... m_iDragClipX = (pos.x() - m_posDrag.x()); setEditCursor(QCursor(Qt::SizeHorCursor)); moveRubberBand(&m_pRubberBand, m_rectHandle); } else if (m_dragState == DragClipSelectLeft || m_dragState == DragClipSelectRight) { // DragClipSelect... m_iDragClipX = (pos.x() - m_posDrag.x()); setEditCursor(QCursor(Qt::SizeHorCursor)); // moveRubberBand(&m_pRubberBand, m_rectHandle); } else if (m_dragState != DragCurveNode && m_pClipDrag) { // DragClipResize... moveRubberBand(&m_pRubberBand, m_rectDrag, 3); } } else if (!m_bCurveEdit || (m_dragCursor != DragCurveNode && m_dragCursor != DragCurveMove)) { // We'll start dragging clip/regions alright... qtractorSession *pSession = qtractorSession::getInstance(); qtractorClipSelect::Item *pClipItem = nullptr; if (m_pClipDrag && pSession) pClipItem = m_pClipSelect->findItem(m_pClipDrag); if (pClipItem && pClipItem->rect.contains(pos)) { const int x = pixelSnap(m_rectDrag.x()); m_iDragClipX = (x - m_rectDrag.x()); m_dragState = m_dragCursor = DragClipMove; setEditCursor(QCursor(Qt::SizeAllCursor)); showClipSelect(); } else { // We'll start rubber banding... m_rectDrag.setTopLeft(m_posDrag); m_rectDrag.setBottomRight(pos); m_dragState = m_dragCursor = DragSelect; setEditCursor(QCursor(Qt::CrossCursor)); // Create the rubber-band if there's none... moveRubberBand(&m_pRubberBand, m_rectDrag); } } qtractorScrollView::viewport()->update(); } // Fall thru... case DragClipStep: case DragClipDrop: default: break; } // qtractorScrollView::mouseMoveEvent(pMouseEvent); } // Handle item selection/dragging -- mouse button release. void qtractorTrackView::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { // qtractorScrollView::mouseReleaseEvent(pMouseEvent); // We'll need options somehow... qtractorOptions *pOptions = qtractorOptions::getInstance(); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { // Direct snap positioning... #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QPoint& pos = viewportToContents(pMouseEvent->position().toPoint()); #else const QPoint& pos = viewportToContents(pMouseEvent->pos()); #endif const unsigned long iFrame = frameSnap( pSession->frameFromPixel(m_posDrag.x() > 0 ? m_posDrag.x() : 0)); // Which mouse state? const Qt::KeyboardModifiers& modifiers = pMouseEvent->modifiers(); bool bModifier = (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)); switch (m_dragState) { case DragSelect: if (m_bCurveEdit) { // Select all current track curve/automation // nodes that fall inside the rubber-band... selectCurveRect(m_rectDrag, SelectRect, selectFlags(modifiers) | SelectCommit); } else { // Here we're mainly supposed to select a few bunch // of clips that fall inside the rubber-band... selectClipRect(m_rectDrag, m_selectMode, selectFlags(modifiers) | SelectCommit); } // For immediate visual feedback... m_pTracks->selectionChangeNotify(); break; case DragClipMove: // Let's move them... moveClipSelect(dragClipMove(pos + m_posStep)); break; case DragClipPaste: // Let's paste them... pasteClipSelect(dragClipMove(pos + m_posStep), modifiers & Qt::ControlModifier); break; case DragClipPasteDrop: // Let's drop-paste them... dropClip(pos + m_posStep); break; case DragClipFadeIn: case DragClipFadeOut: dragClipFadeDrop(pos); break; case DragClipSelectLeft: case DragClipSelectRight: dragClipSelectMove(pos); break; case DragClipRepeatLeft: // HACK: Reaper-like feature shameless plug... if (pos.x() < m_posDrag.x()) { dragClipRepeatLeft(pos); break; } // Fall thru... case DragClipResizeLeft: dragClipResizeDrop(pos, bModifier); break; case DragClipRepeatRight: // HACK: Reaper-like feature shameless plug... if (pos.x() > m_posDrag.x()) { dragClipRepeatRight(pos); break; } // Fall thru... case DragClipResizeRight: dragClipResizeDrop(pos, bModifier); break; case DragCurveMove: // Let's move them... moveCurveSelect(pos + m_posStep); break; case DragCurvePaste: pasteCurveSelect(pos + m_posStep); break; case DragStart: // Deferred left-button positioning... if (m_pClipDrag) { // Make it right on the file view now... if (!m_bCurveEdit && m_selectMode != SelectClip) selectClip(!bModifier); // Nothing more has been deferred... } // As long we're editing curve/automation... if (m_bCurveEdit) dragCurveNode(pos, modifiers); // As long we're not editing anything... if (pOptions && pOptions->bShiftKeyModifier) bModifier = !bModifier; if (m_dragCursor == DragNone && bModifier) { // Direct play-head positioning: // first, set actual engine position... pSession->setPlayHead(iFrame); // Play-head positioning... setPlayHead(iFrame); // Done with (deferred) play-head positioning. } // Fall thru... case DragCurveNode: // Specially when editing curve nodes, // for immediate visual feedback... if (m_pCurveEditCommand && !m_pCurveEditCommand->isEmpty()) { pSession->commands()->push(m_pCurveEditCommand); pSession->updateSession(); m_pCurveEditCommand = nullptr; m_pTracks->dirtyChangeNotify(); } else { m_pTracks->selectionChangeNotify(); } // Fall thru... case DragCurveStep: case DragClipStep: case DragClipDrop: case DragNone: default: break; } } // Force null state. resetDragState(); } // Handle item/clip editing from mouse. void qtractorTrackView::mouseDoubleClickEvent ( QMouseEvent *pMouseEvent ) { qtractorScrollView::mouseDoubleClickEvent(pMouseEvent); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QPoint& pos = viewportToContents(pMouseEvent->position().toPoint()); #else const QPoint& pos = viewportToContents(pMouseEvent->pos()); #endif TrackViewInfo tvi; qtractorTrack *pTrack = trackAt(pos, true, &tvi); if (pTrack) { qtractorCurve::Node *pNode = nodeAtTrack(pos, pTrack, &tvi); if (pNode) { openEditCurveNode(pTrack->currentCurve(), pNode); return; } } // By this time we should have something under... qtractorClip *pClip = m_pClipDrag; if (pClip == nullptr) pClip = clipAt(pos, true); if (pClip) m_pTracks->editClip(pClip); else m_pTracks->selectCurrentTrack(); } // Handle zoom with mouse wheel. void qtractorTrackView::wheelEvent ( QWheelEvent *pWheelEvent ) { if (pWheelEvent->modifiers() & Qt::ControlModifier) { const int delta = pWheelEvent->angleDelta().y(); if (delta > 0) m_pTracks->zoomIn(); else m_pTracks->zoomOut(); } else qtractorScrollView::wheelEvent(pWheelEvent); } // Focus lost event. void qtractorTrackView::focusOutEvent ( QFocusEvent *pFocusEvent ) { if (m_dragState == DragClipStep || m_dragState == DragClipPaste || m_dragState == DragClipPasteDrop || m_dragState == DragCurveStep || m_dragState == DragCurvePaste) resetDragState(); qtractorScrollView::focusOutEvent(pFocusEvent); } // Trap for help/tool-tip events. bool qtractorTrackView::eventFilter ( QObject *pObject, QEvent *pEvent ) { QWidget *pViewport = qtractorScrollView::viewport(); if (static_cast (pObject) == pViewport) { if (pEvent->type() == QEvent::ToolTip && m_bToolTips) { QHelpEvent *pHelpEvent = static_cast (pEvent); if (pHelpEvent) { const QPoint& pos = qtractorScrollView::viewportToContents(pHelpEvent->pos()); TrackViewInfo tvi; qtractorTrack *pTrack = trackAt(pos, false, &tvi); if (pTrack) { qtractorCurve::Node *pNode = nodeAtTrack(pos, pTrack, &tvi); if (pNode) { qtractorCurve *pCurve = pTrack->currentCurve(); QToolTip::showText(pHelpEvent->globalPos(), nodeToolTip(pCurve, pNode), pViewport); return true; } qtractorClip *pClip = clipAtTrack(pos, nullptr, pTrack, &tvi); if (pClip) { QToolTip::showText(pHelpEvent->globalPos(), pClip->toolTip(), pViewport); return true; } } } } else if (pEvent->type() == QEvent::Leave && m_dragState != DragCurvePaste && m_dragState != DragCurveStep && m_dragState != DragClipPasteDrop && m_dragState != DragClipPaste && m_dragState != DragClipStep) { m_dragCursor = DragNone; unsetEditCursor(); return true; } } // Not handled here. return qtractorScrollView::eventFilter(pObject, pEvent); } // Clip file(item) selection convenience method. void qtractorTrackView::selectClip ( bool bReset ) { if (m_pClipDrag == nullptr) return; // Reset selection (conditional)... int iUpdate = 0; QRect rectUpdate = m_pClipSelect->rect(); // Do the selection dance, first... qtractorClipSelect::Item *pClipItem = m_pClipSelect->findItem(m_pClipDrag); const bool bSelect = !(pClipItem && pClipItem->rect.contains(m_posDrag)); if (!bReset) { m_pClipDrag->setClipSelected(false); m_pClipSelect->selectItem(m_pClipDrag, m_rectDrag, bSelect); ++iUpdate; } else if (bSelect || m_selectMode != SelectClip) { m_pClipSelect->reset(); if (bSelect) m_pClipSelect->selectItem(m_pClipDrag, m_rectDrag, true); ++iUpdate; } if (iUpdate > 0) { updateRect(rectUpdate.united(m_pClipSelect->rect())); m_pTracks->selectionChangeNotify(); } // Do the file view selection then... qtractorTrack *pTrack = m_pClipDrag->track(); if (pTrack == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorFiles *pFiles = pMainForm->files(); if (pFiles == nullptr) return; switch (pTrack->trackType()) { case qtractorTrack::Audio: { qtractorAudioClip *pAudioClip = static_cast (m_pClipDrag); if (pAudioClip) pFiles->selectAudioFile(pAudioClip->filename()); break; } case qtractorTrack::Midi: { qtractorMidiClip *pMidiClip = static_cast (m_pClipDrag); if (pMidiClip) pFiles->selectMidiFile( pMidiClip->filename(), pMidiClip->trackChannel()); break; } default: break; } } // Convert selection flags from keyboard modifiers. int qtractorTrackView::selectFlags ( const Qt::KeyboardModifiers modifiers ) { int flags = SelectNone; if ((modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) == 0) flags |= qtractorTrackView::SelectClear; if (modifiers & Qt::ControlModifier) flags |= qtractorTrackView::SelectToggle; return flags; } // Clip selection mode accessors. void qtractorTrackView::setSelectMode ( qtractorTrackView::SelectMode selectMode ) { m_selectMode = selectMode; } qtractorTrackView::SelectMode qtractorTrackView::selectMode (void) const { return m_selectMode; } // Select everything (clips) under a given (rubber-band) rectangle. void qtractorTrackView::selectClipRect ( const QRect& rectDrag, SelectMode selectMode, int flags, SelectEdit selectEdit ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; QRect rect(rectDrag.normalized()); if (rect.left() < 0) rect.setLeft(0); // The precise (snapped) selection frame points... const unsigned long iSelectStart = frameSnap(pSession->frameFromPixel(rect.left())); const unsigned long iSelectEnd = frameSnap(pSession->frameFromPixel(rect.right())); // Special whole-vertical range case... QRect rectRange(0, 0, 0, qtractorScrollView::contentsHeight()); rectRange.setLeft(pSession->pixelFromFrame(iSelectStart)); rectRange.setRight(pSession->pixelFromFrame(iSelectEnd)); if (selectMode == SelectRange) { rect.setTop(rectRange.top()); rect.setBottom(rectRange.height()); } // Reset all selected clips... int iUpdate = 0; QRect rectUpdate = m_pClipSelect->rect(); if ((flags & SelectClear) && (m_pClipSelect->items().count() > 0)) { m_pClipSelect->reset(); ++iUpdate; } // Now find all the clips/regions that fall // in the given rectangular region... int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); if (rect.bottom() < y1) break; if (y2 >= rect.top()) { const int y = y1 + 1; const int h = y2 - y1 - 2; for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { const int x = pSession->pixelFromFrame(pClip->clipStart()); if (x > rect.right()) break; const int w = pSession->pixelFromFrame(pClip->clipLength()); // Test whether the whole clip rectangle // intersects the rubber-band range one... QRect rectClip(x, y, w, h); if (rect.intersects(rectClip)) { if (selectMode != SelectClip) { rectClip = rectRange.intersected(rectClip); pClip->setClipSelect(iSelectStart, iSelectEnd); } m_pClipSelect->selectItem(pClip, rectClip, true); ++iUpdate; } } } pTrack = pTrack->next(); } // Update the screen real estate... if (iUpdate > 0) { updateRect(rectUpdate.united(m_pClipSelect->rect())); // m_pTracks->selectionChangeNotify(); } // That's all very nice but we'll also set edit-range positioning... if (selectEdit & EditHead) setEditHead(iSelectStart); if (selectEdit & EditTail) setEditTail(iSelectEnd); } // Select every clip of a given track-range. void qtractorTrackView::selectClipTrackRange ( qtractorTrack *pTrackPtr, bool bReset ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const unsigned long iSelectStart = pSession->editHead(); const unsigned long iSelectEnd = pSession->editTail(); // Get and select the track's rectangular area // between the edit head and tail points... QRect rect(0, 0, 0, qtractorScrollView::contentsHeight()); rect.setLeft(pSession->pixelFromFrame(iSelectStart)); rect.setRight(pSession->pixelFromFrame(iSelectEnd)); // Reset selection (unconditional)... int iUpdate = 0; QRect rectUpdate = m_pClipSelect->rect(); if (bReset && m_pClipSelect->items().count() > 0) { m_pClipSelect->reset(); ++iUpdate; } int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); if (pTrack == pTrackPtr || pTrackPtr == nullptr) { const int y = y1 + 1; const int h = y2 - y1 - 2; rect.setY(y); rect.setHeight(h); for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { const int x = pSession->pixelFromFrame(pClip->clipStart()); if (x > rect.right()) break; const int w = pSession->pixelFromFrame(pClip->clipLength()); // Test whether the track clip rectangle // intersects the rubber-band range one... QRect rectClip(x, y, w, h); if (rect.intersects(rectClip)) { rectClip = rect.intersected(rectClip); const bool bSelect = !pClip->isClipSelected(); if (bSelect) pClip->setClipSelect(iSelectStart, iSelectEnd); m_pClipSelect->selectItem(pClip, rectClip, bSelect); ++iUpdate; } } if (pTrack == pTrackPtr) break; } pTrack = pTrack->next(); } // This is most probably an overall update... if (iUpdate > 0) { updateRect(rectUpdate.united(m_pClipSelect->rect())); m_pTracks->selectionChangeNotify(); } // Make sure we keep focus... qtractorScrollView::setFocus(); } // Select every clip of a given track. void qtractorTrackView::selectClipTrack ( qtractorTrack *pTrackPtr, bool bReset ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Reset selection (conditional)... int iUpdate = 0; QRect rectUpdate = m_pClipSelect->rect(); if (bReset && m_pClipSelect->items().count() > 0) { // && m_pClipSelect->singleTrack() != pTrackPtr) { m_pClipSelect->reset(); ++iUpdate; } int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); if (pTrack == pTrackPtr) { const int y = y1 + 1; const int h = y2 - y1 - 2; for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { const int x = pSession->pixelFromFrame(pClip->clipStart()); const int w = pSession->pixelFromFrame(pClip->clipLength()); const bool bSelect = !pClip->isClipSelected(); m_pClipSelect->selectItem(pClip, QRect(x, y, w, h), bSelect); ++iUpdate; } break; } pTrack = pTrack->next(); } // This is most probably an overall update... if (iUpdate > 0) { updateRect(rectUpdate.united(m_pClipSelect->rect())); m_pTracks->selectionChangeNotify(); } // Make sure we keep focus... qtractorScrollView::setFocus(); } // Select all clip contents. void qtractorTrackView::selectClipAll (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Reset selection... int iUpdate = 0; QRect rectUpdate = m_pClipSelect->rect(); if (m_pClipSelect->items().count() > 0) { m_pClipSelect->reset(); ++iUpdate; } // Select all clips on all tracks... int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); const int y = y1 + 1; const int h = y2 - y1 - 2; for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { const int x = pSession->pixelFromFrame(pClip->clipStart()); const int w = pSession->pixelFromFrame(pClip->clipLength()); m_pClipSelect->selectItem(pClip, QRect(x, y, w, h)); ++iUpdate; } pTrack = pTrack->next(); } // This is most probably an overall update... if (iUpdate > 0) { updateRect(rectUpdate.united(m_pClipSelect->rect())); m_pTracks->selectionChangeNotify(); } // Make sure we keep focus... qtractorScrollView::setFocus(); } // Invert selection on all tracks and clips. void qtractorTrackView::selectClipInvert (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; int iUpdate = 0; QRect rectUpdate = m_pClipSelect->rect(); // Invert selection... int y1, y2 = 0; for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { y1 = y2; y2 += pTrack->zoomHeight(); const int y = y1 + 1; const int h = y2 - y1 - 2; for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { const int x = pSession->pixelFromFrame(pClip->clipStart()); const int w = pSession->pixelFromFrame(pClip->clipLength()); const bool bSelect = !pClip->isClipSelected(); m_pClipSelect->selectItem(pClip, QRect(x, y, w, h), bSelect); ++iUpdate; } } // This is most probably an overall update... if (iUpdate > 0) { updateRect(rectUpdate.united(m_pClipSelect->rect())); m_pTracks->selectionChangeNotify(); } // Make sure we keep focus... qtractorScrollView::setFocus(); } // Select all clips of given filename and track/channel. void qtractorTrackView::selectClipFile ( qtractorTrack::TrackType trackType, const QString& sFilename, int iTrackChannel, bool bSelect ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Reset selection (conditional)... int iUpdate = 0; QRect rectUpdate = m_pClipSelect->rect(); if (m_pClipSelect->items().count() > 0 && (QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) == 0) { m_pClipSelect->reset(); ++iUpdate; } int x0 = qtractorScrollView::contentsWidth(); int y0 = qtractorScrollView::contentsHeight(); int y1, y2 = 0; for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { y1 = y2; y2 += pTrack->zoomHeight(); if (pTrack->trackType() != trackType) continue; const int y = y1 + 1; const int h = y2 - y1 - 2; for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { if (pClip->filename() != sFilename) continue; if (iTrackChannel >= 0 && trackType == qtractorTrack::Midi) { qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip && int(pMidiClip->trackChannel()) != iTrackChannel) continue; } const int x = pSession->pixelFromFrame(pClip->clipStart()); const int w = pSession->pixelFromFrame(pClip->clipLength()); m_pClipSelect->selectItem(pClip, QRect(x, y, w, h), bSelect); if (x0 > x) x0 = x; if (y0 > y) y0 = y; ++iUpdate; } } // This is most probably an overall update... if (iUpdate > 0) { updateRect(rectUpdate.united(m_pClipSelect->rect())); m_pTracks->selectionChangeNotify(); // Make sure the earliest is barely visible... if (isClipSelected()) ensureVisible(x0, y0, 24, 24); } // Make sure we keep focus... (maybe not:) // qtractorScrollView::setFocus(); } // Select curve nodes under a given (rubber-band) rectangle. void qtractorTrackView::selectCurveRect ( const QRect& rectDrag, SelectMode selectMode, int flags, SelectEdit selectEdit ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; QRect rect(rectDrag.normalized()); // The precise (snapped) selection frame points... const unsigned long iSelectStart = frameSnap(pSession->frameFromPixel(rect.left())); const unsigned long iSelectEnd = frameSnap(pSession->frameFromPixel(rect.right())); // Special whole-vertical range case... rect.setLeft(pSession->pixelFromFrame(iSelectStart)); rect.setRight(pSession->pixelFromFrame(iSelectEnd)); if (selectMode == SelectRange) { rect.setTop(0); rect.setBottom(qtractorScrollView::contentsHeight()); } // Reset all selected nodes... int iUpdate = 0; QRect rectUpdate = m_pCurveSelect->rect(); if ((flags & SelectClear) && (m_pCurveSelect->items().count() > 0)) { m_pCurveSelect->clear(); ++iUpdate; } // Now find all the curve/nodes that fall // in the given rectangular region... const bool bToggle = (flags & SelectToggle); int x1 = qtractorScrollView::contentsX(); int x2 = x1 + (qtractorScrollView::viewport())->width(); if (x1 > rect.left()) x1 = rect.left(); if (x2 < rect.right()) x2 = rect.right(); int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); if (y1 > rect.bottom()) break; if (y2 >= rect.top()) { qtractorCurve *pCurve = pTrack->currentCurve(); if (pCurve && !pCurve->isLocked()) { const int h = y2 - y1 - 2; const unsigned long iFrameStart = pSession->frameFromPixel(x1); const unsigned long iFrameEnd = pSession->frameFromPixel(x2); qtractorCurve::Cursor cursor(pCurve); qtractorCurve::Node *pNode = cursor.seek(iFrameStart); while (pNode && pNode->frame < iFrameEnd) { const int x = pSession->pixelFromFrame(pNode->frame); const int y = y2 - int(cursor.scale(pNode) * float(h)); const bool bSelect = rect.contains(x, y); m_pCurveSelect->selectItem(pCurve, pNode, QRect(x - 4, y - 4, 8, 8), bSelect, bToggle); ++iUpdate; pNode = pNode->next(); } if (m_pCurveSelect->isCurrentCurve(pCurve)) break; } } pTrack = pTrack->next(); } // Update the screen real estate... if (iUpdate > 0) { m_pCurveSelect->update(flags & SelectCommit); updateRect(rectUpdate.united(m_pCurveSelect->rect())); // m_pTracks->selectionChangeNotify(); } // That's all very nice but we'll also set edit-range positioning... if (selectEdit & EditHead) setEditHead(iSelectStart); if (selectEdit & EditTail) setEditTail(iSelectEnd); } // Select every current curve/automation node of a given track-range. void qtractorTrackView::selectCurveTrackRange ( qtractorTrack *pTrackPtr, bool bReset ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const unsigned long iSelectStart = pSession->editHead(); const unsigned long iSelectEnd = pSession->editTail(); // Get and select the track's rectangular area // between the edit head and tail points... QRect rect(0, 0, 0, qtractorScrollView::contentsHeight()); rect.setLeft(pSession->pixelFromFrame(iSelectStart)); rect.setRight(pSession->pixelFromFrame(iSelectEnd)); // Reset selection (unconditional)... if (pTrackPtr && !bReset) bReset = !m_pCurveSelect->isCurrentCurve(pTrackPtr->currentCurve()); int iUpdate = 0; QRect rectUpdate = m_pCurveSelect->rect(); if (bReset && m_pCurveSelect->items().count() > 0) { m_pCurveSelect->clear(); ++iUpdate; } int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); if (pTrack == pTrackPtr || pTrackPtr == nullptr) { qtractorCurve *pCurve = pTrack->currentCurve(); if (pCurve && !pCurve->isLocked()) { const int h = y2 - y1 - 2; rect.setY(y1 + 1); rect.setHeight(h); qtractorCurve::Node *pNode = pCurve->nodes().first(); while (pNode) { const float s = pCurve->scaleFromValue(pNode->value); const int x = pSession->pixelFromFrame(pNode->frame); const int y = y2 - int(s * float(h)); const bool bSelect = rect.contains(x, y); m_pCurveSelect->selectItem(pCurve, pNode, QRect(x - 4, y - 4, 8, 8), bSelect, !bReset); ++iUpdate; pNode = pNode->next(); } } if (pTrackPtr || m_pCurveSelect->isCurrentCurve(pCurve)) break; } pTrack = pTrack->next(); } // This is most probably an overall update... if (iUpdate > 0) { m_pCurveSelect->update(true); updateRect(rectUpdate.united(m_pCurveSelect->rect())); m_pTracks->selectionChangeNotify(); } // Make sure we keep focus... qtractorScrollView::setFocus(); } // Select every current curve/automation node of a given track. void qtractorTrackView::selectCurveTrack ( qtractorTrack *pTrackPtr, bool bReset ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Reset selection (conditional)... if (pTrackPtr && !bReset) bReset = !m_pCurveSelect->isCurrentCurve(pTrackPtr->currentCurve()); int iUpdate = 0; QRect rectUpdate = m_pCurveSelect->rect(); if (bReset && m_pCurveSelect->items().count() > 0) { m_pCurveSelect->clear(); ++iUpdate; } int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); if (pTrack == pTrackPtr || pTrackPtr == nullptr) { qtractorCurve *pCurve = pTrack->currentCurve(); if (pCurve && !pCurve->isLocked()) { const int h = y2 - y1 - 2; qtractorCurve::Node *pNode = pCurve->nodes().first(); while (pNode) { const float s = pCurve->scaleFromValue(pNode->value); const int x = pSession->pixelFromFrame(pNode->frame); const int y = y2 - int(s * float(h)); m_pCurveSelect->selectItem(pCurve, pNode, QRect(x - 4, y - 4, 8, 8), true, !bReset); ++iUpdate; pNode = pNode->next(); } } if (pTrackPtr || m_pCurveSelect->isCurrentCurve(pCurve)) break; } pTrack = pTrack->next(); } // This is most probably an overall update... if (iUpdate > 0) { m_pCurveSelect->update(true); updateRect(rectUpdate.united(m_pCurveSelect->rect())); m_pTracks->selectionChangeNotify(); } // Make sure we keep focus... qtractorScrollView::setFocus(); } // Select all current track curve/automation nodes. void qtractorTrackView::selectCurveAll (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTrack *pCurrentTrack = m_pTracks->currentTrack(); if (pCurrentTrack == nullptr) return; int iUpdate = 0; QRect rectUpdate = m_pCurveSelect->rect(); if (m_pCurveSelect->items().count() > 0) { m_pCurveSelect->clear(); ++iUpdate; } // Select all current track/curve automation nodes... int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); if (pCurrentTrack == pTrack) { qtractorCurve *pCurve = pTrack->currentCurve(); if (pCurve && !pCurve->isLocked()) { const int h = y2 - y1 - 2; qtractorCurve::Node *pNode = pCurve->nodes().first(); while (pNode) { const float s = pCurve->scaleFromValue(pNode->value); const int x = pSession->pixelFromFrame(pNode->frame); const int y = y2 - int(s * float(h)); m_pCurveSelect->selectItem(pCurve, pNode, QRect(x - 4, y - 4, 8, 8)); ++iUpdate; pNode = pNode->next(); } } break; } pTrack = pTrack->next(); } // This is most probably an overall update... if (iUpdate > 0) { m_pCurveSelect->update(true); updateRect(rectUpdate.united(m_pCurveSelect->rect())); m_pTracks->selectionChangeNotify(); } // Make sure we keep focus... qtractorScrollView::setFocus(); } // Invert selection on current track curve/automation nodes. void qtractorTrackView::selectCurveInvert (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; int iUpdate = 0; QRect rectUpdate = m_pCurveSelect->rect(); int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); qtractorCurve *pCurve = pTrack->currentCurve(); if (pCurve && !pCurve->isLocked()) { const int h = y2 - y1 - 2; qtractorCurve::Node *pNode = pCurve->nodes().first(); while (pNode) { const float s = pCurve->scaleFromValue(pNode->value); const int x = pSession->pixelFromFrame(pNode->frame); const int y = y2 - int(s * float(h)); m_pCurveSelect->selectItem(pCurve, pNode, QRect(x - 4, y - 4, 8, 8), true, true); ++iUpdate; pNode = pNode->next(); } if (m_pCurveSelect->isCurrentCurve(pCurve)) break; } pTrack = pTrack->next(); } // This is most probably an overall update... if (iUpdate > 0) { m_pCurveSelect->update(true); updateRect(rectUpdate.united(m_pCurveSelect->rect())); m_pTracks->selectionChangeNotify(); } // Make sure we keep focus... qtractorScrollView::setFocus(); } // Contents update overloaded methods. void qtractorTrackView::updateRect ( const QRect& rect ) { qtractorScrollView::update( // Add some slack margin... QRect(qtractorScrollView::contentsToViewport( rect.topLeft()), rect.size() + QSize(4, 4))); } // Make current selected clip reference. void qtractorTrackView::setCurrentClip ( qtractorClip *pClip ) { m_pClipDrag = pClip; } // Whether there's any clip currently editable. qtractorClip *qtractorTrackView::currentClip (void) const { qtractorClip *pClip = m_pClipDrag; if (pClip == nullptr && isClipSelected()) pClip = m_pClipSelect->items().constBegin().key(); return pClip; } // Clip selection accessor. qtractorClipSelect *qtractorTrackView::clipSelect (void) const { return m_pClipSelect; } // Update whole clip selection. void qtractorTrackView::updateClipSelect (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Clear all selected clips, but don't // reset their own selection state... m_pClipSelect->clear(); // Now find all the clips/regions // that were currently selected... int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); const int y = y1 + 1; const int h = y2 - y1 - 2; for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { pClip->setClipSelect(pClip->clipSelectStart(), pClip->clipSelectEnd()); if (pClip->isClipSelected()) { const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipSelectStart = pClip->clipSelectStart(); const unsigned long iClipSelectEnd = pClip->clipSelectEnd(); const int x = pSession->pixelFromFrame(iClipSelectStart); const int w = pSession->pixelFromFrame(iClipSelectEnd) - x; const unsigned long offset = iClipSelectStart - iClipStart; m_pClipSelect->addItem(pClip, QRect(x, y, w, h), offset); } } pTrack = pTrack->next(); } } // Draw/hide the whole current clip selection. void qtractorTrackView::showClipSelect (void) const { const qtractorClipSelect::ItemList& items = m_pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorClipSelect::Item *pClipItem = iter.value(); QRect rectClip = pClipItem->rect; if (m_bDragSingleTrack) { rectClip.setY(m_iDragSingleTrackY); rectClip.setHeight(m_iDragSingleTrackHeight); } moveRubberBand(&(pClipItem->rubberBand), rectClip, 3); } showToolTip(m_pClipSelect->rect(), m_iDragClipX); qtractorScrollView::viewport()->update(); } void qtractorTrackView::hideClipSelect (void) const { const qtractorClipSelect::ItemList& items = m_pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorRubberBand *pRubberBand = iter.value()->rubberBand; if (pRubberBand && pRubberBand->isVisible()) pRubberBand->hide(); } } // Update whole automation/curve selection. void qtractorTrackView::updateCurveSelect (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; int iUpdate = 0; QRect rectUpdate = m_pCurveSelect->rect(); int y1, y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack) { y1 = y2; y2 += pTrack->zoomHeight(); qtractorCurve *pCurve = pTrack->currentCurve(); if (pCurve && m_pCurveSelect->isCurrentCurve(pCurve)) { const int h = y2 - y1 - 2; qtractorCurve::Node *pNode = pCurve->nodes().first(); while (pNode) { qtractorCurveSelect::Item *pItem = m_pCurveSelect->findItem(pNode); if (pItem) { const float s = pCurve->scaleFromValue(pNode->value); const int x = pSession->pixelFromFrame(pNode->frame); const int y = y2 - int(s * float(h)); pItem->rectNode.setRect(x - 4, y - 4, 8, 8); ++iUpdate; } pNode = pNode->next(); } break; } pTrack = pTrack->next(); } // This is most probably an overall update... if (iUpdate > 0) { m_pCurveSelect->update(true); updateRect(rectUpdate.united(m_pCurveSelect->rect())); // m_pTracks->selectionChangeNotify(); } } // Show selection tooltip... void qtractorTrackView::showToolTip ( const QRect& rect, int dx ) const { if (!m_bToolTips) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return; const unsigned long iFrameStart = frameSnap( pTimeScale->frameFromPixel(qMax(0, rect.left()) + dx)); const unsigned long iFrameEnd = frameSnap( pTimeScale->frameFromPixel(qMax(0, rect.right()) + dx)); QToolTip::showText( QCursor::pos(), tr("Start:\t%1\nEnd:\t%2\nLength:\t%3") .arg(pTimeScale->textFromFrame(iFrameStart)) .arg(pTimeScale->textFromFrame(iFrameEnd)) .arg(pTimeScale->textFromFrame(iFrameStart, true, iFrameEnd - iFrameStart)), qtractorScrollView::viewport()); } // Draw/hide the whole drop rectagle list void qtractorTrackView::updateClipDropRects ( int y, int h ) const { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; int x = 0; const bool bDropSpan = (m_bDropSpan && m_dropType != qtractorTrack::Midi); QListIterator iter(m_dropItems); while (iter.hasNext()) { DropItem *pDropItem = iter.next(); pDropItem->rect.setY(y); pDropItem->rect.setHeight(h); if (bDropSpan) { if (x > 0) pDropItem->rect.moveLeft(x); else x = pDropItem->rect.x(); x = pixelSnap(x + pDropItem->rect.width()); } else { y += h + 4; } } } void qtractorTrackView::showClipDropRects (void) const { QRect rect; QListIterator iter(m_dropItems); while (iter.hasNext()) { DropItem *pDropItem = iter.next(); moveRubberBand(&(pDropItem->rubberBand), pDropItem->rect, 3); rect = rect.united(pDropItem->rect); } showToolTip(rect, m_iDragClipX); } void qtractorTrackView::hideClipDropRects (void) const { QListIterator iter(m_dropItems); while (iter.hasNext()) { qtractorRubberBand *pRubberBand = iter.next()->rubberBand; if (pRubberBand && pRubberBand->isVisible()) pRubberBand->hide(); } } // Show and move rubber-band item. void qtractorTrackView::moveRubberBand ( qtractorRubberBand **ppRubberBand, const QRect& rectDrag, int thick ) const { QRect rect(rectDrag.normalized()); // Horizontal adjust... rect.translate(m_iDragClipX, 0); // Convert rectangle into view coordinates... rect.moveTopLeft(qtractorScrollView::contentsToViewport(rect.topLeft())); // Make sure the rectangle doesn't get too off view, // which it would make it sluggish :) if (rect.left() < 0) rect.setLeft(-8); if (rect.right() > qtractorScrollView::width()) rect.setRight(qtractorScrollView::width() + 8); // Create the rubber-band if there's none... qtractorRubberBand *pRubberBand = *ppRubberBand; if (pRubberBand == nullptr) { pRubberBand = new qtractorRubberBand( QRubberBand::Rectangle, qtractorScrollView::viewport(), thick); #if 0 QPalette pal(pRubberBand->palette()); pal.setColor(pRubberBand->foregroundRole(), pal.highlight().color()); pRubberBand->setPalette(pal); pRubberBand->setBackgroundRole(QPalette::NoRole); #endif // Do not ever forget to set it back... *ppRubberBand = pRubberBand; } // Just move it pRubberBand->setGeometry(rect); // Ah, and make it visible, of course... if (!pRubberBand->isVisible()) pRubberBand->show(); } // Check whether we're up to drag something on a track or one of its clips. qtractorClip *qtractorTrackView::dragClipStart ( const QPoint& pos, const Qt::KeyboardModifiers& modifiers, bool bSelectTrack, QRect *pClipRect ) { TrackViewInfo tvi; qtractorTrack *pTrack = trackAt(pos, bSelectTrack, &tvi); if (pTrack == nullptr) return nullptr; qtractorCurve::Node *pNode = m_pDragCurveNode; if (pNode == nullptr) pNode = nodeAtTrack(pos, pTrack, &tvi); if (pNode) { if (m_bCurveEdit && m_dragState == DragStart && (m_pCurveSelect->items().count() > 1) && (m_pCurveSelect->findItem(pNode))) m_dragCursor = DragCurveMove; else m_dragCursor = DragCurveNode; setEditCursor(QCursor(Qt::PointingHandCursor)); return clipAt(pos, bSelectTrack); } else { unsetEditCursor(); } qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return nullptr; #if 0 QRect rectClip; qtractorClip *pClip = clipAtTrack(pos, &rectClip, pTrack, &tvi); if (pClip && dragClipStartEx(pos, modifiers, pClip, rectClip)) return pClip; #else if (m_pSessionCursor == nullptr) return nullptr; qtractorClip *pClip = m_pSessionCursor->clip(tvi.trackIndex); if (pClip == nullptr) pClip = pTrack->clips().first(); if (pClip == nullptr) return nullptr; qtractorClip *pClipAt = nullptr; QRect rectClip(tvi.trackRect); while (pClip && pClip->clipStart() < tvi.trackEnd) { const int x = pSession->pixelFromFrame(pClip->clipStart()); const int w = pSession->pixelFromFrame(pClip->clipLength()); if (pos.x() >= x && x + w >= pos.x()) { pClipAt = pClip; rectClip.setX(x); rectClip.setWidth(w); if (pClipRect) *pClipRect = rectClip; if (dragClipStartEx(pos, modifiers, pClipAt, rectClip)) return pClipAt; } pClip = pClip->next(); } #endif // Reset cursor if any persist around. if (m_dragCursor != DragNone) { m_dragCursor = DragNone; unsetEditCursor(); } return pClipAt; } // Check whether we're up to drag a clip fade-in/out or resize handles. bool qtractorTrackView::dragClipStartEx ( const QPoint& pos, const Qt::KeyboardModifiers& modifiers, qtractorClip *pClip, const QRect& rectClip ) { qtractorTrack *pTrack = pClip->track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; // Fade-in handle check... m_rectHandle.setRect(rectClip.left() + 1 + pSession->pixelFromFrame(pClip->fadeInLength()), rectClip.top() + 1, 8, 8); if (m_rectHandle.contains(pos)) { m_dragCursor = DragClipFadeIn; setEditCursor(QCursor(Qt::PointingHandCursor)); return true; } // Fade-out handle check... m_rectHandle.setRect(rectClip.right() - 8 - pSession->pixelFromFrame(pClip->fadeOutLength()), rectClip.top() + 1, 8, 8); if (m_rectHandle.contains(pos)) { m_dragCursor = DragClipFadeOut; setEditCursor(QCursor(Qt::PointingHandCursor)); return true; } int x; // Resize-left check... x = rectClip.left(); if (pos.x() >= x - 4 && x + 4 >= pos.x()) { m_dragCursor = (modifiers & Qt::ControlModifier ? DragClipRepeatLeft : DragClipResizeLeft); setEditCursor(QCursor(Qt::SizeHorCursor)); return true; } // Resize-right check... x = rectClip.right(); if (pos.x() >= x - 4 && x + 4 >= pos.x()) { m_dragCursor = (modifiers & Qt::ControlModifier ? DragClipRepeatRight : DragClipResizeRight); setEditCursor(QCursor(Qt::SizeHorCursor)); return true; } // Select-left check... x = pSession->pixelFromFrame(pClip->clipSelectStart()); if (pos.x() >= x - 4 && x + 4 >= pos.x()) { m_dragCursor = DragClipSelectLeft; setEditCursor(QCursor(Qt::SizeHorCursor)); return true; } // Select-right check... x = pSession->pixelFromFrame(pClip->clipSelectEnd()); if (pos.x() >= x - 4 && x + 4 >= pos.x()) { m_dragCursor = DragClipSelectRight; setEditCursor(QCursor(Qt::SizeHorCursor)); return true; } return false; } // Clip fade-in/out handle drag-moving parts. void qtractorTrackView::dragClipFadeMove ( const QPoint& pos ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Always change horizontally wise... const int x0 = pixelSnap(pos.x()); int dx = (x0 - m_posDrag.x()); if (m_rectHandle.left() + dx < m_rectDrag.left()) dx = m_rectDrag.left() - m_rectHandle.left(); else if (m_rectHandle.right() + dx > m_rectDrag.right()) dx = m_rectDrag.right() - m_rectHandle.right(); m_iDragClipX = dx; moveRubberBand(&m_pRubberBand, m_rectHandle); ensureVisible(pos.x(), pos.y(), 24, 24); // Prepare to update the whole view area... updateRect(m_rectDrag); // Show fade-in/out tooltip.. QRect rect(m_rectDrag); if (m_dragState == DragClipFadeIn) rect.setRight(m_rectHandle.left() + m_iDragClipX); else if (m_dragState == DragClipFadeOut) rect.setLeft(m_rectHandle.right() + m_iDragClipX); showToolTip(rect, 0); } // Clip fade-in/out handle settler. void qtractorTrackView::dragClipFadeDrop ( const QPoint& pos ) { if (m_pClipDrag == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; dragClipFadeMove(pos); // We'll build a command... qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("clip %1").arg( m_dragState == DragClipFadeIn ? tr("fade-in") : tr("fade-out"))); if (m_dragState == DragClipFadeIn) { pClipCommand->fadeInClip(m_pClipDrag, pSession->frameFromPixel( m_rectHandle.left() + m_iDragClipX - m_rectDrag.left()), m_pClipDrag->fadeInType()); } else if (m_dragState == DragClipFadeOut) { pClipCommand->fadeOutClip(m_pClipDrag, pSession->frameFromPixel( m_rectDrag.right() - m_iDragClipX - m_rectHandle.right()), m_pClipDrag->fadeOutType()); } // Reset state for proper redrawing... m_dragState = DragNone; // Put it in the form of an undoable command... pSession->execute(pClipCommand); } // Clip select edge drag-moving and setler. void qtractorTrackView::dragClipSelectMove ( const QPoint& pos ) { qtractorClip *pClip = m_pClipDrag; if (pClip == nullptr) return; if (!pClip->isClipSelected()) return; qtractorTrack *pTrack = pClip->track(); if (pTrack == nullptr) return; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; int iUpdate = 0; int x = pixelSnap(pos.x()); if (m_dragState == DragClipSelectLeft) { const unsigned long iClipSelectEnd = pClip->clipSelectEnd(); const int x2 = pSession->pixelFromFrame(iClipSelectEnd); if (x < x2) { const unsigned long iClipSelectStart = (x > 0 ? pSession->frameFromPixel(x) : 0); pClip->setClipSelect(iClipSelectStart, iClipSelectEnd); ++iUpdate; } } else if (m_dragState == DragClipSelectRight) { const unsigned long iClipSelectStart = pClip->clipSelectStart(); const int x1 = pSession->pixelFromFrame(iClipSelectStart); if (x > x1) { const unsigned long iClipSelectEnd = pSession->frameFromPixel(x); pClip->setClipSelect(iClipSelectStart, iClipSelectEnd); ++iUpdate; } } if (iUpdate > 0) { updateClipSelect(); qtractorScrollView::viewport()->update(); } ensureVisible(pos.x(), pos.y(), 24, 24); } // Clip resize drag-moving handler. void qtractorTrackView::dragClipResizeMove ( const QPoint& pos ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Always change horizontally wise... int x = 0; const int dx = (pos.x() - m_posDrag.x()); QRect rect(m_rectDrag); if (m_dragState == DragClipResizeLeft || m_dragState == DragClipRepeatLeft) { if (rect.left() > -(dx)) x = pixelSnap(rect.left() + dx); if (x < 0) x = 0; else if (x > rect.right() - 8) x = rect.right() - 8; rect.setLeft(x); } else if (m_dragState == DragClipResizeRight || m_dragState == DragClipRepeatRight) { if (rect.right() > -(dx)) x = pixelSnap(rect.right() + dx); if (x < rect.left() + 8) x = rect.left() + 8; rect.setRight(x); } moveRubberBand(&m_pRubberBand, rect, 3); showToolTip(rect, 0); ensureVisible(pos.x(), pos.y(), 24, 24); } // Clip resize/repeat drag-moving settler. void qtractorTrackView::dragClipResizeDrop ( const QPoint& pos, bool bTimeStretch ) { if (m_pClipDrag == nullptr) return; if (!m_pClipDrag->queryEditor()) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // We'll build a command... qtractorClipCommand *pClipCommand = new qtractorClipCommand( bTimeStretch ? tr("clip stretch") : tr("clip resize")); unsigned long iClipStart = m_pClipDrag->clipStart(); unsigned long iClipOffset = m_pClipDrag->clipOffset(); unsigned long iClipLength = m_pClipDrag->clipLength(); // Always change horizontally wise... int x = 0; const int dx = (pos.x() - m_posDrag.x()); if (m_dragState == DragClipResizeLeft) { unsigned long iClipDelta; x = m_rectDrag.left() + dx; if (x < 0) x = 0; else if (x > m_rectDrag.right() - 8) x = m_rectDrag.right() - 8; iClipStart = frameSnap(pSession->frameFromPixel(x)); if (m_pClipDrag->clipStart() > iClipStart) { iClipDelta = (m_pClipDrag->clipStart() - iClipStart); if (!bTimeStretch) { if (iClipOffset > iClipDelta) iClipOffset -= iClipDelta; else iClipOffset = 0; } iClipLength += iClipDelta; } else { iClipDelta = (iClipStart - m_pClipDrag->clipStart()); if (!bTimeStretch) iClipOffset += iClipDelta; iClipLength -= iClipDelta; } } else if (m_dragState == DragClipResizeRight) { x = m_rectDrag.right() + dx; if (x < m_rectDrag.left() + 8) x = m_rectDrag.left() + 8; iClipLength = frameSnap(pSession->frameFromPixel(x)) - iClipStart; } // Time stretching... float fTimeStretch = 0.0f; if (bTimeStretch && m_pClipDrag->track()) { float fOldTimeStretch = 1.0f; if ((m_pClipDrag->track())->trackType() == qtractorTrack::Audio) { qtractorAudioClip *pAudioClip = static_cast (m_pClipDrag); if (pAudioClip) fOldTimeStretch = pAudioClip->timeStretch(); } fTimeStretch = (float(iClipLength) * fOldTimeStretch) / float(m_pClipDrag->clipLength()); } // Declare the clip resize parcel... pClipCommand->resizeClip(m_pClipDrag, iClipStart, iClipOffset, iClipLength, fTimeStretch); // Put it in the form of an undoable command... pSession->execute(pClipCommand); } // Clip resize/repeat handler (over to the left). void qtractorTrackView::dragClipRepeatLeft ( const QPoint& pos ) { if (m_pClipDrag == nullptr) return; if (!m_pClipDrag->queryEditor()) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTrack *pTrack = m_pClipDrag->track(); if (pTrack == nullptr) return; // We'll build a command... qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("clip repeat")); unsigned long iClipStart = m_pClipDrag->clipStart(); unsigned long iClipOffset = m_pClipDrag->clipOffset(); unsigned long iClipLength = m_pClipDrag->clipLength(); // About to repeat horizontally-wise, to the left... const int x0 = pSession->pixelFromFrame(iClipStart); const int x2 = x0 + (pos.x() - m_posDrag.x()); int x = x0; while (x > x2) { // Let's remember this, just in case... const unsigned long iOldClipStart = iClipStart; // Next clone starts backward... if (iClipStart > iClipLength) { iClipStart = frameSnap(iClipStart - iClipLength); // Get next clone pixel position... x = pSession->pixelFromFrame(iClipStart); if (x < x2) { const unsigned long iClipStart2 = frameSnap(pSession->frameFromPixel(x2)); const unsigned long iClipDelta2 = iClipStart2 - iClipStart; iClipStart = iClipStart2; iClipOffset += iClipDelta2; iClipLength -= iClipDelta2; } } else { // Get next clone pixel position... if (x2 > 0) { const unsigned long iClipStart2 = frameSnap(pSession->frameFromPixel(x2)); const unsigned long iClipLength2 = iClipStart - iClipStart2; iClipStart = iClipStart2; iClipOffset += iClipLength - iClipLength2; iClipLength = iClipLength2; } else { const unsigned long iClipStart2 = iClipStart + iClipLength; const unsigned long iClipDelta2 = frameSnap(iClipStart2) - iClipStart2; const unsigned long iClipLength2 = iClipStart - iClipDelta2; iClipStart = 0; iClipOffset += iClipLength - iClipLength2; iClipLength = iClipLength2; } // break out next... x = x2; } // Now, its imperative to make a proper clone... qtractorClip *pNewClip = cloneClip(m_pClipDrag); // Add the new cloned clip... if (pNewClip) { // HACK: convert/override MIDI clip-offset, length // times across potential tempo/time-sig changes... if (pTrack->trackType() == qtractorTrack::Midi) { const unsigned long iClipStartTime = pSession->tickFromFrame(iClipStart); const unsigned long iClipOffsetTime = pSession->tickFromFrameRange( iOldClipStart, iOldClipStart + iClipOffset, true); const unsigned long iClipLengthTime = pSession->tickFromFrameRange( iOldClipStart, iOldClipStart + iClipLength); iClipOffset = pSession->frameFromTickRange( iClipStartTime, iClipStartTime + iClipOffsetTime, true); iClipLength = pSession->frameFromTickRange( iClipStartTime, iClipStartTime + iClipLengthTime); } // Clone clip properties... pNewClip->setClipStart(iClipStart); pNewClip->setClipOffset(iClipOffset); pNewClip->setClipLength(iClipLength); pNewClip->setFadeInLength(m_pClipDrag->fadeInLength()); pNewClip->setFadeOutLength(m_pClipDrag->fadeOutLength()); // Add it, ofc. pClipCommand->addClip(pNewClip, pTrack); } } // Put it in the form of an undoable command... if (!pClipCommand->isEmpty()) pSession->execute(pClipCommand); else delete pClipCommand; } // Clip resize/repeat handler (over to the right). void qtractorTrackView::dragClipRepeatRight ( const QPoint& pos ) { if (m_pClipDrag == nullptr) return; if (!m_pClipDrag->queryEditor()) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTrack *pTrack = m_pClipDrag->track(); if (pTrack == nullptr) return; // We'll build a command... qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("clip repeat")); unsigned long iClipStart = m_pClipDrag->clipStart(); unsigned long iClipOffset = m_pClipDrag->clipOffset(); unsigned long iClipLength = m_pClipDrag->clipLength(); // Always repeat horizontally-wise, to the right... const int x0 = pSession->pixelFromFrame(iClipStart + iClipLength); const int x2 = x0 + (pos.x() - m_posDrag.x()); int x = x0; while (x < x2) { // Let's remember this, just in case... const unsigned long iOldClipStart = iClipStart; // Next clone starts forward... iClipStart = frameSnap(iClipStart + iClipLength); // Get next clone pixel position... x = pSession->pixelFromFrame(iClipStart + iClipLength); if (x > x2) { const unsigned long iClipStart2 = frameSnap(pSession->frameFromPixel(x2)); iClipLength = iClipStart2 - iClipStart; } // HACK: Avoid extraneous clip-lengths... if (iClipLength < 1) break; // Now, its imperative to make a proper clone... qtractorClip *pNewClip = cloneClip(m_pClipDrag); // Add the new cloned clip... if (pNewClip) { // HACK: convert/override MIDI clip-offset, length // times across potential tempo/time-sig changes... if (pTrack->trackType() == qtractorTrack::Midi) { const unsigned long iClipStartTime = pSession->tickFromFrame(iClipStart); const unsigned long iClipOffsetTime = pSession->tickFromFrameRange( iOldClipStart, iOldClipStart + iClipOffset, true); const unsigned long iClipLengthTime = pSession->tickFromFrameRange( iOldClipStart, iOldClipStart + iClipLength); iClipOffset = pSession->frameFromTickRange( iClipStartTime, iClipStartTime + iClipOffsetTime, true); iClipLength = pSession->frameFromTickRange( iClipStartTime, iClipStartTime + iClipLengthTime); } // Clone clip properties... pNewClip->setClipStart(iClipStart); pNewClip->setClipOffset(iClipOffset); pNewClip->setClipLength(iClipLength); pNewClip->setFadeInLength(m_pClipDrag->fadeInLength()); pNewClip->setFadeOutLength(m_pClipDrag->fadeOutLength()); // Add it, ofc. pClipCommand->addClip(pNewClip, pTrack); } } // Put it in the form of an undoable command... if (!pClipCommand->isEmpty()) pSession->execute(pClipCommand); else delete pClipCommand; } // Automation curve node drag-move methods. void qtractorTrackView::dragCurveNode ( const QPoint& pos, const Qt::KeyboardModifiers& modifiers ) { TrackViewInfo tvi; qtractorTrack *pTrack = trackAt(pos, false, &tvi); if (pTrack == nullptr) return; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList == nullptr) return; qtractorCurve *pCurve = pCurveList->currentCurve(); if (pCurve == nullptr) return; if (pCurve->isLocked()) return; if (m_pDragCurve && m_pDragCurve != pCurve) return; const unsigned long iFrame = pCurve->cursor().frame(); if (m_pCurveEditCommand == nullptr) m_pCurveEditCommand = new qtractorCurveEditCommand(pCurve); qtractorCurve::Node *pNode = m_pDragCurveNode; m_pDragCurveNode = nullptr; if (pNode && m_dragState == DragCurveNode) { m_pCurveSelect->removeItem(pNode); m_pCurveEditCommand->removeNode(pNode); pCurve->unlinkNode(pNode); pNode = nullptr; } if (m_pDragCurve == nullptr) { m_pDragCurve = pCurve; m_dragCursor = DragCurveNode; setEditCursor(QCursor(Qt::PointingHandCursor)); } ensureVisible(pos.x(), pos.y(), 24, 24); const int h = tvi.trackRect.height(); const int y2 = tvi.trackRect.bottom() + 1; if (pNode == nullptr) { qtractorCurveEditList edits(pCurve); const unsigned long frame = frameSnap( pSession->frameFromPixel(pos.x() < 0 ? 0 : pos.x())); const float value = pCurve->valueFromScale(float(y2 - pos.y()) / float(h)); pNode = pCurve->addNode(frame, value, &edits); m_pCurveEditCommand->addEditList(&edits); } if (pNode) { const float s = pCurve->scaleFromValue(pNode->value); const int x = pSession->pixelFromFrame(pNode->frame); const int y = y2 - int(s * float(h)); const QRect rectUpdate = m_pCurveSelect->rect(); m_pCurveSelect->selectItem(pCurve, pNode, QRect(x - 4, y - 4, 8, 8), true, modifiers & Qt::ControlModifier); m_pCurveSelect->update(true); updateRect(rectUpdate.united(m_pCurveSelect->rect())); m_pDragCurveNode = pNode; if (m_bToolTips) { QWidget *pViewport = qtractorScrollView::viewport(); QToolTip::showText(pViewport->mapToGlobal(contentsToViewport(pos)), nodeToolTip(m_pDragCurve, m_pDragCurveNode), pViewport); } } pCurve->cursor().seek(iFrame); } // Common tool-tip builder for automation nodes. QString qtractorTrackView::nodeToolTip ( qtractorCurve *pCurve, qtractorCurve::Node *pNode) const { QString sToolTip; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale) { sToolTip = QString("(%2, %3)") .arg(pTimeScale->textFromFrame(pNode->frame)) .arg(pNode->value); if (pCurve) { qtractorSubject *pSubject = pCurve->subject(); if (pSubject) { sToolTip = QString("%1\n%2") .arg(pSubject->name()) .arg(sToolTip); } } } } return sToolTip; } // Reset drag/select/move state. void qtractorTrackView::resetDragState (void) { // To remember what we were doing... const DragState dragState = m_dragState; // Should fallback mouse cursor... if (m_dragCursor != DragNone) unsetEditCursor(); if (m_dragState == DragClipStep) m_pClipSelect->reset(); if (m_dragState == DragCurveStep) m_pCurveSelect->clear(); if (m_dragState == DragClipSelectLeft || m_dragState == DragClipSelectRight || m_dragState == DragClipRepeatLeft || m_dragState == DragClipRepeatRight || m_dragState == DragClipResizeLeft || m_dragState == DragClipResizeRight || m_dragState == DragClipPasteDrop || m_dragState == DragClipPaste || m_dragState == DragClipStep || m_dragState == DragClipMove || m_dragState == DragCurvePaste || m_dragState == DragCurveStep || m_dragState == DragCurveMove) { m_iEditCurveNodeDirty = 0; closeEditCurveNode(); updateContents(); } // Force null state, now. m_dragState = DragNone; m_dragCursor = DragNone; m_iDragClipX = 0; // m_pClipDrag = nullptr; m_posStep = QPoint(0, 0); // No pasting nomore. m_iPasteCount = 0; m_iPastePeriod = 0; // Single track dragging reset. m_bDragSingleTrack = false; m_iDragSingleTrackY = 0; m_iDragSingleTrackHeight = 0; // Automation curve stuff reset. m_pDragCurve = nullptr; m_pDragCurveNode = nullptr; m_iDragCurveX = 0; if (m_pCurveEditCommand) delete m_pCurveEditCommand; m_pCurveEditCommand = nullptr; // If we were moving clips around, // just hide selection, of course. hideClipSelect(); // Just hide the rubber-band... if (m_pRubberBand) { m_pRubberBand->hide(); delete m_pRubberBand; m_pRubberBand = nullptr; } // If we were dragging fade-slope lines, refresh... if (dragState == DragClipFadeIn || dragState == DragClipFadeOut) updateRect(m_rectDrag); // No dropping files, whatsoever. qDeleteAll(m_dropItems); m_dropItems.clear(); m_dropType = qtractorTrack::None; } // Keyboard event handler. void qtractorTrackView::keyPressEvent ( QKeyEvent *pKeyEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTrackView::keyPressEvent(%d)", pKeyEvent->key()); #endif const Qt::KeyboardModifiers& modifiers = pKeyEvent->modifiers(); const int iKey = pKeyEvent->key(); switch (iKey) { case Qt::Key_Insert: // Aha, joking :) case Qt::Key_Return: if (m_dragState == DragClipStep) moveClipSelect(dragClipMove(m_posDrag + m_posStep)); else if (m_dragState == DragCurveStep) moveCurveSelect(m_posDrag + m_posStep); else { const QPoint& pos = qtractorScrollView::viewportToContents( qtractorScrollView::viewport()->mapFromGlobal(QCursor::pos())); if (m_dragState == DragClipMove) moveClipSelect(dragClipMove(pos + m_posStep)); else if (m_dragState == DragClipPaste) pasteClipSelect(dragClipMove(pos + m_posStep), modifiers & Qt::ControlModifier); else if (m_dragState == DragClipPasteDrop) dropClip(pos + m_posStep); else if (m_dragState == DragCurveMove) moveCurveSelect(pos + m_posStep); else if (m_dragState == DragCurvePaste) pasteCurveSelect(pos + m_posStep); } // Fall thru... case Qt::Key_Escape: // HACK: Force selection clearance! m_dragState = (m_bCurveEdit ? DragCurveStep : DragClipStep); resetDragState(); break; case Qt::Key_Home: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos(0, 0); } else { qtractorScrollView::setContentsPos( 0, qtractorScrollView::contentsY()); } break; case Qt::Key_End: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsWidth(), qtractorScrollView::contentsHeight()); } else { qtractorScrollView::setContentsPos( qtractorScrollView::contentsWidth(), qtractorScrollView::contentsY()); } break; case Qt::Key_Left: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX() - qtractorScrollView::width(), qtractorScrollView::contentsY()); } else if (!keyStep(iKey, modifiers)) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX() - 16, qtractorScrollView::contentsY()); } break; case Qt::Key_Right: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX() + qtractorScrollView::width(), qtractorScrollView::contentsY()); } else if (!keyStep(iKey, modifiers)) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX() + 16, qtractorScrollView::contentsY()); } break; case Qt::Key_Up: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), qtractorScrollView::contentsY() - qtractorScrollView::height()); } else if (!keyStep(iKey, modifiers)) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), qtractorScrollView::contentsY() - 16); } break; case Qt::Key_Down: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), qtractorScrollView::contentsY() + qtractorScrollView::height()); } else if (!keyStep(iKey, modifiers)) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), qtractorScrollView::contentsY() + 16); } break; case Qt::Key_PageUp: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), 16); } else { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), qtractorScrollView::contentsY() - qtractorScrollView::height()); } break; case Qt::Key_PageDown: if (modifiers & Qt::ControlModifier) { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), qtractorScrollView::contentsHeight()); } else { qtractorScrollView::setContentsPos( qtractorScrollView::contentsX(), qtractorScrollView::contentsY() + qtractorScrollView::height()); } break; default: qtractorScrollView::keyPressEvent(pKeyEvent); break; } } // Keyboard step handler. bool qtractorTrackView::keyStep ( int iKey, const Qt::KeyboardModifiers& modifiers ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Set initial bound conditions... if (m_dragState == DragNone) { if (isClipSelected() || !m_dropItems.isEmpty()) { m_dragState = m_dragCursor = DragClipStep; m_rectDrag = m_pClipSelect->rect(); m_posDrag = m_rectDrag.topLeft(); m_posStep = QPoint(0, 0); m_iDragClipX = pixelSnap(m_rectDrag.x()) - m_rectDrag.x(); setEditCursor(QCursor(Qt::SizeAllCursor)); } else if (isCurveSelected() && m_bCurveEdit && iKey != Qt::Key_Up && iKey != Qt::Key_Down) { m_dragState = m_dragCursor = DragCurveStep; m_rectDrag = m_pCurveSelect->rect(); m_posDrag = m_rectDrag.topLeft(); m_posStep = QPoint(0, 0); m_iDragCurveX = pixelSnap(m_rectDrag.x()) - m_rectDrag.x(); setEditCursor(QCursor(Qt::SizeHorCursor)); } } // Now to say the truth... const bool bClipStep = m_dragState == DragClipMove || m_dragState == DragClipStep || m_dragState == DragClipPaste || m_dragState == DragClipPasteDrop; const bool bCurveStep = m_dragState == DragCurveMove || m_dragState == DragCurveStep || m_dragState == DragCurvePaste; if (!bClipStep && !bCurveStep) return false; // Determine vertical step... if (iKey == Qt::Key_Up || iKey == Qt::Key_Down) { if (bClipStep) { qtractorTrack *pTrack = m_pClipSelect->singleTrack(); if (pTrack) { const qtractorTrack::TrackType trackType = pTrack->trackType(); int iVerticalStep = 0; pTrack = m_pTracks->currentTrack(); if (iKey == Qt::Key_Up) { if (pTrack) pTrack = pTrack->prev(); else pTrack = pSession->tracks().last(); while (pTrack && pTrack->trackType() != trackType) { iVerticalStep -= pTrack->zoomHeight(); pTrack = pTrack->prev(); } if (pTrack && pTrack->trackType() == trackType) iVerticalStep -= pTrack->zoomHeight(); else iVerticalStep = 0; } else { if (pTrack) { iVerticalStep += pTrack->zoomHeight(); pTrack = pTrack->next(); } while (pTrack && pTrack->trackType() != trackType) { iVerticalStep += pTrack->zoomHeight(); pTrack = pTrack->next(); } } const int y0 = m_posDrag.y(); const int y1 = y0 + m_posStep.y() + iVerticalStep; m_posStep.setY((y1 < 0 ? 0 : y1) - y0); } } } else // Determine horizontal step... if (iKey == Qt::Key_Left || iKey == Qt::Key_Right) { int iHorizontalStep = 0; const int x0 = m_posDrag.x(); int x1 = x0 + m_posStep.x(); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekPixel(x1); if (modifiers & Qt::ShiftModifier) { iHorizontalStep = pNode->pixelsPerBeat() * pNode->beatsPerBar; } else { const unsigned short iSnapPerBeat = pSession->snapPerBeat(); if (iSnapPerBeat > 0) iHorizontalStep = pNode->pixelsPerBeat() / iSnapPerBeat; } if (iHorizontalStep < 4) iHorizontalStep = 4; if (iKey == Qt::Key_Left) x1 -= iHorizontalStep; else x1 += iHorizontalStep; m_posStep.setX(pixelSnap(x1 < 0 ? 0 : x1) - x0); } // Early sanity check... QRect rect = m_rectDrag; if (bClipStep && m_dragState != DragClipPasteDrop) rect = m_pClipSelect->rect(); else if (bCurveStep) rect = m_pCurveSelect->rect(); QPoint pos = m_posDrag; if (m_dragState != DragClipStep && m_dragState != DragCurveStep) { pos = qtractorScrollView::viewportToContents( qtractorScrollView::viewport()->mapFromGlobal(QCursor::pos())); } int x2 = - pos.x(); int y2 = - pos.y(); if (m_dragState != DragClipStep && m_dragState != DragCurveStep) { x2 += (m_posDrag.x() - rect.x()); y2 += (m_posDrag.y() - rect.y()); } if (m_posStep.x() < x2) { m_posStep.setX (x2); } else { x2 += qtractorScrollView::contentsWidth() - (rect.width() >> 1); if (m_posStep.x() > x2) m_posStep.setX (x2); } if (bClipStep) { if (m_posStep.y() < y2) { m_posStep.setY (y2); } else { y2 += qtractorScrollView::contentsHeight() - (rect.height() >> 1); if (m_posStep.y() > y2) m_posStep.setY (y2); } } // Do our deeds (flag we're key-steppin')... if (m_dragState == DragClipPasteDrop) { dragClipDrop(pos + m_posStep, true); showClipDropRects(); } else if (bClipStep) { dragClipMove(pos + m_posStep, true); } else if (bCurveStep) { dragCurveMove(pos + m_posStep, true); } return true; } // Make given contents position visible in view. void qtractorTrackView::ensureVisible ( int cx, int cy, int mx, int my ) { const int w = qtractorScrollView::width(); const int wm = (w >> 3); if (cx > w - wm) updateContentsWidth(cx + wm); qtractorScrollView::ensureVisible(cx, cy, mx, my); } // Make given frame position visible in view. void qtractorTrackView::ensureVisibleFrame ( unsigned long iFrame ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { const int x0 = qtractorScrollView::contentsX(); const int y = qtractorScrollView::contentsY(); const int w = m_pixmap.width(); const int w3 = w - (w >> 3); int x = pSession->pixelFromFrame(iFrame); if (x < x0) x -= w3; else if (x > x0 + w3) x += w3; ensureVisible(x, y, 24, 0); // qtractorScrollView::setFocus(); } } // Session cursor accessor. qtractorSessionCursor *qtractorTrackView::sessionCursor (void) const { return m_pSessionCursor; } // Vertical line positioning. void qtractorTrackView::drawPositionX ( int& iPositionX, int x, bool bSyncView ) { // Update track-view position... const int x0 = qtractorScrollView::contentsX(); const int w = qtractorScrollView::width(); const int h = qtractorScrollView::height(); const int wm = (w >> 3); // Time-line header extents... const int h0 = m_pTracks->trackTime()->height(); const int d0 = (h0 >> 1); // Restore old position... int x1 = iPositionX - x0; if (iPositionX != x && x1 >= 0 && x1 < w + d0) { // Override old view line... qtractorScrollView::viewport()->update(QRect(x1, 0, 1, h)); ((m_pTracks->trackTime())->viewport())->update( QRect(x1 - d0, d0, h0, d0)); } // New position is in... iPositionX = x; // Force position to be in view? if (bSyncView && (x < x0 || x > x0 + w - wm) && m_dragState == DragNone && m_dragCursor == DragNone // && QApplication::mouseButtons() == Qt::NoButton && --m_iSyncViewHold < 0) { // Make sure no floating widget is around... closeEditCurveNode(); // Maybe we'll need some head-room... if (x < qtractorScrollView::contentsWidth() - w) { qtractorScrollView::setContentsPos( x - wm, qtractorScrollView::contentsY()); } else updateContentsWidth(x + w); m_iSyncViewHold = 0; } else { // Draw the line, by updating the new region... x1 = x - x0; if (x1 >= 0 && x1 < w + d0) { qtractorScrollView::viewport()->update(QRect(x1, 0, 1, h)); ((m_pTracks->trackTime())->viewport())->update( QRect(x1 - d0, d0, h0, d0)); } } } // Playhead positioning. void qtractorTrackView::setPlayHead ( unsigned long iPlayHead, bool bSyncView ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { const int iPlayHeadX = pSession->pixelFromFrame(iPlayHead); drawPositionX(m_iPlayHeadX, iPlayHeadX, bSyncView); } } int qtractorTrackView::playHeadX (void) const { return m_iPlayHeadX; } // Auto-backwatrd interim play-head positioning. void qtractorTrackView::setPlayHeadAutoBackward ( unsigned long iPlayHead ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { pSession->setPlayHeadAutoBackward(iPlayHead); const int iPlayHeadX = pSession->pixelFromFrame(iPlayHead); drawPositionX(m_iPlayHeadAutoBackwardX, iPlayHeadX); } } int qtractorTrackView::playHeadAutoBackwardX (void) const { return m_iPlayHeadAutoBackwardX; } // Edit-head positioning void qtractorTrackView::setEditHead ( unsigned long iEditHead ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { if (iEditHead > pSession->editTail()) setEditTail(iEditHead); else setSyncViewHoldOn(true); pSession->setEditHead(iEditHead); const int iEditHeadX = pSession->pixelFromFrame(iEditHead); drawPositionX(m_iEditHeadX, iEditHeadX); } } int qtractorTrackView::editHeadX (void) const { return m_iEditHeadX; } // Edit-tail positioning void qtractorTrackView::setEditTail ( unsigned long iEditTail ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { if (iEditTail < pSession->editHead()) setEditHead(iEditTail); else setSyncViewHoldOn(true); pSession->setEditTail(iEditTail); const int iEditTailX = pSession->pixelFromFrame(iEditTail); drawPositionX(m_iEditTailX, iEditTailX); } } int qtractorTrackView::editTailX (void) const { return m_iEditTailX; } // Whether there's any clip currently selected. bool qtractorTrackView::isClipSelected (void) const { return (m_pClipSelect->items().count() > 0); } // Whether there's any curve/automation currently selected. bool qtractorTrackView::isCurveSelected (void) const { return (m_pCurveSelect->items().count() > 0); } // Whether there's a single track selection. qtractorTrack *qtractorTrackView::singleTrackSelected (void) { return m_pClipSelect->singleTrack(); } // Clear current selection (no notify). void qtractorTrackView::clearSelect ( bool bReset ) { // g_clipboard.clear(); QRect rectUpdate; if (bReset && m_pClipDrag) { rectUpdate = qtractorScrollView::viewport()->rect(); m_pClipDrag = nullptr; } if (m_pClipSelect->items().count() > 0) { rectUpdate = rectUpdate.united(m_pClipSelect->rect()); m_pClipSelect->reset(); } if (m_pCurveSelect->items().count() > 0) { rectUpdate = rectUpdate.united(m_pCurveSelect->rect()); m_pCurveSelect->clear(); } if (!rectUpdate.isEmpty()) updateRect(rectUpdate); } // Update current selection (no notify). void qtractorTrackView::updateSelect (void) { // Keep selection (we'll update all contents anyway)... if (m_bCurveEdit) updateCurveSelect(); else updateClipSelect(); } // Whether there's any clip on clipboard. (static) bool qtractorTrackView::isClipboard (void) { return (g_clipboard.clips.count() > 0 || g_clipboard.nodes.count() > 0); } // Whether there's a single track on clipboard. qtractorTrack *qtractorTrackView::singleTrackClipboard (void) { return g_clipboard.singleTrack; } // Clear current clipboard (no notify). void qtractorTrackView::clearClipboard (void) { g_clipboard.clear(); } // Clipboard stuffer methods. void qtractorTrackView::ClipBoard::addClip ( qtractorClip *pClip, const QRect& clipRect, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength ) { ClipItem *pClipItem = new ClipItem(pClip, clipRect, iClipStart, iClipOffset, iClipLength); if (iClipOffset == pClip->clipOffset()) pClipItem->fadeInLength = pClip->fadeInLength(); if (iClipOffset + iClipLength == pClip->clipOffset() + pClip->clipLength()) pClipItem->fadeOutLength = pClip->fadeOutLength(); clips.append(pClipItem); } void qtractorTrackView::ClipBoard::addNode ( qtractorCurve::Node *pNode, const QRect& nodeRect, unsigned long iFrame, float fValue ) { nodes.append(new NodeItem(pNode, nodeRect, iFrame, fValue)); } // Clipboard reset method. void qtractorTrackView::ClipBoard::clear (void) { qDeleteAll(clips); clips.clear(); qDeleteAll(nodes); nodes.clear(); singleTrack = nullptr; frames = 0; } // Clip selection sanity check method. bool qtractorTrackView::queryClipSelect ( qtractorClip *pClip ) { // Check if anything is really selected... if (m_pClipSelect->items().count() < 1) return (pClip && pClip->queryEditor()); // Just ask whether any target clips have pending editors... QList clips; const qtractorClipSelect::ItemList& items = m_pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorClip *pClip = iter.key(); // Make sure it's a legal selection... if (pClip->track() && pClip->isClipSelected()) clips.append(pClip); } QListIterator clips_iter(clips); while (clips_iter.hasNext()) { qtractorClip *pClip = clips_iter.next(); // Ask if it has any pending editor... if (!pClip->queryEditor()) return false; } // If it reaches here, we can do what we will to... return true; } // Curve/automation selection executive method. void qtractorTrackView::executeCurveSelect ( qtractorTrackView::Command cmd ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorCurve *pCurve = m_pCurveSelect->curve(); if (pCurve == nullptr) return; // Reset clipboard... const bool bClipboard = (cmd == Cut || cmd == Copy); if (bClipboard) { g_clipboard.clear(); g_clipboard.frames = pSession->frameFromPixel(m_pCurveSelect->rect().width()); QApplication::clipboard()->clear(); } // We'll build a composite command... qtractorCurveEditCommand *pCurveEditCommand = nullptr; const QString& sCmdMask = tr("%1 automation"); QString sCmdName; switch (cmd) { case Cut: sCmdName = sCmdMask.arg(tr("cut")); break; case Delete: sCmdName = sCmdMask.arg(tr("delete")); break; default: break; } if (!sCmdName.isEmpty()) pCurveEditCommand = new qtractorCurveEditCommand(sCmdName, pCurve); const qtractorCurveSelect::ItemList& items = m_pCurveSelect->items(); qtractorCurveSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorCurveSelect::ItemList::ConstIterator iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorCurve::Node *pNode = iter.key(); if (bClipboard) { g_clipboard.addNode(pNode, iter.value()->rectNode, pNode->frame, pNode->value); } if (pCurveEditCommand) pCurveEditCommand->removeNode(pNode); } // Hint from the whole selection rectangle... g_clipboard.frames = pSession->frameFromPixel(m_pCurveSelect->rect().width()); // Put it in the form of an undoable command... if (pCurveEditCommand) pSession->execute(pCurveEditCommand); } // Clip selection executive method. void qtractorTrackView::executeClipSelect ( qtractorTrackView::Command cmd, qtractorClip *pClipEx ) { // Check if it's all about a single clip target... if (m_pClipSelect->items().count() < 1) { if (pClipEx == nullptr) pClipEx = currentClip(); } else pClipEx = nullptr; // Selection always takes precedence... // Check if anything is really selected and sane... if (!queryClipSelect(pClipEx)) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Reset clipboard... const bool bClipboard = (cmd == Cut || cmd == Copy); if (bClipboard) { g_clipboard.clear(); g_clipboard.singleTrack = m_pClipSelect->singleTrack(); g_clipboard.frames = pSession->frameFromPixel(m_pClipSelect->rect().width()); QApplication::clipboard()->clear(); } // We'll build a composite command... qtractorClipCommand *pClipCommand = nullptr; const QString& sCmdMask = tr("%1 clip"); QString sCmdName; switch (cmd) { case Cut: sCmdName = sCmdMask.arg(tr("cut")); break; case Delete: sCmdName = sCmdMask.arg(tr("delete")); break; case Split: sCmdName = sCmdMask.arg(tr("split")); break; default: break; } if (!sCmdName.isEmpty()) pClipCommand = new qtractorClipCommand(sCmdName); if (pClipEx) { // -- Single clip... if (bClipboard) { TrackViewInfo tvi; if (trackInfo(pClipEx->track(), &tvi)) { QRect rectClip; clipInfo(pClipEx, &rectClip, &tvi); g_clipboard.addClip(pClipEx, rectClip, pClipEx->clipStart(), pClipEx->clipOffset(), pClipEx->clipLength()); } } if (pClipCommand && cmd != Split) pClipCommand->removeClip(pClipEx); // Done, single clip. } const qtractorClipSelect::ItemList& items = m_pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorClip *pClip = iter.key(); qtractorTrack *pTrack = pClip->track(); // Make sure it's legal selection... if (pTrack && pClip->isClipSelected()) { // Clip parameters. const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipOffset = pClip->clipOffset(); const unsigned long iClipLength = pClip->clipLength(); const unsigned long iClipEnd = iClipStart + iClipLength; // Clip selection points. const unsigned long iSelectStart = pClip->clipSelectStart(); const unsigned long iSelectEnd = pClip->clipSelectEnd(); const unsigned long iSelectOffset = iSelectStart - iClipStart; const unsigned long iSelectLength = iSelectEnd - iSelectStart; // Determine and dispatch selected clip regions... qtractorClipSelect::Item *pClipItem = iter.value(); if (iSelectStart > iClipStart) { if (iSelectEnd < iClipEnd) { // -- Middle region... if (bClipboard) { g_clipboard.addClip(pClip, pClipItem->rect, iSelectStart, iClipOffset + iSelectOffset, iSelectLength); } if (pClipCommand) { // Left-clip... qtractorClip *pClipLeft = cloneClip(pClip); if (pClipLeft) { pClipLeft->setClipStart(iClipStart); pClipLeft->setClipOffset(iClipOffset); pClipLeft->setClipLength(iSelectOffset); pClipLeft->setFadeInLength(pClip->fadeInLength()); pClipCommand->addClip(pClipLeft, pTrack); } // Split(middle)-clip... if (cmd == Split) { // Middle-clip... pClipCommand->resizeClip(pClip, iSelectStart, iClipOffset + iSelectOffset, iSelectLength); // Adjust middle-clip selection... pClip->setClipSelect( iSelectStart, iSelectStart + iSelectLength); } else pClipCommand->removeClip(pClip); // Right-clip... qtractorClip *pClipRight = cloneClip(pClip); if (pClipRight) { pClipRight->setClipStart(iSelectEnd); pClipRight->setClipOffset(iClipOffset + iSelectOffset + iSelectLength); pClipRight->setClipLength(iClipEnd - iSelectEnd); pClipRight->setFadeOutLength(pClip->fadeOutLength()); pClipCommand->addClip(pClipRight, pTrack); } } // Done, middle region. } else { // -- Right region... if (bClipboard) { g_clipboard.addClip(pClip, pClipItem->rect, iSelectStart, iClipOffset + iSelectOffset, iSelectLength); } if (pClipCommand) { // Left-clip... qtractorClip *pClipLeft = cloneClip(pClip); if (pClipLeft) { pClipLeft->setClipStart(iClipStart); pClipLeft->setClipOffset(iClipOffset); pClipLeft->setClipLength(iSelectOffset); pClipLeft->setFadeInLength(pClip->fadeInLength()); pClipCommand->addClip(pClipLeft, pTrack); } // Split(right)-clip... if (cmd == Split) { // Right-clip... pClipCommand->resizeClip(pClip, iSelectStart, iClipOffset + iSelectOffset, iSelectLength); // Adjust middle-clip selection... pClip->setClipSelect( iSelectStart, iSelectStart + iSelectLength); } else pClipCommand->removeClip(pClip); } // Done, right region. } } else if (iSelectEnd < iClipEnd) { // -- Left region... if (bClipboard) { g_clipboard.addClip(pClip, pClipItem->rect, iClipStart, iClipOffset, iSelectLength); } if (pClipCommand) { // Split(left)-clip... if (cmd == Split) { // Left-clip... pClipCommand->resizeClip(pClip, iClipStart, iClipOffset, iSelectLength); // Adjust middle-clip selection... pClip->setClipSelect( iClipStart, iClipStart + iSelectLength); } else pClipCommand->removeClip(pClip); // Right-clip... qtractorClip *pClipRight = cloneClip(pClip); if (pClipRight) { pClipRight->setClipStart(iSelectEnd); pClipRight->setClipOffset(iClipOffset + iSelectLength); pClipRight->setClipLength(iClipLength - iSelectLength); pClipRight->setFadeOutLength(pClip->fadeOutLength()); pClipCommand->addClip(pClipRight, pTrack); } } // Done, left region. } else { // -- Whole clip... if (bClipboard) { g_clipboard.addClip(pClip, pClipItem->rect, iClipStart, iClipOffset, iClipLength); } if (pClipCommand && cmd != Split) pClipCommand->removeClip(pClip); // Done, whole clip. } } } // Hint from the whole selection rectangle... g_clipboard.frames = (pClipEx ? pClipEx->clipLength() : pSession->frameFromPixel(m_pClipSelect->rect().width())); // Reset selection on cut or delete; // put it in the form of an undoable command... if (pClipCommand) { if (cmd == Split) pClipCommand->setClearSelect(false); pSession->execute(pClipCommand); } } // Retrieve current paste period. // (as from current clipboard width) unsigned long qtractorTrackView::pastePeriod (void) const { if (g_clipboard.clips.count() < 1) return 0; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return 0; return frameSnap(g_clipboard.frames); } // Paste from clipboard (start). void qtractorTrackView::pasteClipboard ( unsigned short iPasteCount, unsigned long iPastePeriod ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTrackView::pasteClipboard(%u, %lu)", iPasteCount, iPastePeriod); #endif qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Make sure the mouse pointer is properly located... const QPoint& pos = qtractorScrollView::viewportToContents( qtractorScrollView::viewport()->mapFromGlobal(QCursor::pos())); // Check if anything's really on clipboard... if (g_clipboard.clips.isEmpty() && g_clipboard.nodes.isEmpty()) { #if QT_VERSION >= 0x0050000 // System clipboard? const QMimeData *pMimeData = QApplication::clipboard()->mimeData(); if (pMimeData && pMimeData->hasUrls()) { dragClipDrop(pos, false, pMimeData); // Make a proper out of this (new) state? if (!m_dropItems.isEmpty()) { m_dragState = m_dragCursor = DragClipPasteDrop; // It doesn't matter which one, both pasteable views are due... qtractorScrollView::setFocus(); setEditCursor(QCursor( QIcon::fromTheme("editPaste").pixmap(22, 22), 12, 12)); // Update the pasted stuff showClipDropRects(); } } #endif // Woot! return; } // FIXME: While pasting automation/curve nodes // maybe we can only do it over the original... TrackViewInfo tvi; qtractorCurve *pCurve = nullptr; if (m_bCurveEdit) { if (g_clipboard.nodes.isEmpty()) return; qtractorTrack *pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr || !trackInfo(pTrack, &tvi)) pTrack = trackAt(pos, true, &tvi); if (pTrack == nullptr) return; pCurve = pTrack->currentCurve(); if (pCurve == nullptr) return; if (pCurve->isLocked()) return; } else if (g_clipboard.clips.isEmpty()) return; // Reset any current selection, whatsoever... m_pClipSelect->reset(); m_pCurveSelect->clear(); resetDragState(); // Set paste parameters... m_iPasteCount = iPasteCount; m_iPastePeriod = iPastePeriod; if (m_iPastePeriod < 1) m_iPastePeriod = frameSnap(g_clipboard.frames); unsigned long iPasteDelta = 0; // Copy clipboard items to floating selection; if (m_bCurveEdit) { // Flag this as target current curve, // otherwise nothing will be displayed... m_pCurveSelect->setCurve(pCurve); const int h = tvi.trackRect.height(); const int y2 = tvi.trackRect.bottom() + 1; QListIterator iter(g_clipboard.nodes); for (unsigned short i = 0; i < m_iPasteCount; ++i) { iter.toFront(); while (iter.hasNext()) { NodeItem *pNodeItem = iter.next(); const int x = pSession->pixelFromFrame(pNodeItem->frame + iPasteDelta); const float s = pCurve->scaleFromValue(pNodeItem->value); const int y = y2 - int(s * float(h)); m_pCurveSelect->addItem(pNodeItem->node, QRect(x - 4, y - 4, 8, 8)); } iPasteDelta += m_iPastePeriod; } m_pCurveSelect->update(true); // We'll start a brand new floating state... m_dragState = m_dragCursor = DragCurvePaste; m_rectDrag = m_pCurveSelect->rect(); m_posDrag = m_rectDrag.topLeft(); m_posStep = QPoint(0, 0); } else { // Copy clipboard items to floating selection; // adjust clip widths/lengths just in case time // scale (horizontal zoom) has been changed... QListIterator iter(g_clipboard.clips); for (unsigned short i = 0; i < m_iPasteCount; ++i) { iter.toFront(); while (iter.hasNext()) { ClipItem *pClipItem = iter.next(); QRect rect(pClipItem->rect); rect.setX(pSession->pixelFromFrame(pClipItem->clipStart + iPasteDelta)); rect.setWidth(pSession->pixelFromFrame(pClipItem->clipLength)); m_pClipSelect->addItem(pClipItem->clip, rect, pClipItem->clipOffset - (pClipItem->clip)->clipOffset()); } iPasteDelta += m_iPastePeriod; } // We'll start a brand new floating state... m_dragState = m_dragCursor = DragClipPaste; m_rectDrag = m_pClipSelect->rect(); m_posDrag = m_rectDrag.topLeft(); m_posStep = QPoint(0, 0); } // It doesn't matter which one, both pasteable views are due... qtractorScrollView::setFocus(); setEditCursor(QCursor(QIcon::fromTheme("editPaste").pixmap(22, 22), 12, 12)); // Let's-a go... qtractorScrollView::viewport()->update(); if (m_bCurveEdit) dragCurveMove(pos + m_posStep); else dragClipMove(pos + m_posStep); } // Intra-drag-n-drop clip move method. void qtractorTrackView::moveClipSelect ( qtractorTrack *pTrack ) { // Check if anything is really selected and sane... if (!queryClipSelect()) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // We'll need this... qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("move clip")); // We can only move clips between tracks of the same type... int iTrackClip = 0; long iClipDelta = 0; const bool bAddTrack = (pTrack == nullptr); qtractorTrack *pSingleTrack = m_pClipSelect->singleTrack(); if (pSingleTrack) { if (bAddTrack) { const int iTrack = pSession->tracks().count() + 1; const QColor& color = qtractorTrack::trackColor(iTrack); pTrack = new qtractorTrack(pSession, pSingleTrack->trackType()); // pTrack->setTrackName(QString("Track %1").arg(iTrack)); pTrack->setBackground(color); pTrack->setForeground(color.darker()); if (pSingleTrack->trackType() == qtractorTrack::Midi) { pTrack->setMidiChannel(pSingleTrack->midiChannel()); pTrack->setMidiBankSelMethod(pSingleTrack->midiBankSelMethod()); pTrack->setMidiBank(pSingleTrack->midiBank()); pTrack->setMidiProg(pSingleTrack->midiProg()); } pClipCommand->addTrack(pTrack); } else if (pSingleTrack->trackType() != pTrack->trackType()) return; } // We'll build a composite command... QList clips; const qtractorClipSelect::ItemList& items = m_pClipSelect->items(); qtractorClipSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorClipSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorClip *pClip = iter.key(); if (pSingleTrack == nullptr) pTrack = pClip->track(); // Make sure it's legal selection... if (pTrack && pClip->isClipSelected()) { // Clip parameters. const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipOffset = pClip->clipOffset(); const unsigned long iClipLength = pClip->clipLength(); const unsigned long iClipEnd = iClipStart + iClipLength; // Clip selection points. const unsigned long iSelectStart = pClip->clipSelectStart(); const unsigned long iSelectEnd = pClip->clipSelectEnd(); const unsigned long iSelectOffset = iSelectStart - iClipStart; const unsigned long iSelectLength = iSelectEnd - iSelectStart; // Determine and keep clip regions... qtractorClipSelect::Item *pClipItem = iter.value(); if (iSelectStart > iClipStart) { // -- Left clip... qtractorClip *pClipLeft = cloneClip(pClip); pClipLeft->setClipStart(iClipStart); pClipLeft->setClipOffset(iClipOffset); pClipLeft->setClipLength(iSelectOffset); pClipLeft->setFadeInLength(pClip->fadeInLength()); pClipLeft->open(); pClipCommand->addClip(pClipLeft, pClipLeft->track()); // Done, left clip. } if (iSelectEnd < iClipEnd) { // -- Right clip... qtractorClip *pClipRight = cloneClip(pClip); pClipRight->setClipStart(iSelectEnd); pClipRight->setClipOffset(iClipOffset + iSelectOffset + iSelectLength); pClipRight->setClipLength(iClipEnd - iSelectEnd); pClipRight->setFadeOutLength(pClip->fadeOutLength()); pClipRight->open(); pClipCommand->addClip(pClipRight, pClipRight->track()); // Done, right clip. } // Convert to precise frame positioning, // but only the first clip gets snapped... unsigned long iClipStart2 = iSelectStart; if (iTrackClip == 0) { const int x = (pClipItem->rect.x() + m_iDragClipX); const unsigned long iFrameStart = frameSnap( pSession->frameFromPixel(x > 0 ? x : 0)); iClipDelta = long(iFrameStart) - long(iClipStart2); iClipStart2 = iFrameStart; } else if (long(iClipStart2) + iClipDelta > 0) { iClipStart2 += iClipDelta; } else { iClipStart2 = 0; } // -- Moved clip... pClipCommand->moveClip(pClip, pTrack, iClipStart2, iClipOffset + iSelectOffset, iSelectLength); clips.append(pClip); // If track's new it will need a name... if (bAddTrack && iTrackClip == 0) { pTrack->setTrackName( pSession->uniqueTrackName(pClip->clipName())); } ++iTrackClip; } } // Put it in the form of an undoable command... pSession->execute(pClipCommand); // Redo selection as new... if (!clips.isEmpty()) { QListIterator clip_iter(clips); while (clip_iter.hasNext()) clip_iter.next()->setClipSelected(true); updateClipSelect(); m_pTracks->selectionChangeNotify(); } } // Paste from clipboard (execute). void qtractorTrackView::pasteClipSelect ( qtractorTrack *pTrack, bool bUnlink ) { // Check if there's anything really on clipboard... if (g_clipboard.clips.count() < 1) return; // Check if anything is really selected and sane... if (!queryClipSelect()) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // We'll need this... qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("paste clip")); // We can only move clips between tracks of the same type... const bool bAddTrack = (pTrack == nullptr); qtractorTrack *pSingleTrack = g_clipboard.singleTrack; if (pSingleTrack) { if (bAddTrack) { const int iTrack = pSession->tracks().count() + 1; const QColor& color = qtractorTrack::trackColor(iTrack); pTrack = new qtractorTrack(pSession, pSingleTrack->trackType()); // pTrack->setTrackName(QString("Track %1").arg(iTrack)); pTrack->setBackground(color); pTrack->setForeground(color.darker()); pClipCommand->addTrack(pTrack); } else if (pSingleTrack->trackType() != pTrack->trackType()) return; } unsigned long iPastePeriod = m_iPastePeriod; if (iPastePeriod < 1) iPastePeriod = g_clipboard.frames; long iPasteDelta = 0; if (m_iDragClipX < 0) iPasteDelta = - pSession->frameFromPixel(- m_iDragClipX); else iPasteDelta = + pSession->frameFromPixel(+ m_iDragClipX); // We'll build a composite command... QList clips; QListIterator iter(g_clipboard.clips); for (unsigned short i = 0; i < m_iPasteCount; ++i) { // Paste iteration... int iTrackClip = 0; iter.toFront(); while (iter.hasNext()) { ClipItem *pClipItem = iter.next(); qtractorClip *pClip = pClipItem->clip; if (pSingleTrack == nullptr) pTrack = pClip->track(); // Convert to precise frame positioning, // but only the first clip gets snapped... unsigned long iClipStart = pClipItem->clipStart; if (long(iClipStart) + iPasteDelta > 0) iClipStart += iPasteDelta; if (iTrackClip == 0) { unsigned long iFrameStart = iClipStart; iClipStart = frameSnap(iFrameStart); iPasteDelta += long(iClipStart) - long(iFrameStart); } // Now, its imperative to make a proper copy of those clips... qtractorClip *pNewClip = cloneClip(pClip, bUnlink); // Add the new pasted clip... if (pNewClip) { // HACK: convert/override MIDI clip-offset, length // times across potential tempo/time-sig changes... unsigned long iClipOffset = pClipItem->clipOffset; unsigned long iClipLength = pClipItem->clipLength; if (pTrack->trackType() == qtractorTrack::Midi) { const unsigned long iOldClipStart = pClipItem->clipStart; const unsigned long iClipStartTime = pSession->tickFromFrame(iClipStart); const unsigned long iClipOffsetTime = pSession->tickFromFrameRange( iOldClipStart, iOldClipStart + iClipOffset, true); const unsigned long iClipLengthTime = pSession->tickFromFrameRange( iOldClipStart, iOldClipStart + iClipLength); iClipOffset = pSession->frameFromTickRange( iClipStartTime, iClipStartTime + iClipOffsetTime, true); iClipLength = pSession->frameFromTickRange( iClipStartTime, iClipStartTime + iClipLengthTime); } // Clone clip properties... pNewClip->setClipStart(iClipStart); pNewClip->setClipOffset(iClipOffset); pNewClip->setClipLength(iClipLength); pNewClip->setFadeInLength(pClipItem->fadeInLength); pNewClip->setFadeOutLength(pClipItem->fadeOutLength); pClipCommand->addClip(pNewClip, pTrack); clips.append(pNewClip); // If track's new it will need a name... if (bAddTrack && iTrackClip == 0) { pTrack->setTrackName( pSession->uniqueTrackName(pClip->clipName())); } ++iTrackClip; } } // Set to repeat... iPasteDelta += iPastePeriod; } // Put it in the form of an undoable command... pSession->execute(pClipCommand); // Redo selection as new... if (!clips.isEmpty()) { QListIterator clip_iter(clips); while (clip_iter.hasNext()) clip_iter.next()->setClipSelected(true); updateClipSelect(); m_pTracks->selectionChangeNotify(); } } // Intra-drag-n-drop curve/automation node move method. void qtractorTrackView::moveCurveSelect ( const QPoint& pos ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; dragCurveMove(pos); TrackViewInfo tvi; qtractorTrack *pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr || !trackInfo(pTrack, &tvi)) pTrack = trackAt(pos, true, &tvi); if (pTrack == nullptr) return; qtractorCurve *pCurve = pTrack->currentCurve(); if (pCurve == nullptr) return; if (pCurve->isLocked()) return; if (!m_pCurveSelect->isCurrentCurve(pCurve)) return; const unsigned long iFrame = pCurve->cursor().frame(); const int x0 = m_pCurveSelect->rect().x(); const int x1 = x0 + m_iDragCurveX; const long delta = long(pSession->frameFromPixel(x1)) - long(pSession->frameFromPixel(x0)); qtractorCurveEditCommand *pCurveEditCommand = new qtractorCurveEditCommand(tr("move automation"), pCurve); // We'll build a composite command... QList nodes; qtractorCurveEditList edits(pCurve); const qtractorCurveSelect::ItemList& items = m_pCurveSelect->items(); qtractorCurveSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorCurveSelect::ItemList::ConstIterator iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorCurveSelect::Item *pItem = iter.value(); if (pItem->flags & 1) { qtractorCurve::Node *pNode = iter.key(); const unsigned long frame = pNode->frame + delta; const float value = pNode->value; pCurveEditCommand->removeNode(pNode); pCurve->unlinkNode(pNode); pNode = pCurve->addNode(frame, value, &edits); if (pNode) nodes.append(pNode); } } pCurveEditCommand->addEditList(&edits); // Put it in the form of an undoable command... if (pCurveEditCommand->isEmpty()) { delete pCurveEditCommand; } else { pSession->commands()->push(pCurveEditCommand); pSession->updateSession(); m_pTracks->dirtyChangeNotify(); } // Redo selection as new... if (!nodes.isEmpty()) { QRect rectUpdate = m_pCurveSelect->rect(); m_pCurveSelect->clear(); const int h = tvi.trackRect.height(); const int y2 = tvi.trackRect.bottom() + 1; QListIterator node_iter(nodes); while (node_iter.hasNext()) { qtractorCurve::Node *pNode = node_iter.next(); const float s = pCurve->scaleFromValue(pNode->value); const int x = pSession->pixelFromFrame(pNode->frame); const int y = y2 - int(s * float(h)); m_pCurveSelect->selectItem(pCurve, pNode, QRect(x - 4, y - 4, 8, 8)); } m_pCurveSelect->update(true); updateRect(rectUpdate.united(m_pCurveSelect->rect())); m_pTracks->selectionChangeNotify(); } pCurve->cursor().seek(iFrame); } // Paste from clipboard (execute). void qtractorTrackView::pasteCurveSelect ( const QPoint& pos ) { // Check if there's anything really on clipboard... if (g_clipboard.nodes.count() < 1) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; dragCurveMove(pos); TrackViewInfo tvi; qtractorTrack *pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr || !trackInfo(pTrack, &tvi)) return; qtractorCurve *pCurve = pTrack->currentCurve(); if (pCurve == nullptr) return; if (pCurve->isLocked()) return; const unsigned long iFrame = pCurve->cursor().frame(); // We'll need this... qtractorCurveEditCommand *pCurveEditCommand = new qtractorCurveEditCommand(tr("paste automation"), pCurve); unsigned long iPastePeriod = m_iPastePeriod; if (iPastePeriod < 1) iPastePeriod = g_clipboard.frames; long iPasteDelta = 0; if (m_iDragCurveX < 0) iPasteDelta = - pSession->frameFromPixel(- m_iDragCurveX); else iPasteDelta = + pSession->frameFromPixel(+ m_iDragCurveX); // We'll build a composite command... QList nodes; qtractorCurveEditList edits(pCurve); QListIterator iter(g_clipboard.nodes); for (unsigned short i = 0; i < m_iPasteCount; ++i) { // Paste iteration... iter.toFront(); while (iter.hasNext()) { NodeItem *pNodeItem = iter.next(); // Convert to precise frame positioning, // but only the first clip gets snapped... unsigned long iNodeFrame = pNodeItem->frame; if (long(iNodeFrame) + iPasteDelta > 0) iNodeFrame += iPasteDelta; if (nodes.isEmpty()) { unsigned long iFrameStart = iNodeFrame; iNodeFrame = frameSnap(iFrameStart); iPasteDelta += long(iNodeFrame) - long(iFrameStart); } // Now, its imperative to make a proper copy of those nodes... qtractorCurve::Node *pNode = pCurve->addNode(iNodeFrame, pNodeItem->value, &edits); if (pNode) nodes.append(pNode); } // Set to repeat... iPasteDelta += iPastePeriod; } pCurveEditCommand->addEditList(&edits); // Put it in the form of an undoable command... if (pCurveEditCommand->isEmpty()) { delete pCurveEditCommand; } else { pSession->commands()->push(pCurveEditCommand); pSession->updateSession(); m_pTracks->dirtyChangeNotify(); } // Redo selection as new... if (!nodes.isEmpty()) { QRect rectUpdate = m_pCurveSelect->rect(); m_pCurveSelect->clear(); const int h = tvi.trackRect.height(); const int y2 = tvi.trackRect.bottom() + 1; QListIterator node_iter(nodes); while (node_iter.hasNext()) { qtractorCurve::Node *pNode = node_iter.next(); const float s = pCurve->scaleFromValue(pNode->value); const int x = pSession->pixelFromFrame(pNode->frame); const int y = y2 - int(s * float(h)); m_pCurveSelect->selectItem(pCurve, pNode, QRect(x - 4, y - 4, 8, 8)); } m_pCurveSelect->update(true); updateRect(rectUpdate.united(m_pCurveSelect->rect())); m_pTracks->selectionChangeNotify(); } pCurve->cursor().seek(iFrame); } // Clip cloner helper. qtractorClip *qtractorTrackView::cloneClip ( qtractorClip *pClip, bool bUnlink ) { if (pClip == nullptr) return nullptr; qtractorTrack *pTrack = pClip->track(); if (pTrack == nullptr) return nullptr; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return nullptr; qtractorClip *pNewClip = nullptr; switch (pTrack->trackType()) { case qtractorTrack::Audio: { qtractorAudioClip *pAudioClip = static_cast (pClip); if (pAudioClip) pNewClip = new qtractorAudioClip(*pAudioClip); break; } case qtractorTrack::Midi: { qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) { pNewClip = new qtractorMidiClip(*pMidiClip); // Whether to Auto-unlink the new MIDI clip... qtractorMidiClip *pNewMidiClip = nullptr; if (bUnlink) pNewMidiClip = static_cast (pNewClip); if (pNewMidiClip) { // Have a new filename revision... const QString& sFilename = pNewMidiClip->createFilePathRevision(true); // Save/replace the clip track... pMidiClip->saveCopyFile(sFilename, false); // Set new copy filename... pNewMidiClip->setFilename(sFilename); pSession->files()->addClipItemEx(qtractorFileList::Midi, pNewMidiClip, true); } } break; } case qtractorTrack::None: default: break; } return pNewClip; } // Multi-item drop mode (whether to span clips horixontally). void qtractorTrackView::setDropSpan ( bool bDropSpan ) { m_bDropSpan = bDropSpan; } bool qtractorTrackView::isDropSpan (void) const { return m_bDropSpan; } // Snap-to-bar zebra mode. void qtractorTrackView::setSnapZebra ( bool bSnapZebra ) { m_bSnapZebra = bSnapZebra; updateContents(); } bool qtractorTrackView::isSnapZebra (void) const { return m_bSnapZebra; } // Snap-to-beat grid mode. void qtractorTrackView::setSnapGrid ( bool bSnapGrid ) { m_bSnapGrid = bSnapGrid; updateContents(); } bool qtractorTrackView::isSnapGrid (void) const { return m_bSnapGrid; } // Floating tool-tips mode. void qtractorTrackView::setToolTips ( bool bToolTips ) { m_bToolTips = bToolTips; } bool qtractorTrackView::isToolTips (void) const { return m_bToolTips; } // Automation curve node editing mode. void qtractorTrackView::setCurveEdit ( bool bCurveEdit ) { if (( m_bCurveEdit && bCurveEdit) || (!m_bCurveEdit && !bCurveEdit)) return; m_bCurveEdit = bCurveEdit; g_clipboard.clear(); clearSelect(); unsetEditCursor(); m_pTracks->selectionChangeNotify(); } bool qtractorTrackView::isCurveEdit (void) const { return m_bCurveEdit; } // Temporary sync-view/follow-playhead hold state. void qtractorTrackView::setSyncViewHoldOn ( bool bOn ) { m_iSyncViewHold = (m_bSyncViewHold && bOn ? QTRACTOR_SYNC_VIEW_HOLD : 0); } void qtractorTrackView::setSyncViewHold ( bool bSyncViewHold ) { m_bSyncViewHold = bSyncViewHold; setSyncViewHoldOn(bSyncViewHold); } bool qtractorTrackView::isSyncViewHold (void) const { return (m_bSyncViewHold && m_iSyncViewHold > 0); } // Return either snapped pixel, or the passed one if [Alt] key is pressed. unsigned int qtractorTrackView::pixelSnap ( unsigned int x ) const { if (QApplication::keyboardModifiers() & Qt::AltModifier) return x; qtractorSession *pSession = qtractorSession::getInstance(); return (pSession ? pSession->pixelSnap(x) : x); } // Return either snapped frame, or the passed one if [Alt] key is pressed. unsigned long qtractorTrackView::frameSnap ( unsigned long iFrame ) const { if (QApplication::keyboardModifiers() & Qt::AltModifier) return iFrame; qtractorSession *pSession = qtractorSession::getInstance(); return (pSession ? pSession->frameSnap(iFrame) : iFrame); } // Automation/curve node editor methods. void qtractorTrackView::openEditCurveNode ( qtractorCurve *pCurve, qtractorCurve::Node *pNode ) { closeEditCurveNode(); if (pCurve == nullptr || pNode == nullptr) return; qtractorSubject *pSubject = pCurve->subject(); if (pSubject == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorTrackView::openEditCurveNode(%p, %p)", pCurve, pNode); #endif m_pEditCurve = pCurve; m_pEditCurveNode = pNode; const float fMaxValue = pSubject->maxValue(); const float fMinValue = pSubject->minValue(); int iDecimals = 0; if (pSubject->isDecimal()) { const float fDecs = ::log10f(fMaxValue - fMinValue); if (fDecs < 0.0f) iDecimals = 6; else if (fDecs < 3.0f) iDecimals = 3; else if (fDecs < 6.0f) iDecimals = 1; #if 0 if (m_pEditCurve->isLogarithmic()) ++iDecimals; #endif } m_pEditCurveNodeSpinBox = new QDoubleSpinBox(this); m_pEditCurveNodeSpinBox->setDecimals(iDecimals); m_pEditCurveNodeSpinBox->setMinimum(fMinValue); m_pEditCurveNodeSpinBox->setMaximum(fMaxValue); m_pEditCurveNodeSpinBox->setSingleStep(::powf(10.0f, - float(iDecimals))); m_pEditCurveNodeSpinBox->setAccelerated(true); m_pEditCurveNodeSpinBox->setToolTip(pSubject->name()); m_pEditCurveNodeSpinBox->setMinimumWidth(42); m_pEditCurveNodeSpinBox->setValue(m_pEditCurveNode->value); QWidget *pViewport = qtractorScrollView::viewport(); const int w = pViewport->width(); const int h = pViewport->height(); const QPoint& vpos = pViewport->mapFromGlobal(QCursor::pos()); const QSize& size = m_pEditCurveNodeSpinBox->sizeHint(); int x = vpos.x() + 4; int y = vpos.y(); if (x + size.width() > w) x -= size.width() + 8; if (y + size.height() > h) y -= size.height(); m_pEditCurveNodeSpinBox->move(x, y); m_pEditCurveNodeSpinBox->show(); m_pEditCurveNodeSpinBox->setFocus(); QObject::connect(m_pEditCurveNodeSpinBox, SIGNAL(valueChanged(double)), SLOT(editCurveNodeChanged())); QObject::connect(m_pEditCurveNodeSpinBox, SIGNAL(editingFinished()), SLOT(editCurveNodeFinished())); // We'll start clean... m_iEditCurveNodeDirty = 0; setSyncViewHoldOn(true); } void qtractorTrackView::closeEditCurveNode (void) { if (m_pEditCurveNodeSpinBox == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorTrackView::closeEditCurveNode()"); #endif // Have we changed anything? if (m_iEditCurveNodeDirty > 0 && m_pEditCurveNode && m_pEditCurve) { // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { const float fOldValue = m_pEditCurveNode->value; const float fNewValue = m_pEditCurveNodeSpinBox->value(); if (qAbs(fNewValue - fOldValue) > float(m_pEditCurveNodeSpinBox->singleStep())) { qtractorCurveEditCommand *pEditCurveNodeCommand = new qtractorCurveEditCommand(m_pEditCurve); pEditCurveNodeCommand->moveNode(m_pEditCurveNode, m_pEditCurveNode->frame, fNewValue); pSession->execute(pEditCurveNodeCommand); } } // Reset editing references... m_iEditCurveNodeDirty = 0; m_pEditCurveNode = nullptr; m_pEditCurve = nullptr; } // Time to close... m_pEditCurveNodeSpinBox->blockSignals(true); m_pEditCurveNodeSpinBox->clearFocus(); m_pEditCurveNodeSpinBox->close(); m_pEditCurveNodeSpinBox->deleteLater(); m_pEditCurveNodeSpinBox = nullptr; qtractorScrollView::setFocus(); } void qtractorTrackView::editCurveNodeChanged (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTrackView::editCurveNodeChanged()"); #endif if (m_pEditCurveNodeSpinBox && m_pEditCurveNode && m_pEditCurve) { ++m_iEditCurveNodeDirty; setSyncViewHoldOn(true); } } void qtractorTrackView::editCurveNodeFinished (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTrackView::editCurveNodeFinished()"); #endif closeEditCurveNode(); } // (Un)set edit mode cursors. void qtractorTrackView::setEditCursor ( const QCursor& cursr ) { qtractorScrollView::viewport()->setCursor(cursr); } void qtractorTrackView::unsetEditCursor (void) { if (m_bCurveEdit) { setEditCursor(QCursor( QIcon::fromTheme("editSelectCurve").pixmap(22), 7, 6)); } else { qtractorScrollView::viewport()->unsetCursor(); } } // end of qtractorTrackView.cpp qtractor-1.5.11/src/PaxHeaders/qtractorInstrumentMenu.h0000644000000000000000000000013215124701674020240 xustar0030 mtime=1767080892.786263483 30 atime=1767080892.786263483 30 ctime=1767080892.786263483 qtractor-1.5.11/src/qtractorInstrumentMenu.h0000644000175000001440000000474515124701674020242 0ustar00rncbcusers// qtractorInstrumentMenu.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorInstrumentMenu_h #define __qtractorInstrumentMenu_h #include #include // Forward decls. class qtractorTrack; class qtractorMidiManager; class QMenu; class QIcon; class QStyle; //---------------------------------------------------------------------- // class qtractorInstrumentMenu -- instrument definition data classes. // class qtractorInstrumentMenu : public QObject { Q_OBJECT public: // Constructor. qtractorInstrumentMenu(QObject *pParent = nullptr); // Main accessor initiator. void updateTrackMenu(qtractorTrack *pTrack, QMenu *pMenu); protected slots: // Menu navigation. void updateBankMenu(); void updateProgMenu(); // Menu executive. void progActionTriggered(bool); protected: bool trackMenuReset(QMenu *pMenu) const; bool trackMenuAdd(QMenu *pMenu, const QIcon& icon, qtractorMidiManager *pMidiManager, const QString& sCurrentName) const; bool bankMenuReset(QMenu *pBankMenu) const; bool bankMenuAdd(QMenu *pBankMenu, const QIcon& icon, qtractorMidiManager *pMidiManager, const QString& sInstrumentName, int iCurrentBank) const; bool progMenuReset(QMenu *pProgMenu) const; bool progMenuAdd(QMenu *pProgMenu, const QIcon& icon, qtractorMidiManager *pMidiManager, const QString& sInstrumentName, int iBank, int iCurrentProg) const; // A custome scrollable menu style (static). static QStyle *scrollableMenuStyle(); private: // Instance variables. qtractorTrack *m_pTrack; }; #endif // __qtractorInstrumentMenu_h // end of qtractorInstrumentMenu.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiControlPluginWidget.cpp0000644000000000000000000000013215124701674022344 xustar0030 mtime=1767080892.790263466 30 atime=1767080892.790263466 30 ctime=1767080892.790263466 qtractor-1.5.11/src/qtractorMidiControlPluginWidget.cpp0000644000175000001440000001101015124701674022325 0ustar00rncbcusers// qtractorMidiControlPluginWidget.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorMidiControlPluginWidget.h" #include "qtractorMidiControlPlugin.h" #include "qtractorMidiControlTypeGroup.h" #include "qtractorMainForm.h" //---------------------------------------------------------------------------- // qtractorMidiControlPluginWidget -- UI wrapper form. // Constructor. qtractorMidiControlPluginWidget::qtractorMidiControlPluginWidget ( QWidget *pParent ) : QWidget(pParent) { // Setup UI struct... m_ui.setupUi(this); m_pControlTypeGroup = new qtractorMidiControlTypeGroup(nullptr, m_ui.ControlTypeComboBox, m_ui.ControlParamComboBox, m_ui.ControlParamTextLabel); // Target object. m_pMidiControlPlugin = nullptr; m_iDirtySetup = 0; // UI signal/slot connections... QObject::connect(m_pControlTypeGroup, SIGNAL(controlTypeChanged(int)), SLOT(changed())); QObject::connect(m_pControlTypeGroup, SIGNAL(controlParamChanged(int)), SLOT(changed())); QObject::connect(m_ui.ControlChannelSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.ControlLogarithmicCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.ControlInvertCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.ControlBipolarCheckBox, SIGNAL(toggled(bool)), SLOT(changedBipolar())); } // Destructor. qtractorMidiControlPluginWidget::~qtractorMidiControlPluginWidget (void) { delete m_pControlTypeGroup; } // Accessors. void qtractorMidiControlPluginWidget::setMidiControlPlugin ( qtractorMidiControlPlugin *pMidiControlPlugin ) { m_pMidiControlPlugin = pMidiControlPlugin; if (m_pMidiControlPlugin) { ++m_iDirtySetup; m_pControlTypeGroup->setControlType( m_pMidiControlPlugin->controlType()); m_pControlTypeGroup->setControlParam( m_pMidiControlPlugin->controlParam()); m_ui.ControlChannelSpinBox->setValue( m_pMidiControlPlugin->controlChannel() + 1); m_ui.ControlLogarithmicCheckBox->setChecked( m_pMidiControlPlugin->isControlLogarithmic()); m_ui.ControlInvertCheckBox->setChecked( m_pMidiControlPlugin->isControlInvert()); m_ui.ControlBipolarCheckBox->setChecked( m_pMidiControlPlugin->isControlBipolar()); m_iDirtySetup = 0; } } qtractorMidiControlPlugin *qtractorMidiControlPluginWidget::midiControlPlugin (void) const { return m_pMidiControlPlugin; } // Change settings (anything else slot). void qtractorMidiControlPluginWidget::changed (void) { if (m_iDirtySetup > 0) return; #ifdef CONFIG_DEBUG qDebug("qtractorMidiControlPluginWidget::changed()"); #endif if (m_pMidiControlPlugin) { m_pMidiControlPlugin->setControlType( m_pControlTypeGroup->controlType()); m_pMidiControlPlugin->setControlParam( m_pControlTypeGroup->controlParam()); m_pMidiControlPlugin->setControlChannel( m_ui.ControlChannelSpinBox->value() - 1); m_pMidiControlPlugin->setControlLogarithmic( m_ui.ControlLogarithmicCheckBox->isChecked()); m_pMidiControlPlugin->setControlInvert( m_ui.ControlInvertCheckBox->isChecked()); dirtyNotify(); } } void qtractorMidiControlPluginWidget::changedBipolar (void) { if (m_iDirtySetup > 0) return; #ifdef CONFIG_DEBUG qDebug("qtractorMidiControlPluginWidget::changedBipolar()"); #endif if (m_pMidiControlPlugin) { m_pMidiControlPlugin->setControlBipolar( m_ui.ControlBipolarCheckBox->isChecked()); dirtyNotify(); } emit bipolarChanged(); } void qtractorMidiControlPluginWidget::dirtyNotify (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->dirtyNotifySlot(); } // end of qtractorMidiControlPluginWidget.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiControl.cpp0000644000000000000000000000012715124701674020025 xustar0029 mtime=1767080892.78926347 29 atime=1767080892.78926347 29 ctime=1767080892.78926347 qtractor-1.5.11/src/qtractorMidiControl.cpp0000644000175000001440000010024415124701674020012 0ustar00rncbcusers// qtractorMidiControl.cpp // /**************************************************************************** Copyright (C) 2005-2024, rncbc aka Rui Nuno Capela. All rights reserved. Copyright (C) 2009, gizzmo aka Mathias Krause. 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 "qtractorAbout.h" #include "qtractorMidiControl.h" #include "qtractorMidiEngine.h" #include "qtractorMidiControlObserver.h" #include "qtractorMidiControlObserverForm.h" #include "qtractorTrackCommand.h" #include "qtractorDocument.h" #include #include #include #include // Deprecated QTextStreamFunctions/Qt namespaces workaround. #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) #define endl Qt::endl #endif #include // Ref. P.448. Approximate cube root of an IEEE float // Hacker's Delight (2nd Edition), by Henry S. Warren // http://www.hackersdelight.org/hdcodetxt/acbrt.c.txt // static inline float cbrtf2 ( float x ) { #ifdef CONFIG_FLOAT32_NOP // Avoid strict-aliasing optimization (gcc -O2). union { float f; int i; } u; u.f = x; u.i = (u.i >> 4) + (u.i >> 2); u.i += (u.i >> 4) + 0x2a6a8000; // 0x2a6497f8; // return 0.33333333f * (2.0f * u.f + x / (u.f * u.f)); return u.f; #else return ::cbrtf(x); #endif } static inline float cubef2 ( float x ) { return x * x * x; } //---------------------------------------------------------------------- // qtractorMidiControl -- MIDI control map (singleton). // // Kind of singleton reference. qtractorMidiControl *qtractorMidiControl::g_pMidiControl = nullptr; // Constructor. qtractorMidiControl::qtractorMidiControl (void) { // Pseudo-singleton reference setup. g_pMidiControl = this; // Default controller mapping... clear(); } // Destructor. qtractorMidiControl::~qtractorMidiControl (void) { // Pseudo-singleton reference shut-down. g_pMidiControl = nullptr; } // Kind of singleton reference. qtractorMidiControl *qtractorMidiControl::getInstance (void) { return g_pMidiControl; } // Clear control map (reset to default). void qtractorMidiControl::clear (void) { m_controlMap.clear(); #ifdef TEST_USx2y // JLCooper faders (as in US-224)... mapChannelControllerParam(15, TrackGain, 0x40); // No feedback. #endif #ifdef TEST_BCx2000 // Generic track feedback controllers (eg. Behringer BCx2000)... mapChannelParamController(7, TrackGain, 0, true); mapChannelParamController(10, TrackPanning, 0, true); mapChannelParamController(20, TrackMute, 0, true); #endif m_observerMap.clear(); } // Clear track (catch-up) map. void qtractorMidiControl::clearControlMap (void) { ControlMap::Iterator it = m_controlMap.begin(); const ControlMap::Iterator& it_end = m_controlMap.end(); for ( ; it != it_end; ++it) it.value().clear(); } // Insert new controller mappings. void qtractorMidiControl::mapChannelParam ( ControlType ctype, unsigned short iChannel, unsigned short iParam, Command command, int iTrack, int iFlags ) { m_controlMap.insert( MapKey(ctype, iChannel, iParam), MapVal(command, iTrack, iFlags)); } void qtractorMidiControl::mapChannelTrack ( ControlType ctype, unsigned short iParam, Command command, int iTrack, int iFlags ) { mapChannelParam( ctype, TrackParam, iParam, command, iTrack, iFlags); } void qtractorMidiControl::mapChannelParamTrack ( ControlType ctype, unsigned short iChannel, unsigned short iParam, Command command, int iTrack, int iFlags ) { mapChannelParam( ctype, iChannel, iParam | TrackParam, command, iTrack, iFlags); } // Remove existing controller mapping. void qtractorMidiControl::unmapChannelParam ( ControlType ctype, unsigned short iChannel, unsigned short iParam ) { m_controlMap.remove(MapKey(ctype, iChannel, iParam)); } // Check if given channel, param triplet is currently mapped. bool qtractorMidiControl::isChannelParamMapped ( ControlType ctype, unsigned short iChannel, unsigned short iParam ) const { return m_controlMap.contains(MapKey(ctype, iChannel, iParam)); } // Resend all (tracks) controllers // (eg. session initialized, track added/removed) void qtractorMidiControl::sendAllControllers ( int iFirstTrack ) const { if (iFirstTrack < 0) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMidiControl::sendAllControllers(%d)", iFirstTrack); #endif // 1. Walk through midi controller map... ControlMap::ConstIterator it = m_controlMap.constBegin(); const ControlMap::ConstIterator& it_end = m_controlMap.constEnd(); for ( ; it != it_end; ++it) { const MapVal& val = it.value(); if (val.isFeedback()) { const MapKey& key = it.key(); const unsigned short iChannel = key.channel(); const unsigned short iParam = (key.param() & TrackParamMask); int iTrack = 0; int iLastTrack = iFirstTrack; if (key.isParamTrack()) iLastTrack += val.trackLimit(); for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (iTrack >= iFirstTrack) { if (key.isChannelTrack()) sendTrackController(key.type(), pTrack, val.command(), iTrack, iParam); else if (key.isParamTrack()) { if (iFirstTrack < iLastTrack && iTrack >= iLastTrack) break; // Bail out from inner track loop. const unsigned short iParamTrack = iParam + val.trackOffset() + iTrack; sendTrackController(key.type(), pTrack, val.command(), iChannel, iParamTrack); } else if (val.track() == iTrack) { sendTrackController(key.type(), pTrack, val.command(), iChannel, iParam); break; // Bail out from inner track loop. } } ++iTrack; } } } // 2. Walk through midi observer map... if (iFirstTrack == 0) { ObserverMap::ConstIterator iter = m_observerMap.constBegin(); const ObserverMap::ConstIterator& iter_end = m_observerMap.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiControlObserver *pMidiObserver = iter.value(); if (pMidiObserver->isFeedback()) { sendController( pMidiObserver->type(), pMidiObserver->channel(), pMidiObserver->param(), pMidiObserver->midiValue()); } } } } // Find incoming controller event map. qtractorMidiControl::ControlMap::Iterator qtractorMidiControl::findEvent ( const qtractorCtlEvent& ctle ) { // Check if controller map includes this event... ControlMap::Iterator it = m_controlMap.begin(); const ControlMap::Iterator& it_end = m_controlMap.end(); for ( ; it != it_end; ++it) { const MapKey& key = it.key(); // Generic key matcher. if (key.type() != ctle.type()) continue; if (!key.isChannelTrack() && key.channel() != ctle.channel()) continue; const unsigned short iParam = (key.param() & TrackParamMask); if (key.isParamTrack()) { const MapVal& val = it.value(); const unsigned short iKeyParam = iParam + val.trackOffset(); const unsigned short iKeyParamLimit = iKeyParam + val.trackLimit(); const unsigned short iCtlParam = ctle.param(); if (iCtlParam >= iKeyParam && (iKeyParam >= iKeyParamLimit || iCtlParam < iKeyParamLimit)) break; } else if (iParam == ctle.param()) break; } return it; } // Process incoming controller event. bool qtractorMidiControl::processEvent ( const qtractorCtlEvent& ctle ) { bool bResult = false; // Find whether there's any observer assigned... qtractorMidiControlObserver *pMidiObserver = findMidiObserver(ctle.type(), ctle.channel(), ctle.param()); if (pMidiObserver) { pMidiObserver->setMidiValue(ctle.value()); bResult = true; } else { qtractorMidiControlObserverForm *pMidiObserverForm = qtractorMidiControlObserverForm::getInstance(); if (pMidiObserverForm) pMidiObserverForm->processEvent(ctle); } // Find incoming controller event map tuple. ControlMap::Iterator it = findEvent(ctle); // Is there one mapped, indeed? if (it == m_controlMap.end()) return bResult; // Find the track by number... const MapKey& key = it.key(); MapVal& val = it.value(); int iTrack = 0; if (key.isChannelTrack()) { iTrack += val.track(); iTrack += int(ctle.channel()); } else if (key.isParamTrack()) { const unsigned short iKeyParam = (key.param() & TrackParamMask) + val.trackOffset(); const unsigned short iKeyParamLimit = iKeyParam + val.trackLimit(); const unsigned short iCtlParam = ctle.param(); if (iCtlParam >= iKeyParam && (iKeyParam >= iKeyParamLimit || iCtlParam < iKeyParamLimit)) iTrack += (iCtlParam - iKeyParam); else return bResult; } else iTrack = val.track(); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return bResult; qtractorTrack *pTrack = pSession->tracks().at(iTrack); if (pTrack == nullptr) return bResult; ControlScale scale(ctle.type()); MapVal::Track& ctlv = val.track(iTrack); float fValue, fOldValue; switch (val.command()) { case TRACK_GAIN: fValue = scale.valueFromMidi(ctle.value()); fOldValue = pTrack->gain(); if (pTrack->trackType() == qtractorTrack::Audio && !val.isDelta()) fValue = ::cubef2(fValue); if (ctlv.syncDecimal(fValue, fOldValue, val.isDelta())) { bResult = pSession->execute( new qtractorTrackGainCommand(pTrack, ctlv.value(), true)); } break; case TRACK_PANNING: fValue = scale.valueSignedFromMidi(ctle.value()); fOldValue = pTrack->panning(); if (ctlv.syncDecimal(fValue, fOldValue, val.isDelta())) { bResult = pSession->execute( new qtractorTrackPanningCommand(pTrack, ctlv.value(), true)); } break; case TRACK_MONITOR: fValue = scale.valueToggledFromMidi(ctle.value()); fOldValue = (pTrack->isMonitor() ? 1.0f : 0.0f); if (ctlv.syncToggled(fValue, fOldValue, val.isDelta())) { bResult = pSession->execute( new qtractorTrackMonitorCommand(pTrack, ctlv.value(), true)); } break; case TRACK_RECORD: fValue = scale.valueToggledFromMidi(ctle.value()); fOldValue = (pTrack->isRecord() ? 1.0f : 0.0f); if (ctlv.syncToggled(fValue, fOldValue, val.isDelta())) { bResult = pSession->execute( new qtractorTrackStateCommand(pTrack, qtractorTrack::Record, ctlv.value(), true)); } break; case TRACK_MUTE: fValue = scale.valueToggledFromMidi(ctle.value()); fOldValue = (pTrack->isMute() ? 1.0f : 0.0f); if (ctlv.syncToggled(fValue, fOldValue, val.isDelta())) { bResult = pSession->execute( new qtractorTrackStateCommand(pTrack, qtractorTrack::Mute, ctlv.value(), true)); } break; case TRACK_SOLO: fValue = scale.valueToggledFromMidi(ctle.value()); fOldValue = (pTrack->isSolo() ? 1.0f : 0.0f); if (ctlv.syncToggled(fValue, fOldValue, val.isDelta())) { bResult = pSession->execute( new qtractorTrackStateCommand(pTrack, qtractorTrack::Solo, ctlv.value(), true)); } break; default: break; } return bResult; } // Process incoming command. void qtractorMidiControl::processTrackCommand ( Command command, int iTrack, float fValue, bool bLogarithmic ) { sendTrackController(iTrack, command, fValue, bLogarithmic); } void qtractorMidiControl::processTrackCommand ( Command command, int iTrack, bool bValue ) { sendTrackController(iTrack, command, (bValue ? 1.0f : 0.0f), false); } // Further processing of outgoing midi controller messages void qtractorMidiControl::sendTrackController ( int iTrack, Command command, float fValue, bool bLogarithmic ) { // Search for the command and parameter in controller map... ControlMap::Iterator it = m_controlMap.begin(); const ControlMap::Iterator& it_end = m_controlMap.end(); for ( ; it != it_end; ++it) { const MapKey& key = it.key(); MapVal& val = it.value(); if (val.command() == command) { val.syncReset(iTrack); if (!val.isFeedback()) continue; // Convert/normalize value... const ControlType ctype = key.type(); const ControlScale scale(ctype); unsigned short iValue = 0; switch (command) { case TRACK_GAIN: if (bLogarithmic) fValue = ::cbrtf2(fValue); iValue = scale.midiFromValue(fValue); break; case TRACK_PANNING: iValue = scale.midiFromValueSigned(fValue); break; case TRACK_MONITOR: case TRACK_RECORD: case TRACK_MUTE: case TRACK_SOLO: iValue = scale.midiFromValueToggled(fValue); // Fall thru... default: break; } // Now send the message out... const unsigned short iParam = (key.param() & TrackParamMask); if (key.isChannelTrack()) sendController(key.type(), iTrack, iParam, iValue); else if (key.isParamTrack()) { const unsigned short iParamTrack = iParam + val.trackOffset() + iTrack; sendController(key.type(), key.channel(), iParamTrack, iValue); } else if (val.track() == iTrack) sendController(key.type(), key.channel(), iParam, iValue); } } } void qtractorMidiControl::sendTrackController ( ControlType ctype, qtractorTrack *pTrack, Command command, unsigned short iChannel, unsigned short iParam ) const { const ControlScale scale(ctype); unsigned short iValue = 0; switch (command) { case TRACK_GAIN: if (pTrack->trackType() == qtractorTrack::Audio) iValue = scale.midiFromValue(::cbrtf2(pTrack->gain())); else iValue = scale.midiFromValue(pTrack->gain()); break; case TRACK_PANNING: iValue = scale.midiFromValueSigned(pTrack->panning()); break; case TRACK_MONITOR: iValue = scale.midiFromValueToggled(pTrack->isMonitor()); break; case TRACK_RECORD: iValue = scale.midiFromValueToggled(pTrack->isRecord()); break; case TRACK_MUTE: iValue = scale.midiFromValueToggled(pTrack->isMute()); break; case TRACK_SOLO: iValue = scale.midiFromValueToggled(pTrack->isSolo()); break; default: break; } sendController(ctype, iChannel, iParam, iValue); } // Send this value out to midi bus. void qtractorMidiControl::sendController ( ControlType ctype, unsigned short iChannel, unsigned short iParam, unsigned short iValue ) const { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; qtractorMidiBus *pMidiBus = pMidiEngine->controlBus_out(); if (pMidiBus == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiControl::sendController(0x%02x, %u, %u, %d)", int(ctype), iChannel, iParam, iValue); #endif pMidiBus->sendEvent(ctype, iChannel, iParam, iValue); } // Insert/remove observer mappings. void qtractorMidiControl::mapMidiObserver ( qtractorMidiControlObserver *pMidiObserver, QWidget *pWidget ) { const MapKey key( pMidiObserver->type(), pMidiObserver->channel(), pMidiObserver->param()); m_observerMap.insert(key, pMidiObserver); mapMidiObserverWidget(pMidiObserver, pWidget); } void qtractorMidiControl::unmapMidiObserver ( qtractorMidiControlObserver *pMidiObserver, bool bResetWidgets ) { const MapKey key( pMidiObserver->type(), pMidiObserver->channel(), pMidiObserver->param()); m_observerMap.remove(key); if (bResetWidgets) unmapMidiObserverWidgets(pMidiObserver, true); } // Observer map predicate. bool qtractorMidiControl::isMidiObserverMapped ( qtractorMidiControlObserver *pMidiObserver ) const { return (findMidiObserver( pMidiObserver->type(), pMidiObserver->channel(), pMidiObserver->param()) == pMidiObserver); } // Observer finder. qtractorMidiControlObserver *qtractorMidiControl::findMidiObserver ( ControlType ctype, unsigned short iChannel, unsigned short iParam ) const { return m_observerMap.value(MapKey(ctype, iChannel, iParam), nullptr); } // Observer (widget) mappings. void qtractorMidiControl::mapMidiObserverWidget ( qtractorMidiControlObserver *pMidiObserver, QWidget *pWidget ) { if (pWidget) m_widgetMap.insert(pMidiObserver, pWidget); QString sToolTip = pMidiObserver->subject()->name(); if (isMidiObserverMapped(pMidiObserver)) { sToolTip += '\n'; sToolTip += '\n'; sToolTip += QObject::tr("MIDI Controller: %1, %2, %3") .arg(qtractorMidiControl::nameFromType(pMidiObserver->type())) .arg(QString::number(pMidiObserver->channel() + 1)) .arg(QString::number(pMidiObserver->param())); } QListIterator iter(m_widgetMap.values(pMidiObserver)); while (iter.hasNext()) iter.next()->setToolTip(sToolTip); } void qtractorMidiControl::unmapMidiObserverWidget ( qtractorMidiControlObserver *pMidiObserver, QWidget *pWidget ) { if (pWidget) { m_widgetMap.remove(pMidiObserver, pWidget); } else { m_widgetMap.remove(pMidiObserver); } } void qtractorMidiControl::unmapMidiObserverWidgets ( qtractorMidiControlObserver *pMidiObserver, bool bResetWidgets ) { if (bResetWidgets) { QListIterator iter(m_widgetMap.values(pMidiObserver)); while (iter.hasNext()) iter.next()->setToolTip(pMidiObserver->subject()->name()); } else { m_widgetMap.remove(pMidiObserver); } } //---------------------------------------------------------------------- // qtractorMidiControl::Document -- MIDI control document. // class qtractorMidiControl::Document : public qtractorDocument { public: // Constructor. Document(QDomDocument *pDocument, qtractorMidiControl *pMidiControl) : qtractorDocument(pDocument, "midi-control"), m_pMidiControl(pMidiControl) {} // Property accessors. qtractorMidiControl *midiControl() const { return m_pMidiControl; } // External storage simple methods. bool load(const QString& sFilename); bool save(const QString& sFilename); protected: // Elemental loader/savers... bool loadElement(QDomElement *pElement) { return m_pMidiControl->loadElement(this, pElement); } bool saveElement(QDomElement *pElement) { return m_pMidiControl->saveElement(this, pElement); } private: // Instance variables. qtractorMidiControl *m_pMidiControl; }; // External storage simple load method. bool qtractorMidiControl::Document::load ( const QString& sFilename ) { QFile file(sFilename); if (!file.open(QIODevice::ReadOnly)) return false; // Parse it a-la-DOM :-) QDomDocument *pDocument = document(); if (!pDocument->setContent(&file)) { file.close(); return false; } file.close(); QDomElement elem = pDocument->documentElement(); // Get root element and check for proper taq name. if (elem.tagName() != "midi-control") return false; return loadElement(&elem); } // External storage simple save method. bool qtractorMidiControl::Document::save ( const QString& sFilename ) { QFile file(sFilename); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false; QDomDocument *pDocument = document(); QDomElement elem = pDocument->createElement("midi-control"); saveElement(&elem); pDocument->appendChild(elem); QTextStream ts(&file); ts << pDocument->toString() << endl; file.close(); return true; } // Load controller rules. bool qtractorMidiControl::loadElement ( Document * /*pDocument*/, QDomElement *pElement ) { for (QDomNode nItem = pElement->firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { // Convert item node to element... QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; if (eItem.tagName() == "map") { ControlType ctype = typeFromText(eItem.attribute("type")); const unsigned short iChannel = keyFromText(eItem.attribute("channel")); unsigned short iParam = 0; const bool bOldMap = (ctype == ControlType(0)); bool bOldTrackParam = false; if (bOldMap) { ctype = qtractorMidiEvent::CONTROLLER; iParam = keyFromText(eItem.attribute("controller")); bOldTrackParam = bool(iParam & TrackParam); } else { iParam = eItem.attribute("param").toUShort(); if (qtractorDocument::boolFromText(eItem.attribute("track"))) iParam |= TrackParam; } Command command = Command(0); int iTrack = 0; bool bDelta = false; bool bFeedback = false; for (QDomNode nVal = eItem.firstChild(); !nVal.isNull(); nVal = nVal.nextSibling()) { // Convert value node to element... QDomElement eVal = nVal.toElement(); if (eVal.isNull()) continue; if (eVal.tagName() == "command") command = commandFromText(eVal.text()); else if (eVal.tagName() == "track") iTrack |= (eVal.text().toInt() & 0x7f); else if (eVal.tagName() == "limit") iTrack |= (eVal.text().toInt() << 7) & 0x3fc0; else if (eVal.tagName() == "delta") bDelta = qtractorDocument::boolFromText(eVal.text()); else if (eVal.tagName() == "feedback") bFeedback = qtractorDocument::boolFromText(eVal.text()); else if (eVal.tagName() == "param" && bOldMap) { iTrack = eVal.text().toInt(); if (bOldTrackParam) { iParam += iTrack; iTrack = 0; } } } int iFlags = 0; if (bDelta) iFlags |= MapVal::Delta; if (bFeedback) iFlags |= MapVal::Feedback; m_controlMap.insert( MapKey(ctype, iChannel, iParam), MapVal(command, iTrack, iFlags)); } } return true; } // Save controller rules. bool qtractorMidiControl::saveElement ( Document *pDocument, QDomElement *pElement ) { // Save this program version (informational)... pElement->setAttribute("version", PROJECT_TITLE " " PROJECT_VERSION); ControlMap::ConstIterator it = m_controlMap.constBegin(); const ControlMap::ConstIterator& it_end = m_controlMap.constEnd(); for ( ; it != it_end; ++it) { const MapKey& key = it.key(); const MapVal& val = it.value(); QDomElement eItem = pDocument->document()->createElement("map"); eItem.setAttribute("type", textFromType(key.type())); eItem.setAttribute("channel", textFromKey(key.channel())); eItem.setAttribute("param", QString::number(key.param() & TrackParamMask)); eItem.setAttribute("track", qtractorDocument::textFromBool(key.isParamTrack())); pDocument->saveTextElement("command", textFromCommand(val.command()), &eItem); pDocument->saveTextElement("track", QString::number(val.trackOffset()), &eItem); pDocument->saveTextElement("limit", QString::number(val.trackLimit()), &eItem); pDocument->saveTextElement("delta", qtractorDocument::textFromBool(val.isDelta()), &eItem); pDocument->saveTextElement("feedback", qtractorDocument::textFromBool(val.isFeedback()), &eItem); pElement->appendChild(eItem); } return true; } // Document file methods. bool qtractorMidiControl::loadDocument ( const QString& sFilename ) { QDomDocument doc("qtractorMidiControl"); return qtractorMidiControl::Document(&doc, this).load(sFilename); } bool qtractorMidiControl::saveDocument ( const QString& sFilename ) { QDomDocument doc("qtractorMidiControl"); return qtractorMidiControl::Document(&doc, this).save(sFilename); } unsigned short qtractorMidiControl::keyFromText ( const QString& sText ) { if (sText == "*" || sText == "TrackParam" || sText.isEmpty()) return TrackParam; else return sText.toUShort(); } QString qtractorMidiControl::textFromKey ( unsigned short iKey ) { if (iKey & TrackParam) return "*"; // "TrackParam"; else return QString::number(iKey); } // Load meter controllers (MIDI). void qtractorMidiControl::loadControllers ( QDomElement *pElement, Controllers& controllers ) { qDeleteAll(controllers); controllers.clear(); for (QDomNode nController = pElement->firstChild(); !nController.isNull(); nController = nController.nextSibling()) { // Convert node to element, if any. QDomElement eController = nController.toElement(); if (eController.isNull()) continue; // Check for controller item... if (eController.tagName() == "controller") { Controller *pController = new Controller; pController->name = eController.attribute("name"); pController->index = eController.attribute("index").toULong(); pController->ctype = typeFromText(eController.attribute("type")); pController->channel = 0; pController->param = 0; pController->logarithmic = false; pController->feedback = false; pController->invert = false; pController->hook = false; pController->latch = false; for (QDomNode nProp = eController.firstChild(); !nProp.isNull(); nProp = nProp.nextSibling()) { // Convert node to element, if any. QDomElement eProp = nProp.toElement(); if (eProp.isNull()) continue; // Check for property item... if (eProp.tagName() == "channel") pController->channel = eProp.text().toUShort(); else if (eProp.tagName() == "param") pController->param = eProp.text().toUShort(); else if (eProp.tagName() == "logarithmic") pController->logarithmic = qtractorDocument::boolFromText(eProp.text()); else if (eProp.tagName() == "feedback") pController->feedback = qtractorDocument::boolFromText(eProp.text()); else if (eProp.tagName() == "invert") pController->invert = qtractorDocument::boolFromText(eProp.text()); else if (eProp.tagName() == "hook") pController->hook = qtractorDocument::boolFromText(eProp.text()); else if (eProp.tagName() == "latch") pController->latch = qtractorDocument::boolFromText(eProp.text()); } controllers.append(pController); } } } // Save meter controllers (MIDI). void qtractorMidiControl::saveControllers ( qtractorDocument *pDocument, QDomElement *pElement, const Controllers& controllers ) { QListIterator iter(controllers); while (iter.hasNext()) { Controller *pController = iter.next(); QDomElement eController = pDocument->document()->createElement("controller"); eController.setAttribute("name", pController->name); eController.setAttribute("index", QString::number(pController->index)); eController.setAttribute("type", textFromType(pController->ctype)); pDocument->saveTextElement("channel", QString::number(pController->channel), &eController); pDocument->saveTextElement("param", QString::number(pController->param), &eController); pDocument->saveTextElement("logarithmic", qtractorDocument::textFromBool(pController->logarithmic), &eController); pDocument->saveTextElement("feedback", qtractorDocument::textFromBool(pController->feedback), &eController); pDocument->saveTextElement("invert", qtractorDocument::textFromBool(pController->invert), &eController); pDocument->saveTextElement("hook", qtractorDocument::textFromBool(pController->hook), &eController); pDocument->saveTextElement("latch", qtractorDocument::textFromBool(pController->latch), &eController); pElement->appendChild(eController); } } //---------------------------------------------------------------------------- // MIDI Controller Type Text/Names - Default control types hash maps. static QHash g_controlTypeTexts; static QHash g_textControlTypes; static QHash g_controlTypeNames; static QHash g_nameControlTypes; void qtractorMidiControl::initControlTypes (void) { static struct { qtractorMidiControl::ControlType ctype; const char *text; const char *name; } s_aControlTypes[] = { { qtractorMidiEvent::NOTEON, "NOTEON", QT_TR_NOOP("Note On") }, { qtractorMidiEvent::NOTEOFF, "NOTEOFF", QT_TR_NOOP("Note Off") }, { qtractorMidiEvent::KEYPRESS, "KEYPRESS", QT_TR_NOOP("Key Press") }, { qtractorMidiEvent::CONTROLLER, "CONTROLLER", QT_TR_NOOP("Controller") }, { qtractorMidiEvent::PGMCHANGE, "PGMCHANGE", QT_TR_NOOP("Pgm Change") }, { qtractorMidiEvent::CHANPRESS, "CHANPRESS", QT_TR_NOOP("Chan Press") }, { qtractorMidiEvent::PITCHBEND, "PITCHBEND", QT_TR_NOOP("Pitch Bend") }, { qtractorMidiEvent::REGPARAM, "REGPARAM", QT_TR_NOOP("RPN") }, { qtractorMidiEvent::NONREGPARAM,"NONREGPARAM", QT_TR_NOOP("NRPN") }, { qtractorMidiEvent::CONTROL14, "CONTROL14", QT_TR_NOOP("Control 14") }, { qtractorMidiControl::ControlType(0), nullptr, nullptr } }; if (g_controlTypeNames.isEmpty()) { // Pre-load ontrol-types hash table... for (int i = 0; s_aControlTypes[i].name; ++i) { qtractorMidiControl::ControlType ctype = s_aControlTypes[i].ctype; const QString& sText = QString(s_aControlTypes[i].text); const QString& sName = tr(s_aControlTypes[i].name); g_controlTypeTexts.insert(ctype, sText); g_controlTypeNames.insert(ctype, sName); g_textControlTypes.insert(sText, ctype); g_nameControlTypes.insert(sName, ctype); } } } // Control type text (translatable) conversion helpers. qtractorMidiControl::ControlType qtractorMidiControl::typeFromText ( const QString& sText ) { initControlTypes(); return g_textControlTypes.value(sText, qtractorMidiControl::ControlType(0)); } QString qtractorMidiControl::textFromType ( qtractorMidiControl::ControlType ctype ) { initControlTypes(); return g_controlTypeTexts.value(ctype); } // Control type name (label) conversion helpers. qtractorMidiControl::ControlType qtractorMidiControl::typeFromName ( const QString& sName ) { initControlTypes(); return g_nameControlTypes.value(sName, qtractorMidiControl::ControlType(0)); } QString qtractorMidiControl::nameFromType ( qtractorMidiControl::ControlType ctype ) { initControlTypes(); return g_controlTypeNames.value(ctype); } //---------------------------------------------------------------------------- // MIDI Controller Command Text/Names - Default command names hash map. static QHash g_commandTexts; static QHash g_textCommands; static QHash g_commandNames; static QHash g_nameCommands; void qtractorMidiControl::initCommandNames (void) { static struct { qtractorMidiControl::Command command; const char *text; const char *name; } s_aCommandNames[] = { { TRACK_GAIN, "TRACK_GAIN", QT_TR_NOOP("Track Gain") }, { TRACK_PANNING, "TRACK_PANNING", QT_TR_NOOP("Track Panning") }, { TRACK_MONITOR, "TRACK_MONITOR", QT_TR_NOOP("Track Monitor") }, { TRACK_RECORD, "TRACK_RECORD", QT_TR_NOOP("Track Record") }, { TRACK_MUTE, "TRACK_MUTE", QT_TR_NOOP("Track Mute") }, { TRACK_SOLO, "TRACK_SOLO", QT_TR_NOOP("Track Solo") }, { Command(0), nullptr, nullptr } }; if (g_commandNames.isEmpty()) { // Pre-load command-names hash table... for (int i = 0; s_aCommandNames[i].name; ++i) { qtractorMidiControl::Command command = s_aCommandNames[i].command; const QString& sText = QString(s_aCommandNames[i].text); const QString& sName = tr(s_aCommandNames[i].name); g_commandNames.insert(command, sName); g_commandTexts.insert(command, sText); g_nameCommands.insert(sName, command); g_textCommands.insert(sText, command); } } } qtractorMidiControl::Command qtractorMidiControl::commandFromText ( const QString& sText ) { #if 0 initCommandNames(); return g_textCommands.value(sText, Command(0)); #else if (sText == "TRACK_GAIN" || sText == "TrackGain") return TRACK_GAIN; else if (sText == "TRACK_PANNING" || sText == "TrackPanning") return TRACK_PANNING; else if (sText == "TRACK_MONITOR" || sText == "TrackMonitor") return TRACK_MONITOR; else if (sText == "TRACK_RECORD" || sText == "TrackRecord") return TRACK_RECORD; else if (sText == "TRACK_MUTE" || sText == "TrackMute") return TRACK_MUTE; else if (sText == "TRACK_SOLO" || sText == "TrackSolo") return TRACK_SOLO; else return Command(0); #endif } QString qtractorMidiControl::textFromCommand ( Command command ) { initCommandNames(); return g_commandTexts.value(command); } qtractorMidiControl::Command qtractorMidiControl::commandFromName ( const QString& sName ) { initCommandNames(); return g_nameCommands.value(sName, Command(0)); } QString qtractorMidiControl::nameFromCommand ( Command command ) { initCommandNames(); return g_commandNames.value(command); } // MIDI control non catch-up/hook global option. bool qtractorMidiControl::g_bSync = false; void qtractorMidiControl::setSync ( bool bSync ) { g_bSync = bSync; } bool qtractorMidiControl::isSync (void) { return g_bSync; } // end of qtractorMidiControl.cpp qtractor-1.5.11/src/PaxHeaders/qtractor_plugin_scan.cpp0000644000000000000000000000013215124701674020237 xustar0030 mtime=1767080892.805263403 30 atime=1767080892.805263403 30 ctime=1767080892.805263403 qtractor-1.5.11/src/qtractor_plugin_scan.cpp0000644000175000001440000013120315124701674020227 0ustar00rncbcusers// qtractor_plugin_scan.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "config.h" #include "qtractor_plugin_scan.h" #include #include #include #include #include #include #ifdef CONFIG_LADSPA //---------------------------------------------------------------------- // class qtractor_ladspa_scan -- LADSPA plugin (bare bones) interface // // Constructor. qtractor_ladspa_scan::qtractor_ladspa_scan (void) : m_pLibrary(nullptr), m_pLadspaDescriptor(nullptr), m_iControlIns(0), m_iControlOuts(0), m_iAudioIns(0), m_iAudioOuts(0) { } // destructor. qtractor_ladspa_scan::~qtractor_ladspa_scan (void) { close(); } // File loader. bool qtractor_ladspa_scan::open ( const QString& sFilename ) { close(); if (!QLibrary::isLibrary(sFilename)) return false; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_ladspa_scan[%p]::open(\"%s\")", this, sFilename.toUtf8().constData()); #endif m_pLibrary = new QLibrary(sFilename); return true; } // Plugin loader. bool qtractor_ladspa_scan::open_descriptor ( unsigned long iIndex ) { if (m_pLibrary == nullptr) return false; close_descriptor(); #ifdef CONFIG_DEBUG_0 qDebug("qtractor_ladspa_scan[%p]::open_descriptor(%lu)", this, iIndex); #endif // Retrieve the LADSPA descriptor function, if any... LADSPA_Descriptor_Function pfnLadspaDescriptor = (LADSPA_Descriptor_Function) m_pLibrary->resolve("ladspa_descriptor"); if (pfnLadspaDescriptor == nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractor_ladspa_scan[%p]: plugin does not have DSSI descriptor.", this); #endif return false; } // Retrieve LADSPA descriptor if any... m_pLadspaDescriptor = (*pfnLadspaDescriptor)(iIndex); if (m_pLadspaDescriptor == nullptr) return false; // Get the official plugin name. m_sName = QString::fromLatin1(m_pLadspaDescriptor->Name); // Compute and cache port counts... m_iControlIns = 0; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; for (unsigned long i = 0; i < m_pLadspaDescriptor->PortCount; ++i) { const LADSPA_PortDescriptor portType = m_pLadspaDescriptor->PortDescriptors[i]; if (LADSPA_IS_PORT_INPUT(portType)) { if (LADSPA_IS_PORT_AUDIO(portType)) ++m_iAudioIns; else if (LADSPA_IS_PORT_CONTROL(portType)) ++m_iControlIns; } else if (LADSPA_IS_PORT_OUTPUT(portType)) { if (LADSPA_IS_PORT_AUDIO(portType)) ++m_iAudioOuts; else if (LADSPA_IS_PORT_CONTROL(portType)) ++m_iControlOuts; } } return true; } // Plugin uloader. void qtractor_ladspa_scan::close_descriptor (void) { if (m_pLadspaDescriptor == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_ladspa_scan[%p]::close_descriptor()", this); #endif m_pLadspaDescriptor = nullptr; m_sName.clear(); m_iControlIns = 0; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; } // File unloader. void qtractor_ladspa_scan::close (void) { close_descriptor(); if (m_pLibrary == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_ladspa_scan[%p]::close()", this); #endif #if 0 if (m_pLibrary->isLoaded()) m_pLibrary->unload(); #endif delete m_pLibrary; m_pLibrary = nullptr; } // Check wether plugin is loaded. bool qtractor_ladspa_scan::isOpen (void) const { if (m_pLibrary == nullptr) return false; return m_pLibrary->isLoaded(); } unsigned int qtractor_ladspa_scan::uniqueID() const { return (m_pLadspaDescriptor ? m_pLadspaDescriptor->UniqueID : 0); } bool qtractor_ladspa_scan::isRealtime() const { if (m_pLadspaDescriptor == nullptr) return false; return LADSPA_IS_HARD_RT_CAPABLE(m_pLadspaDescriptor->Properties); } //------------------------------------------------------------------------- // The LADSPA plugin stance scan method. // static void qtractor_ladspa_scan_file ( const QString& sFilename ) { #ifdef CONFIG_DEBUG qDebug("qtractor_ladspa_scan_file(\"%s\")", sFilename.toUtf8().constData()); #endif qtractor_ladspa_scan plugin; if (!plugin.open(sFilename)) return; QTextStream sout(stdout); unsigned long i = 0; while (plugin.open_descriptor(i)) { sout << "LADSPA|"; sout << plugin.name() << '|'; sout << plugin.audioIns() << ':' << plugin.audioOuts() << '|'; sout << 0 << ':' << 0 << '|'; sout << plugin.controlIns() << ':' << plugin.controlOuts() << '|'; QStringList flags; if (plugin.isRealtime()) flags.append("RT"); sout << flags.join(",") << '|'; sout << sFilename << '|' << i << '|'; sout << "0x" << QString::number(plugin.uniqueID(), 16) << '\n'; plugin.close_descriptor(); ++i; } plugin.close(); // Must always give an answer, even if it's a wrong one... if (i == 0) sout << "qtractor_ladspa_scan: " << sFilename << ": plugin file error.\n"; } #endif // CONFIG_LADSPA #ifdef CONFIG_DSSI //---------------------------------------------------------------------- // class qtractor_dssi_scan -- LADSPA plugin re bones) interface // // Constructor. qtractor_dssi_scan::qtractor_dssi_scan (void) : m_pLibrary(nullptr), m_pLadspaDescriptor(nullptr), m_iControlIns(0), m_iControlOuts(0), m_iAudioIns(0), m_iAudioOuts(0), m_bEditor(false) { } // destructor. qtractor_dssi_scan::~qtractor_dssi_scan (void) { close(); } // File loader. bool qtractor_dssi_scan::open ( const QString& sFilename ) { close(); if (!QLibrary::isLibrary(sFilename)) return false; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_dssi_scan[%p]::open(\"%s\")", this, sFilename.toUtf8().constData()); #endif m_pLibrary = new QLibrary(sFilename); return true; } // Plugin loader. bool qtractor_dssi_scan::open_descriptor ( unsigned long iIndex ) { if (m_pLibrary == nullptr) return false; close_descriptor(); #ifdef CONFIG_DEBUG_0 qDebug("qtractor_dssi_scan[%p]::open_descriptor(%lu)", this, iIndex); #endif // Retrieve the DSSI descriptor function, if any... DSSI_Descriptor_Function pfnDssiDescriptor = (DSSI_Descriptor_Function) m_pLibrary->resolve("dssi_descriptor"); if (pfnDssiDescriptor == nullptr) return false; if (pfnDssiDescriptor == nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractor_dssi_scan[%p]: plugin does not have DSSI descriptor.", this); #endif return false; } // Retrieve the DSSI descriptor if any... m_pDssiDescriptor = (*pfnDssiDescriptor)(iIndex); if (m_pDssiDescriptor == nullptr) return false; // We're also a LADSPA one... m_pLadspaDescriptor = m_pDssiDescriptor->LADSPA_Plugin; if (m_pLadspaDescriptor == nullptr) return false; // Get the official plugin name. m_sName = QString::fromLatin1(m_pLadspaDescriptor->Name); // Compute and cache port counts... m_iControlIns = 0; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; for (unsigned long i = 0; i < m_pLadspaDescriptor->PortCount; ++i) { const LADSPA_PortDescriptor portType = m_pLadspaDescriptor->PortDescriptors[i]; if (LADSPA_IS_PORT_INPUT(portType)) { if (LADSPA_IS_PORT_AUDIO(portType)) ++m_iAudioIns; else if (LADSPA_IS_PORT_CONTROL(portType)) ++m_iControlIns; } else if (LADSPA_IS_PORT_OUTPUT(portType)) { if (LADSPA_IS_PORT_AUDIO(portType)) ++m_iAudioOuts; else if (LADSPA_IS_PORT_CONTROL(portType)) ++m_iControlOuts; } } m_bEditor = false; // Check for GUI editor executable... const QFileInfo fi(m_pLibrary->fileName()); const QFileInfo gi(fi.dir(), fi.baseName()); if (gi.isDir()) { QDir dir(gi.absoluteFilePath()); const QString sMask("%1_*"); QStringList names; names.append(sMask.arg(fi.baseName())); names.append(sMask.arg(m_pLadspaDescriptor->Label)); dir.setNameFilters(names); m_bEditor = !dir.entryList(QDir::Files | QDir::Executable).isEmpty(); } return true; } // Plugin uloader. void qtractor_dssi_scan::close_descriptor (void) { if (m_pLadspaDescriptor == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_dssi_scan[%p]::close_descriptor()", this); #endif m_pLadspaDescriptor = nullptr; m_sName.clear(); m_iControlIns = 0; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; m_bEditor = false; } // File unloader. void qtractor_dssi_scan::close (void) { close_descriptor(); if (m_pLibrary == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_dssi_scan[%p]::close()", this); #endif #if 0 if (m_pLibrary->isLoaded()) m_pLibrary->unload(); #endif delete m_pLibrary; m_pLibrary = nullptr; } // Check wether plugin is loaded. bool qtractor_dssi_scan::isOpen (void) const { if (m_pLibrary == nullptr) return false; return m_pLibrary->isLoaded(); } unsigned int qtractor_dssi_scan::uniqueID() const { return (m_pLadspaDescriptor ? m_pLadspaDescriptor->UniqueID : 0); } bool qtractor_dssi_scan::isRealtime() const { if (m_pLadspaDescriptor == nullptr) return false; return LADSPA_IS_HARD_RT_CAPABLE(m_pLadspaDescriptor->Properties); } bool qtractor_dssi_scan::isConfigure() const { if (m_pDssiDescriptor == nullptr) return false; return (m_pDssiDescriptor->configure != nullptr); } //------------------------------------------------------------------------- // The DSSI plugin stance scan method. // static void qtractor_dssi_scan_file ( const QString& sFilename ) { #ifdef CONFIG_DEBUG qDebug("qtractor_dssi_scan_file(\"%s\")", sFilename.toUtf8().constData()); #endif qtractor_dssi_scan plugin; if (!plugin.open(sFilename)) return; QTextStream sout(stdout); unsigned long i = 0; while (plugin.open_descriptor(i)) { sout << "DSSI|"; sout << plugin.name() << '|'; sout << plugin.audioIns() << ':' << plugin.audioOuts() << '|'; sout << 1 << ':' << 0 << '|'; sout << plugin.controlIns() << ':' << plugin.controlOuts() << '|'; QStringList flags; if (plugin.isEditor()) flags.append("GUI"); if (plugin.isConfigure()) flags.append("EXT"); if (plugin.isRealtime()) flags.append("RT"); sout << flags.join(",") << '|'; sout << sFilename << '|' << i << '|'; sout << "0x" << QString::number(plugin.uniqueID(), 16) << '\n'; plugin.close_descriptor(); ++i; } plugin.close(); // Must always give an answer, even if it's a wrong one... if (i == 0) sout << "qtractor_dssi_scan: " << sFilename << ": plugin file error.\n"; } #endif // CONFIG_DSSI #ifdef CONFIG_VST2 #if !defined(__WIN32__) && !defined(_WIN32) && !defined(WIN32) #define __cdecl #endif #ifdef CONFIG_VESTIGE #include #else #include #endif #if !defined(VST_2_3_EXTENSIONS) #ifdef CONFIG_VESTIGE typedef int32_t VstInt32; typedef intptr_t VstIntPtr; #else typedef long VstInt32; typedef long VstIntPtr; #endif #define VSTCALLBACK #endif typedef AEffect* (*VST_GetPluginInstance) (audioMasterCallback); static VstIntPtr VSTCALLBACK qtractor_vst2_scan_callback (AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt); // Current working VST Shell identifier. static int g_iVst2ShellCurrentId = 0; // Specific extended flags that saves us // from calling canDo() in audio callbacks. enum VST_FlagsEx { effFlagsExCanSendVstEvents = 1 << 0, effFlagsExCanSendVstMidiEvents = 1 << 1, effFlagsExCanSendVstTimeInfo = 1 << 2, effFlagsExCanReceiveVstEvents = 1 << 3, effFlagsExCanReceiveVstMidiEvents = 1 << 4, effFlagsExCanReceiveVstTimeInfo = 1 << 5, effFlagsExCanProcessOffline = 1 << 6, effFlagsExCanUseAsInsert = 1 << 7, effFlagsExCanUseAsSend = 1 << 8, effFlagsExCanMixDryWet = 1 << 9, effFlagsExCanMidiProgramNames = 1 << 10 }; // Some VeSTige missing opcodes and flags. #ifdef CONFIG_VESTIGE const int effSetProgramName = 4; const int effGetParamLabel = 6; const int effGetParamDisplay = 7; const int effGetChunk = 23; const int effSetChunk = 24; const int effFlagsProgramChunks = 32; #endif //---------------------------------------------------------------------- // class qtractor_vst2_scan -- VST2 plugin re bones) interface // // Constructor. qtractor_vst2_scan::qtractor_vst2_scan (void) : m_pLibrary(nullptr), m_pEffect(nullptr), m_iFlagsEx(0), m_bEditor(false) { } // destructor. qtractor_vst2_scan::~qtractor_vst2_scan (void) { close(); } // File loader. bool qtractor_vst2_scan::open ( const QString& sFilename ) { close(); if (!QLibrary::isLibrary(sFilename)) return false; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_vst2_scan[%p]::open(\"%s\", %lu)", this, sFilename.toUtf8().constData()); #endif m_pLibrary = new QLibrary(sFilename); m_sName = QFileInfo(sFilename).baseName(); return true; } // Plugin loader. bool qtractor_vst2_scan::open_descriptor ( unsigned long iIndex ) { if (m_pLibrary == nullptr) return false; close_descriptor(); #ifdef CONFIG_DEBUG_0 qDebug("qtractor_vst2_scan[%p]::open_descriptor(%lu)", this, iIndex); #endif VST_GetPluginInstance pfnGetPluginInstance = (VST_GetPluginInstance) m_pLibrary->resolve("VSTPluginMain"); if (pfnGetPluginInstance == nullptr) pfnGetPluginInstance = (VST_GetPluginInstance) m_pLibrary->resolve("main"); if (pfnGetPluginInstance == nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst2_scan[%p]: plugin does not have a main entry point.", this); #endif return false; } m_pEffect = (*pfnGetPluginInstance)(qtractor_vst2_scan_callback); if (m_pEffect == nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst2_scan[%p]: plugin instance could not be created.", this); #endif return false; } // Did VST plugin instantiated OK? if (m_pEffect->magic != kEffectMagic) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst2_scan[%p]: plugin is not a valid VST.", this); #endif m_pEffect = nullptr; return false; } // Check whether it's a VST Shell... const int categ = vst2_dispatch(effGetPlugCategory, 0, 0, nullptr, 0.0f); if (categ == kPlugCategShell) { int id = 0; char buf[40]; unsigned long i = 0; for ( ; iIndex >= i; ++i) { buf[0] = (char) 0; id = vst2_dispatch(effShellGetNextPlugin, 0, 0, (void *) buf, 0.0f); if (id == 0 || !buf[0]) break; } // Check if we're actually the intended plugin... if (i < iIndex || id == 0 || !buf[0]) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst2_scan[%p]: " "vst2_shell(%lu) plugin is not a valid VST.", this, iIndex); #endif m_pEffect = nullptr; return false; } // Make it known... g_iVst2ShellCurrentId = id; // Re-allocate the thing all over again... pfnGetPluginInstance = (VST_GetPluginInstance) m_pLibrary->resolve("VSTPluginMain"); if (pfnGetPluginInstance == nullptr) pfnGetPluginInstance = (VST_GetPluginInstance) m_pLibrary->resolve("main"); if (pfnGetPluginInstance == nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst2_scan[%p]: " "vst2_shell(%lu) plugin does not have a main entry point.", this, iIndex); #endif m_pEffect = nullptr; return false; } // Does the VST plugin instantiate OK? m_pEffect = (*pfnGetPluginInstance)(qtractor_vst2_scan_callback); // Not needed anymore, hopefully... g_iVst2ShellCurrentId = 0; // Don't go further if failed... if (m_pEffect == nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst2_scan[%p]: " "vst2_shell(%lu) plugin instance could not be created.", this, iIndex); #endif return false; } if (m_pEffect->magic != kEffectMagic) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst2_scan[%p]: " "vst2_shell(%lu) plugin is not a valid VST.", this, iIndex); #endif m_pEffect = nullptr; return false; } #ifdef CONFIG_DEBUG qDebug("qtractor_vst2_scan[%p]: " "vst2_shell(%lu) id=0x%x name=\"%s\"", this, i, id, buf); #endif } else // Not a VST Shell plugin... if (iIndex > 0) { m_pEffect = nullptr; return false; } // vst2_dispatch(effIdentify, 0, 0, nullptr, 0.0f); vst2_dispatch(effOpen, 0, 0, nullptr, 0.0f); // Get label name... char szName[256]; ::memset(szName, 0, sizeof(szName)); if (vst2_dispatch(effGetEffectName, 0, 0, (void *) szName, 0.0f)) m_sName = szName; // Specific inquiries... m_iFlagsEx = 0; if (vst2_canDo("sendVstEvents")) m_iFlagsEx |= effFlagsExCanSendVstEvents; if (vst2_canDo("sendVstMidiEvent")) m_iFlagsEx |= effFlagsExCanSendVstMidiEvents; if (vst2_canDo("sendVstTimeInfo")) m_iFlagsEx |= effFlagsExCanSendVstTimeInfo; if (vst2_canDo("receiveVstEvents")) m_iFlagsEx |= effFlagsExCanReceiveVstEvents; if (vst2_canDo("receiveVstMidiEvent")) m_iFlagsEx |= effFlagsExCanReceiveVstMidiEvents; if (vst2_canDo("receiveVstTimeInfo")) m_iFlagsEx |= effFlagsExCanReceiveVstTimeInfo; if (vst2_canDo("offline")) m_iFlagsEx |= effFlagsExCanProcessOffline; if (vst2_canDo("plugAsChannelInsert")) m_iFlagsEx |= effFlagsExCanUseAsInsert; if (vst2_canDo("plugAsSend")) m_iFlagsEx |= effFlagsExCanUseAsSend; if (vst2_canDo("mixDryWet")) m_iFlagsEx |= effFlagsExCanMixDryWet; if (vst2_canDo("midiProgramNames")) m_iFlagsEx |= effFlagsExCanMidiProgramNames; m_bEditor = (m_pEffect->flags & effFlagsHasEditor); return true; } // Plugin unloader. void qtractor_vst2_scan::close_descriptor (void) { if (m_pEffect == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_vst2_scan[%p]::close_descriptor()", this); #endif vst2_dispatch(effClose, 0, 0, 0, 0.0f); m_pEffect = nullptr; m_iFlagsEx = 0; // m_bEditor = false; m_sName.clear(); } // File unloader. void qtractor_vst2_scan::close (void) { if (m_pLibrary == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_vst2_scan[%p]::close()", this); #endif vst2_dispatch(effClose, 0, 0, 0, 0.0f); if (m_pLibrary->isLoaded() && !m_bEditor) m_pLibrary->unload(); delete m_pLibrary; m_pLibrary = nullptr; m_bEditor = false; } // Check wether plugin is loaded. bool qtractor_vst2_scan::isOpen (void) const { if (m_pLibrary == nullptr) return false; return m_pLibrary->isLoaded(); } unsigned int qtractor_vst2_scan::uniqueID() const { return (m_pEffect ? m_pEffect->uniqueID : 0); } int qtractor_vst2_scan::numPrograms() const { return (m_pEffect ? m_pEffect->numPrograms : 0); } int qtractor_vst2_scan::numParams() const { return (m_pEffect ? m_pEffect->numParams : 0); } int qtractor_vst2_scan::numInputs() const { return (m_pEffect ? m_pEffect->numInputs : 0); } int qtractor_vst2_scan::numOutputs() const { return (m_pEffect ? m_pEffect->numOutputs : 0); } int qtractor_vst2_scan::numMidiInputs() const { return (m_pEffect && ( (m_iFlagsEx & effFlagsExCanReceiveVstMidiEvents) || (m_pEffect->flags & effFlagsIsSynth) ? 1 : 0)); } int qtractor_vst2_scan::numMidiOutputs() const { return ((m_iFlagsEx & effFlagsExCanSendVstMidiEvents) ? 1 : 0); } bool qtractor_vst2_scan::hasEditor() const { return m_bEditor; } bool qtractor_vst2_scan::hasProgramChunks() const { return (m_pEffect && (m_pEffect->flags & effFlagsProgramChunks)); } // VST host dispatcher. int qtractor_vst2_scan::vst2_dispatch ( long opcode, long index, long value, void *ptr, float opt ) const { if (m_pEffect == nullptr) return 0; #ifdef CONFIG_DEBUG_0 qDebug("vst2_plugin[%p]::vst2_dispatch(%ld, %ld, %ld, %p, %g)", this, opcode, index, value, ptr, opt); #endif return m_pEffect->dispatcher(m_pEffect, opcode, index, value, ptr, opt); } // VST flag inquirer. bool qtractor_vst2_scan::vst2_canDo ( const char *pszCanDo ) const { return (vst2_dispatch(effCanDo, 0, 0, (void *) pszCanDo, 0.0f) > 0); } //---------------------------------------------------------------------- // The magnificient host callback, which every VST plugin will call. static VstIntPtr VSTCALLBACK qtractor_vst2_scan_callback ( AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr /*value*/, void */*ptr*/, float opt ) { VstIntPtr ret = 0; switch (opcode) { case audioMasterVersion: ret = 2; break; case audioMasterAutomate: effect->setParameter(effect, index, opt); break; case audioMasterCurrentId: ret = (VstIntPtr) g_iVst2ShellCurrentId; break; case audioMasterGetSampleRate: effect->dispatcher(effect, effSetSampleRate, 0, 0, nullptr, 44100.0f); break; case audioMasterGetBlockSize: effect->dispatcher(effect, effSetBlockSize, 0, 1024, nullptr, 0.0f); break; case audioMasterGetAutomationState: ret = 1; // off break; case audioMasterGetLanguage: ret = kVstLangEnglish; break; default: break; } return ret; } //------------------------------------------------------------------------- // The VST plugin stance scan method. // static void qtractor_vst2_scan_file ( const QString& sFilename ) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst2_scan_file(\"%s\")", sFilename.toUtf8().constData()); #endif qtractor_vst2_scan plugin; if (!plugin.open(sFilename)) return; QTextStream sout(stdout); unsigned long i = 0; while (plugin.open_descriptor(i)) { sout << "VST2|"; sout << plugin.name() << '|'; sout << plugin.numInputs() << ':' << plugin.numOutputs() << '|'; sout << plugin.numMidiInputs() << ':' << plugin.numMidiOutputs() << '|'; sout << plugin.numParams() << ':' << 0 << '|'; QStringList flags; if (plugin.hasEditor()) flags.append("GUI"); if (plugin.hasProgramChunks()) flags.append("EXT"); flags.append("RT"); sout << flags.join(",") << '|'; sout << sFilename << '|' << i << '|'; sout << "0x" << QString::number(plugin.uniqueID(), 16) << '\n'; plugin.close_descriptor(); ++i; } plugin.close(); // Must always give an answer, even if it's a wrong one... if (i == 0) sout << "qtractor_vst2_scan: " << sFilename << ": plugin file error.\n"; } #endif // CONFIG_VST2 #ifdef CONFIG_VST3 #include "pluginterfaces/vst/ivsthostapplication.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/gui/iplugview.h" //----------------------------------------------------------------------------- using namespace Steinberg; //----------------------------------------------------------------------------- class qtractor_vst3_scan_host : public Vst::IHostApplication { public: qtractor_vst3_scan_host () { FUNKNOWN_CTOR } virtual ~qtractor_vst3_scan_host () { FUNKNOWN_DTOR } DECLARE_FUNKNOWN_METHODS //--- IHostApplication ---- // tresult PLUGIN_API getName (Vst::String128 name) override { const QString str("qtractor_plugin_scan"); const int nsize = qMin(str.length(), 127); ::memcpy(name, str.utf16(), nsize * sizeof(Vst::TChar)); name[nsize] = 0; return kResultOk; } tresult PLUGIN_API createInstance (TUID /*cid*/, TUID /*_iid*/, void **obj) override { *obj = nullptr; return kResultFalse; } FUnknown *get() { return static_cast (this); } }; tresult PLUGIN_API qtractor_vst3_scan_host::queryInterface ( const char *_iid, void **obj ) { QUERY_INTERFACE(_iid, obj, FUnknown::iid, IHostApplication) QUERY_INTERFACE(_iid, obj, IHostApplication::iid, IHostApplication) *obj = nullptr; return kNoInterface; } uint32 PLUGIN_API qtractor_vst3_scan_host::addRef (void) { return 1; } uint32 PLUGIN_API qtractor_vst3_scan_host::release (void) { return 1; } static qtractor_vst3_scan_host g_vst3HostContext; //---------------------------------------------------------------------- // class qtractor_vst3_scan::Impl -- VST3 plugin interface impl. // class qtractor_vst3_scan::Impl { public: // Constructor. Impl() : m_module(nullptr), m_component(nullptr), m_controller(nullptr) {} // destructor. ~Impl() { close_descriptor(); close(); } // File loader. bool open ( const QString& sFilename ) { close(); const QByteArray aFilename = sFilename.toUtf8(); m_module = ::dlopen(sFilename.toUtf8().constData(), RTLD_LOCAL | RTLD_LAZY); if (!m_module) return false; typedef bool (*VST3_ModuleEntry)(void *); const VST3_ModuleEntry module_entry = VST3_ModuleEntry(::dlsym(m_module, "ModuleEntry")); if (module_entry) module_entry(m_module); return true; } bool open_descriptor ( unsigned long iIndex ) { if (!m_module) return false; close_descriptor(); typedef IPluginFactory *(*VST3_GetFactory)(); const VST3_GetFactory get_plugin_factory = VST3_GetFactory(::dlsym(m_module, "GetPluginFactory")); if (!get_plugin_factory) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst3_scan::Impl[%p]::open_descriptor(%lu)" " *** Failed to resolve plug-in factory.", this, iIndex); #endif return false; } IPluginFactory *factory = get_plugin_factory(); if (!factory) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst3_scan::Impl[%p]::open_descriptor(%lu)" " *** Failed to retrieve plug-in factory.", this, iIndex); #endif return false; } const int32 nclasses = factory->countClasses(); unsigned long i = 0; for (int32 n = 0; n < nclasses; ++n) { PClassInfo classInfo; if (factory->getClassInfo(n, &classInfo) != kResultOk) continue; if (::strcmp(classInfo.category, kVstAudioEffectClass)) continue; if (iIndex == i) { m_classInfo = classInfo; Vst::IComponent *component = nullptr; if (factory->createInstance( classInfo.cid, Vst::IComponent::iid, (void **) &component) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst3_scan::Impl[%p]::open_descriptor(%lu)" " *** Failed to create plug-in component.", this, iIndex); #endif return false; } m_component = owned(component); if (m_component->initialize(g_vst3HostContext.get()) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst3_scan::Impl[%p]::open_descriptor(%lu)" " *** Failed to initialize plug-in component.", this, iIndex); #endif close_descriptor(); return false; } Vst::IEditController *controller = nullptr; if (m_component->queryInterface( Vst::IEditController::iid, (void **) &controller) != kResultOk) { TUID controller_cid; if (m_component->getControllerClassId(controller_cid) == kResultOk) { if (factory->createInstance( controller_cid, Vst::IEditController::iid, (void **) &controller) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst3_scan::Impl[%p]::open_descriptor(%lu)" " *** Failed to create plug-in controller.", this, iIndex); #endif } if (controller && controller->initialize(g_vst3HostContext.get()) != kResultOk) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst3_scan::Impl[%p]::open_descriptor(%lu)" " *** Failed to initialize plug-in controller.", this, iIndex); controller = nullptr; #endif } } } if (controller) m_controller = owned(controller); // Connect components... if (m_component && m_controller) { FUnknownPtr component_cp(m_component); FUnknownPtr controller_cp(m_controller); if (component_cp && controller_cp) { component_cp->connect(controller_cp); controller_cp->connect(component_cp); } } return true; } ++i; } return false; } void close_descriptor () { if (m_component && m_controller) { FUnknownPtr component_cp(m_component); FUnknownPtr controller_cp(m_controller); if (component_cp && controller_cp) { component_cp->disconnect(controller_cp); controller_cp->disconnect(component_cp); } } if (m_component && m_controller && FUnknownPtr (m_component).getInterface()) { m_controller->terminate(); } m_controller = nullptr; if (m_component) { m_component->terminate(); m_component = nullptr; } } void close () { if (!m_module) return; typedef void (*VST3_ModuleExit)(); const VST3_ModuleExit module_exit = VST3_ModuleExit(::dlsym(m_module, "ModuleExit")); if (module_exit) module_exit(); ::dlclose(m_module); m_module = nullptr; } // Accessors. Vst::IComponent *component() const { return m_component; } Vst::IEditController *controller() const { return m_controller; } const PClassInfo& classInfo() const { return m_classInfo; } int numChannels ( Vst::MediaType type, Vst::BusDirection direction ) const { if (!m_component) return -1; int nchannels = 0; int nactive = 0; const int32 nbuses = m_component->getBusCount(type, direction); for (int32 i = 0; i < nbuses; ++i) { Vst::BusInfo busInfo; if (m_component->getBusInfo(type, direction, i, busInfo) == kResultOk) { if (busInfo.busType == Vst::kMain) { nchannels += busInfo.channelCount; if (busInfo.flags & Vst::BusInfo::kDefaultActive) nactive += busInfo.channelCount; } } } return (nactive > 0 ? nactive : nchannels); } private: // Instance variables. void *m_module; PClassInfo m_classInfo; IPtr m_component; IPtr m_controller; }; //---------------------------------------------------------------------- // class qtractor_vst3_scan -- VST3 plugin interface // // Constructor. qtractor_vst3_scan::qtractor_vst3_scan (void) : m_pImpl(new Impl()) { clear(); } // destructor. qtractor_vst3_scan::~qtractor_vst3_scan (void) { close_descriptor(); close(); delete m_pImpl; } // File loader. bool qtractor_vst3_scan::open ( const QString& sFilename ) { close(); #ifdef CONFIG_DEBUG_0 qDebug("qtractor_vst3_scan[%p]::open(\"%s\")", this, sFilename.toUtf8().constData()); #endif return m_pImpl->open(sFilename); } bool qtractor_vst3_scan::open_descriptor ( unsigned long iIndex ) { close_descriptor(); #ifdef CONFIG_DEBUG_0 qDebug("qtractor_vst3_scan[%p]::open_descriptor( %lu)", this, iIndex); #endif if (!m_pImpl->open_descriptor(iIndex)) return false; const PClassInfo& classInfo = m_pImpl->classInfo(); m_sName = QString::fromLocal8Bit(classInfo.name); m_iUniqueID = qHash(QByteArray(classInfo.cid, sizeof(TUID))); m_iAudioIns = m_pImpl->numChannels(Vst::kAudio, Vst::kInput); m_iAudioOuts = m_pImpl->numChannels(Vst::kAudio, Vst::kOutput); m_iMidiIns = m_pImpl->numChannels(Vst::kEvent, Vst::kInput); m_iMidiOuts = m_pImpl->numChannels(Vst::kEvent, Vst::kOutput); Vst::IEditController *controller = m_pImpl->controller(); if (controller) { IPtr editor = owned(controller->createView(Vst::ViewType::kEditor)); m_bEditor = (editor != nullptr); } m_iControlIns = 0; m_iControlOuts = 0; if (controller) { const int32 nparams = controller->getParameterCount(); for (int32 i = 0; i < nparams; ++i) { Vst::ParameterInfo paramInfo; if (controller->getParameterInfo(i, paramInfo) == kResultOk) { if (paramInfo.flags & Vst::ParameterInfo::kIsReadOnly) ++m_iControlOuts; else if (paramInfo.flags & Vst::ParameterInfo::kCanAutomate) ++m_iControlIns; } } } return true; } // File unloader. void qtractor_vst3_scan::close_descriptor (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractor_vst3_scan[%p]::close_descriptor()", this); #endif m_pImpl->close_descriptor(); clear(); } void qtractor_vst3_scan::close (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractor_vst3_scan[%p]::close()", this); #endif m_pImpl->close(); } // Properties. bool qtractor_vst3_scan::isOpen (void) const { return (m_pImpl->controller() != nullptr); } // Cleaner/wiper. void qtractor_vst3_scan::clear (void) { m_sName.clear(); m_iUniqueID = 0; m_iControlIns = 0; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; m_iMidiIns = 0; m_iMidiOuts = 0; m_bEditor = false; } //------------------------------------------------------------------------- // qtractor_vst3_scan_file - The main scan procedure. // static void qtractor_vst3_scan_file ( const QString& sFilename ) { #ifdef CONFIG_DEBUG qDebug("qtractor_vst3_scan_file(\"%s\")", sFilename.toUtf8().constData()); #endif qtractor_vst3_scan plugin; if (!plugin.open(sFilename)) return; QTextStream sout(stdout); unsigned long i = 0; while (plugin.open_descriptor(i)) { sout << "VST3|"; sout << plugin.name() << '|'; sout << plugin.audioIns() << ':' << plugin.audioOuts() << '|'; sout << plugin.midiIns() << ':' << plugin.midiOuts() << '|'; sout << plugin.controlIns() << ':' << plugin.controlOuts() << '|'; QStringList flags; if (plugin.hasEditor()) flags.append("GUI"); flags.append("EXT"); flags.append("RT"); sout << flags.join(",") << '|'; sout << sFilename << '|' << i << '|'; sout << "0x" << QString::number(plugin.uniqueID(), 16) << '\n'; plugin.close_descriptor(); ++i; } plugin.close(); // Must always give an answer, even if it's a wrong one... if (i == 0) sout << "qtractor_vst3_scan: " << sFilename << ": plugin file error.\n"; } #endif // CONFIG_VST3 #ifdef CONFIG_CLAP #include //---------------------------------------------------------------------- // class qtractor_clap_scan::Impl -- CLAP plugin interface impl. // class qtractor_clap_scan::Impl { public: // Constructor. Impl() : m_module(nullptr), m_entry(nullptr), m_factory(nullptr), m_plugin(nullptr) { ::memset(&m_host, 0, sizeof(m_host)); m_host.host_data = this; m_host.clap_version = CLAP_VERSION; m_host.name = "qtractor_plugin_scan"; m_host.version = PROJECT_VERSION; m_host.vendor = "rncbc.org"; m_host.url = "https://qtractor.org"; m_host.get_extension = qtractor_clap_scan::Impl::get_extension; m_host.request_restart = qtractor_clap_scan::Impl::request_restart; m_host.request_process = qtractor_clap_scan::Impl::request_process; m_host.request_callback = qtractor_clap_scan::Impl::request_callback; clear(); } // destructor. ~Impl() { close_descriptor(); close(); } static const void *get_extension(const clap_host *host, const char *ext_id) { return nullptr; } static void request_restart (const clap_host *host) {} static void request_process (const clap_host *host) {} static void request_callback(const clap_host *host) {} // File loader. bool open ( const QString& sFilename ) { close(); const QByteArray aFilename = sFilename.toUtf8(); m_module = ::dlopen(aFilename.constData(), RTLD_LOCAL | RTLD_LAZY); if (!m_module) return false; m_entry = reinterpret_cast ( ::dlsym(m_module, "clap_entry")); if (!m_entry) return false; m_entry->init(aFilename.constData()); m_factory = static_cast ( m_entry->get_factory(CLAP_PLUGIN_FACTORY_ID)); if (!m_factory) return false; return true; } bool open_descriptor ( unsigned long iIndex ) { if (!m_factory) return false; close_descriptor(); auto count = m_factory->get_plugin_count(m_factory); if (iIndex >= count) return false; auto desc = m_factory->get_plugin_descriptor(m_factory, iIndex); if (!desc) { qDebug("qtractor_clap_scan::Impl[%p]::open_descriptor(%lu)" " *** No plug-in descriptor.", this, iIndex); return false; } if (!clap_version_is_compatible(desc->clap_version)) { qDebug("qtractor_clap_scan::Impl[%p]::open_descriptor(%lu)" " *** Incompatible CLAP version:" " plug-in is %d.%d.%d, host is %d.%d.%d.", this, iIndex, desc->clap_version.major, desc->clap_version.minor, desc->clap_version.revision, CLAP_VERSION.major, CLAP_VERSION.minor, CLAP_VERSION.revision); return false; } m_plugin = m_factory->create_plugin(m_factory, &m_host, desc->id); if (!m_plugin) { qDebug("qtractor_clap_scan::Impl[%p]::open_descriptor(%lu)" " *** Could not create plug-in with id: %s.", this, iIndex, desc->id); return false; } if (!m_plugin->init(m_plugin)) { qDebug("qtractor_clap_scan::Impl[%p]::open_descriptor(%lu)" " *** Could not initialize plug-in with id: %s.", this, iIndex, desc->id); m_plugin->destroy(m_plugin); m_plugin = nullptr; return false; } m_sName = desc->name; m_iUniqueID = qHash(desc->id); const clap_plugin_audio_ports *audio_ports = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_AUDIO_PORTS)); if (audio_ports && audio_ports->count && audio_ports->get) { clap_audio_port_info info; const uint32_t nins = audio_ports->count(m_plugin, true); for (uint32_t i = 0; i < nins; ++i) { ::memset(&info, 0, sizeof(info)); if (audio_ports->get(m_plugin, i, true, &info)) { if (info.flags & CLAP_AUDIO_PORT_IS_MAIN) m_iAudioIns += info.channel_count; } } const uint32_t nouts = audio_ports->count(m_plugin, false); for (uint32_t i = 0; i < nouts; ++i) { ::memset(&info, 0, sizeof(info)); if (audio_ports->get(m_plugin, i, false, &info)) { if (info.flags & CLAP_AUDIO_PORT_IS_MAIN) m_iAudioOuts += info.channel_count; } } } const clap_plugin_note_ports *note_ports = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_NOTE_PORTS)); if (note_ports && note_ports->count && note_ports->get) { clap_note_port_info info; const uint32_t nins = note_ports->count(m_plugin, true); for (uint32_t i = 0; i < nins; ++i) { ::memset(&info, 0, sizeof(info)); if (note_ports->get(m_plugin, i, true, &info)) { // if (info.supported_dialects & CLAP_NOTE_DIALECT_MIDI) ++m_iMidiIns; } } const uint32_t nouts = note_ports->count(m_plugin, false); for (uint32_t i = 0; i < nouts; ++i) { ::memset(&info, 0, sizeof(info)); if (note_ports->get(m_plugin, i, false, &info)) { // if (info.supported_dialects & CLAP_NOTE_DIALECT_MIDI) ++m_iMidiOuts; } } } const clap_plugin_params *params = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_PARAMS)); if (params && params->count && params->get_info) { clap_param_info info; const uint32_t nparams = params->count(m_plugin); for (uint32_t i = 0; i < nparams; ++i) { ::memset(&info, 0, sizeof(info)); if (params->get_info(m_plugin, i, &info)) { if (info.flags & CLAP_PARAM_IS_READONLY) ++m_iControlOuts; else ++m_iControlIns; } } } const clap_plugin_gui *gui = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_GUI)); if (gui && gui->is_api_supported && gui->create && gui->destroy) { m_bEditor = ( gui->is_api_supported(m_plugin, CLAP_WINDOW_API_X11, false) || gui->is_api_supported(m_plugin, CLAP_WINDOW_API_X11, true)); } const clap_plugin_state *state = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_STATE)); m_bState = (state && state->save && state->load); return true; } void close_descriptor () { if (m_plugin) { m_plugin->destroy(m_plugin); m_plugin = nullptr; } clear(); } void close () { if (!m_module) return; m_factory = nullptr; if (m_entry) { m_entry->deinit(); m_entry = nullptr; } ::dlclose(m_module); m_module = nullptr; } bool isOpen () const { return (m_module && m_entry && m_factory); } // Properties. const QString& name() const { return m_sName; } unsigned int uniqueID() const { return m_iUniqueID; } int controlIns() const { return m_iControlIns; } int controlOuts() const { return m_iControlOuts; } int audioIns() const { return m_iAudioIns; } int audioOuts() const { return m_iAudioOuts; } int midiIns() const { return m_iMidiIns; } int midiOuts() const { return m_iMidiOuts; } bool hasEditor() const { return m_bEditor; } bool hasState() const { return m_bState; } protected: // Cleaner/wiper. void clear () { m_sName.clear(); m_iUniqueID = 0; m_iControlIns = 0; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; m_iMidiIns = 0; m_iMidiOuts = 0; m_bEditor = false; m_bState = false; } private: // Instance variables. void *m_module; const clap_plugin_entry *m_entry; const clap_plugin_factory *m_factory; const clap_plugin *m_plugin; clap_host m_host; QString m_sName; unsigned long m_iIndex; unsigned int m_iUniqueID; int m_iControlIns; int m_iControlOuts; int m_iAudioIns; int m_iAudioOuts; int m_iMidiIns; int m_iMidiOuts; bool m_bEditor; bool m_bState; }; //---------------------------------------------------------------------- // class qtractor_clap_scan -- CLAP plugin (bare bones) interface // // Constructor. qtractor_clap_scan::qtractor_clap_scan (void) : m_pImpl(new Impl()) { } // destructor. qtractor_clap_scan::~qtractor_clap_scan (void) { close_descriptor(); close(); delete m_pImpl; } // File loader. bool qtractor_clap_scan::open ( const QString& sFilename ) { close(); #ifdef CONFIG_DEBUG_0 qDebug("qtractor_clap_scan[%p]::open(\"%s\")", this, sFilename.toUtf8().constData()); #endif return m_pImpl->open(sFilename); } bool qtractor_clap_scan::open_descriptor ( unsigned long iIndex ) { close_descriptor(); #ifdef CONFIG_DEBUG_0 qDebug("qtractor_clap_scan[%p]::open_descriptor( %lu)", this, iIndex); #endif return m_pImpl->open_descriptor(iIndex); } // File unloader. void qtractor_clap_scan::close_descriptor (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractor_clap_scan[%p]::close_descriptor()", this); #endif m_pImpl->close_descriptor(); } void qtractor_clap_scan::close (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractor_clap_scan[%p]::close()", this); #endif m_pImpl->close(); } // Properties. bool qtractor_clap_scan::isOpen (void) const { return m_pImpl->isOpen(); } const QString& qtractor_clap_scan::name (void) const { return m_pImpl->name(); } unsigned int qtractor_clap_scan::uniqueID (void) const { return m_pImpl->uniqueID(); } int qtractor_clap_scan::controlIns (void) const { return m_pImpl->controlIns(); } int qtractor_clap_scan::controlOuts (void) const { return m_pImpl->controlOuts(); } int qtractor_clap_scan::audioIns (void) const { return m_pImpl->audioIns(); } int qtractor_clap_scan::audioOuts (void) const { return m_pImpl->audioOuts(); } int qtractor_clap_scan::midiIns (void) const { return m_pImpl->midiIns(); } int qtractor_clap_scan::midiOuts (void) const { return m_pImpl->midiOuts(); } bool qtractor_clap_scan::hasEditor (void) const { return m_pImpl->hasEditor(); } bool qtractor_clap_scan::hasState (void) const { return m_pImpl->hasState(); } //------------------------------------------------------------------------- // qtractor_clap_scan_file - The main scan procedure. // static void qtractor_clap_scan_file ( const QString& sFilename ) { #ifdef CONFIG_DEBUG qDebug("qtractor_clap_scan_file(\"%s\")", sFilename.toUtf8().constData()); #endif qtractor_clap_scan plugin; if (!plugin.open(sFilename)) return; QTextStream sout(stdout); unsigned long i = 0; while (plugin.open_descriptor(i)) { sout << "CLAP|"; sout << plugin.name() << '|'; sout << plugin.audioIns() << ':' << plugin.audioOuts() << '|'; sout << plugin.midiIns() << ':' << plugin.midiOuts() << '|'; sout << plugin.controlIns() << ':' << plugin.controlOuts() << '|'; QStringList flags; if (plugin.hasEditor()) flags.append("GUI"); if (plugin.hasState()) flags.append("EXT"); flags.append("RT"); sout << flags.join(",") << '|'; sout << sFilename << '|' << i << '|'; sout << "0x" << QString::number(plugin.uniqueID(), 16) << '\n'; plugin.close_descriptor(); ++i; } plugin.close(); // Must always give an answer, even if it's a wrong one... if (i == 0) sout << "qtractor_clap_scan: " << sFilename << ": plugin file error.\n"; } #endif // CONFIG_CLAP //------------------------------------------------------------------------- // main - The main program trunk. // #include int main ( int argc, char **argv ) { QCoreApplication app(argc, argv); #ifdef CONFIG_DEBUG qDebug("%s: hello. (version %s)", argv[0], PROJECT_VERSION); #endif QTextStream sin(stdin); while (!sin.atEnd()) { const QString& sLine = sin.readLine(); if (!sLine.isEmpty()) { const QStringList& req = sLine.split(':'); const QString& sHint = req.at(0).toUpper(); const QString& sFilename = req.at(1); #ifdef CONFIG_LADSPA if (sHint == "LADSPA") qtractor_ladspa_scan_file(sFilename); else #endif #ifdef CONFIG_DSSI if (sHint == "DSSI") qtractor_dssi_scan_file(sFilename); else #endif #ifdef CONFIG_VST2 if (sHint == "VST2" || sHint == "VST") qtractor_vst2_scan_file(sFilename); else #endif #ifdef CONFIG_VST3 if (sHint == "VST3") qtractor_vst3_scan_file(sFilename); else #endif #ifdef CONFIG_CLAP if (sHint == "CLAP") qtractor_clap_scan_file(sFilename); else #endif break; } } #ifdef CONFIG_DEBUG qDebug("%s: bye.", argv[0]); #endif return 0; } // end of qtractor_plugin_scan.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiBuffer.h0000644000000000000000000000013215124701674017257 xustar0030 mtime=1767080892.788263475 30 atime=1767080892.788263475 30 ctime=1767080892.788263475 qtractor-1.5.11/src/qtractorMidiBuffer.h0000644000175000001440000001073415124701674017254 0ustar00rncbcusers// qtractorMidiBuffer.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorMidiBuffer_h #define __qtractorMidiBuffer_h #include "qtractorList.h" #include //---------------------------------------------------------------------- // class qtractorMidiBuffer -- MIDI event FIFO buffer/cache declaration. // class qtractorMidiBuffer { public: // Minimum buffer size enum { MinBufferSize = 0x400 }; // Constructor. qtractorMidiBuffer(unsigned int iBufferSize = MinBufferSize) : m_pBuffer(nullptr), m_iBufferSize(0), m_iBufferMask(0), m_iWriteIndex(0), m_iReadIndex(0) { // Adjust size to nearest power-of-two, if necessary. m_iBufferSize = MinBufferSize; while (m_iBufferSize < iBufferSize) m_iBufferSize <<= 1; m_iBufferMask = (m_iBufferSize - 1); m_pBuffer = new snd_seq_event_t [m_iBufferSize]; } // Destructor. ~qtractorMidiBuffer() { if (m_pBuffer) delete [] m_pBuffer; } // Implementation properties. unsigned int bufferSize() const { return m_iBufferSize; } // Clears the buffer. void clear() { m_iReadIndex = m_iWriteIndex = 0; } // Returns nonzero if there aren't any events available. bool isEmpty() const { return (m_iReadIndex == m_iWriteIndex); } // Returns a pointer to the first of the output events. snd_seq_event_t *peek() const { return (isEmpty() ? nullptr : &m_pBuffer[m_iReadIndex]); } // Read next event from buffer. snd_seq_event_t *next() { if (!isEmpty()) ++m_iReadIndex &= m_iBufferMask; return peek(); } // Read event from buffer. snd_seq_event_t *pop() { const unsigned int iReadIndex = m_iReadIndex; if (iReadIndex == m_iWriteIndex) return nullptr; m_iReadIndex = (iReadIndex + 1) & m_iBufferMask; return &m_pBuffer[iReadIndex]; } // Write event to buffer. bool push(snd_seq_event_t *pEvent, unsigned long iTick = 0) { const unsigned int iWriteIndex = (m_iWriteIndex + 1) & m_iBufferMask; if (iWriteIndex == m_iReadIndex) return false; m_pBuffer[m_iWriteIndex] = *pEvent; m_pBuffer[m_iWriteIndex].time.tick = iTick; m_iWriteIndex = iWriteIndex; return true; } // Write event to buffer (ordered). bool insert(snd_seq_event_t *pEvent, unsigned long iTick = 0) { const unsigned int iWriteIndex = (m_iWriteIndex + 1) & m_iBufferMask; if (iWriteIndex == m_iReadIndex) return false; unsigned int i = m_iWriteIndex; unsigned int j = i; for (;;) { --i &= m_iBufferMask; if (j == m_iReadIndex || iTick >= m_pBuffer[i].time.tick) { m_pBuffer[j] = *pEvent; m_pBuffer[j].time.tick = iTick; break; } m_pBuffer[j] = m_pBuffer[i]; j = i; } m_iWriteIndex = iWriteIndex; return true; } // Returns number of events currently available. unsigned int count() const { const unsigned int iWriteIndex = m_iWriteIndex; const unsigned int iReadIndex = m_iReadIndex; if (iWriteIndex > iReadIndex) { return (iWriteIndex - iReadIndex); } else { return (iWriteIndex - iReadIndex + m_iBufferSize) & m_iBufferMask; } } // Get event from buffer by index. snd_seq_event_t *at(unsigned int iIndex) const { const unsigned int iReadIndex = (m_iReadIndex + iIndex) & m_iBufferMask; return &m_pBuffer[iReadIndex]; } // Reset events in buffer. void reset(unsigned long iTick = 0) { unsigned int i = m_iReadIndex; while (i != m_iWriteIndex) { m_pBuffer[i].time.tick = iTick; ++i &= m_iBufferMask; } } private: // Instance variables. snd_seq_event_t *m_pBuffer; unsigned int m_iBufferSize; unsigned int m_iBufferMask; unsigned int m_iWriteIndex; unsigned int m_iReadIndex; }; #endif // __qtractorMidiBuffer_h // end of qtractorMidiBuffer.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiEventList.cpp0000644000000000000000000000013215124701674020316 xustar0030 mtime=1767080892.793263454 30 atime=1767080892.793263454 30 ctime=1767080892.793263454 qtractor-1.5.11/src/qtractorMidiEventList.cpp0000644000175000001440000007270015124701674020314 0ustar00rncbcusers// qtractorMidiEventList.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorMidiEventList.h" #include "qtractorMidiEditor.h" #include "qtractorMidiEditorForm.h" #include "qtractorMidiEditCommand.h" #include "qtractorMidiEditView.h" #include "qtractorMidiSequence.h" #include "qtractorSpinBox.h" #include #include #include #include #include #include //---------------------------------------------------------------------------- // qtractorMidiEventListView::ItemModel -- List model. class qtractorMidiEventListView::ItemModel : public QAbstractItemModel { public: // Constructor. ItemModel(qtractorMidiEditor *pEditor, QObject *pParent = nullptr); // Concretizers (virtual). int rowCount(const QModelIndex& parent = QModelIndex()) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orient, int role) const; QVariant data(const QModelIndex& index, int role) const; Qt::ItemFlags flags(const QModelIndex& index ) const; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; QModelIndex parent(const QModelIndex&) const; void reset(); // Specifics. qtractorMidiEvent *eventOfIndex(const QModelIndex& index) const; QModelIndex indexOfEvent(qtractorMidiEvent *pEvent) const; QModelIndex indexFromTick(unsigned long iTick) const; unsigned long tickFromIndex(const QModelIndex& index) const; QModelIndex indexFromFrame(unsigned long iFrame) const; unsigned long frameFromIndex(const QModelIndex& index) const; qtractorMidiEditor *editor() const; protected: qtractorMidiEvent *eventAt(int i) const; QString itemDisplay(const QModelIndex& index) const; QString itemToolTip(const QModelIndex& index) const; int columnAlignment(int column) const; private: // Model variables. QStringList m_headers; qtractorMidiEditor *m_pEditor; qtractorMidiSequence *m_pSeq; unsigned long m_iTimeOffset; mutable qtractorMidiEvent *m_pEvent; mutable int m_iEvent; }; //---------------------------------------------------------------------------- // qtractorMidiEventListView::ItemDelegate -- Custom (tree) list item delegate. class qtractorMidiEventListView::ItemDelegate : public QItemDelegate { public: // Constructor. ItemDelegate(QObject *pParent = nullptr); // QItemDelegate Interface... void paint(QPainter *pPainter, const QStyleOptionViewItem& option, const QModelIndex& index) const; QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index) const; 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; }; //---------------------------------------------------------------------------- // qtractorMidiEventListView::ItemModel -- List model. // Constructor. qtractorMidiEventListView::ItemModel::ItemModel ( qtractorMidiEditor *pEditor, QObject *pParent ) : QAbstractItemModel(pParent), m_pEditor(pEditor), m_pSeq(nullptr), m_iTimeOffset(0), m_pEvent(nullptr), m_iEvent(0) { m_headers << tr("Time") << tr("Type") << tr("Name") << tr("Value") << tr("Duration/Data"); reset(); } int qtractorMidiEventListView::ItemModel::rowCount ( const QModelIndex& /*parent*/ ) const { return (m_pSeq ? m_pSeq->events().count() : 0); } int qtractorMidiEventListView::ItemModel::columnCount ( const QModelIndex& /*parent*/ ) const { return m_headers.count(); } QVariant qtractorMidiEventListView::ItemModel::headerData ( int section, Qt::Orientation orient, int role ) const { // qDebug("headerData(%d, %d, %d)", section, int(orient), int(role)); if (orient == Qt::Horizontal) { switch (role) { case Qt::DisplayRole: #if 0 if (section == 0) { switch ((m_pEditor->timeScale())->displayFormat()) { case qtractorTimeScale::Frames: return tr("Frame"); case qtractorTimeScale::Time: return tr("Time"); case qtractorTimeScale::BBT: return tr("BBT"); } } #endif return m_headers.at(section); case Qt::TextAlignmentRole: return columnAlignment(section); default: break; } } return QVariant(); } QVariant qtractorMidiEventListView::ItemModel::data ( const QModelIndex& index, int role ) const { // qDebug("data(%d, %d, %d)", index.row(), index.column(), int(role)); switch (role) { case Qt::DisplayRole: return itemDisplay(index); case Qt::TextAlignmentRole: return columnAlignment(index.column()); case Qt::ToolTipRole: return itemToolTip(index); default: return QVariant(); } } Qt::ItemFlags qtractorMidiEventListView::ItemModel::flags ( const QModelIndex& index ) const { const Qt::ItemFlags flags = QAbstractItemModel::flags(index); return flags | Qt::ItemIsEnabled | Qt::ItemIsEditable; } QModelIndex qtractorMidiEventListView::ItemModel::index ( int row, int column, const QModelIndex& /*parent*/) const { // qDebug("index(%d, %d)", row, column); qtractorMidiEvent *pEvent = eventAt(row); if (pEvent) return createIndex(row, column, pEvent); else return QModelIndex(); } QModelIndex qtractorMidiEventListView::ItemModel::parent ( const QModelIndex& ) const { return QModelIndex(); } void qtractorMidiEventListView::ItemModel::reset (void) { // qDebug("reset()"); m_pSeq = m_pEditor->sequence(); m_iTimeOffset = m_pEditor->timeOffset(); m_pEvent = nullptr; m_iEvent = 0; #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) QAbstractItemModel::beginResetModel(); QAbstractItemModel::endResetModel(); #else QAbstractItemModel::reset(); #endif } qtractorMidiEvent *qtractorMidiEventListView::ItemModel::eventAt ( int i ) const { if (m_pSeq == nullptr) return nullptr; const int n = m_pSeq->events().count(); const int m = (n >> 1); if (m_pEvent == nullptr || i > m_iEvent + m || i < m_iEvent - m) { if (i > m) { m_iEvent = n - 1; m_pEvent = m_pSeq->events().last(); } else { m_iEvent = 0; m_pEvent = m_pSeq->events().first(); } } if (i > m_iEvent) { while (m_pEvent && m_pEvent->next() && i > m_iEvent) { m_pEvent = m_pEvent->next(); ++m_iEvent; } } else if (i < m_iEvent) { while (m_pEvent && m_pEvent->prev() && i < m_iEvent) { m_pEvent = m_pEvent->prev(); --m_iEvent; } } return m_pEvent; } qtractorMidiEvent *qtractorMidiEventListView::ItemModel::eventOfIndex ( const QModelIndex& index ) const { return static_cast (index.internalPointer()); } QModelIndex qtractorMidiEventListView::ItemModel::indexOfEvent ( qtractorMidiEvent *pEvent ) const { if (pEvent == nullptr) return QModelIndex(); const unsigned long iTime = pEvent->time(); if (indexFromTick(m_iTimeOffset + iTime).isValid()) { while (m_pEvent != pEvent && m_pEvent->next() && m_pEvent->time() == iTime) { m_pEvent = m_pEvent->next(); ++m_iEvent; } } return (m_pEvent == pEvent ? createIndex(m_iEvent, 0, m_pEvent) : QModelIndex()); } QModelIndex qtractorMidiEventListView::ItemModel::indexFromTick ( unsigned long iTick ) const { if (m_pEvent == nullptr && m_pSeq) { m_iEvent = 0; m_pEvent = m_pSeq->events().first(); } if (m_pEvent == nullptr) return QModelIndex(); const unsigned long iTime = (iTick > m_iTimeOffset ? iTick - m_iTimeOffset : 0); if (m_pEvent->time() >= iTime) { while (m_pEvent && m_pEvent->prev() && (m_pEvent->prev())->time() >= iTime) { m_pEvent = m_pEvent->prev(); --m_iEvent; } } else while (m_pEvent && m_pEvent->next() && iTime > m_pEvent->time()) { m_pEvent = m_pEvent->next(); ++m_iEvent; } return createIndex(m_iEvent, 0, m_pEvent); } unsigned long qtractorMidiEventListView::ItemModel::tickFromIndex ( const QModelIndex& index ) const { qtractorMidiEvent *pEvent = eventOfIndex(index); return m_iTimeOffset + (pEvent ? pEvent->time() : 0); } QModelIndex qtractorMidiEventListView::ItemModel::indexFromFrame ( unsigned long iFrame ) const { return indexFromTick((m_pEditor->timeScale())->tickFromFrame(iFrame)); } unsigned long qtractorMidiEventListView::ItemModel::frameFromIndex ( const QModelIndex& index ) const { return (m_pEditor->timeScale())->frameFromTick(tickFromIndex(index)); } qtractorMidiEditor *qtractorMidiEventListView::ItemModel::editor (void) const { return m_pEditor; } QString qtractorMidiEventListView::ItemModel::itemDisplay ( const QModelIndex& index ) const { // qDebug("itemDisplay(%d, %d)", index.row(), index.column()); const QString sDashes(2, '-'); qtractorMidiEvent *pEvent = eventOfIndex(index); if (pEvent) { switch (index.column()) { case 0: // Time. return (m_pEditor->timeScale())->textFromTick( m_iTimeOffset + pEvent->time()); case 1: // Type. switch (pEvent->type()) { case qtractorMidiEvent::NOTEON: return tr("Note On (%1)").arg(pEvent->note()); case qtractorMidiEvent::NOTEOFF: return tr("Note Off (%1)").arg(pEvent->note()); case qtractorMidiEvent::KEYPRESS: return tr("Key Press (%1)").arg(pEvent->note()); case qtractorMidiEvent::CONTROLLER: return tr("Controller (%1)").arg(pEvent->controller()); case qtractorMidiEvent::CONTROL14: return tr("Control 14 (%1)").arg(pEvent->controller()); case qtractorMidiEvent::REGPARAM: return tr("RPN (%1)").arg(pEvent->param()); case qtractorMidiEvent::NONREGPARAM: return tr("NRPN (%1)").arg(pEvent->param()); case qtractorMidiEvent::PGMCHANGE: return tr("Pgm Change"); case qtractorMidiEvent::CHANPRESS: return tr("Chan Press"); case qtractorMidiEvent::PITCHBEND: return tr("Pitch Bend"); case qtractorMidiEvent::SYSEX: return tr("SysEx"); case qtractorMidiEvent::META: return tr("Meta (%1)").arg(int(pEvent->type())); default: break; } return tr("Unknown (%1)").arg(int(pEvent->type())); case 2: // Name. switch (pEvent->type()) { case qtractorMidiEvent::NOTEON: case qtractorMidiEvent::NOTEOFF: case qtractorMidiEvent::KEYPRESS: return m_pEditor->noteName(pEvent->note()); case qtractorMidiEvent::PGMCHANGE: return m_pEditor->programName(pEvent->param()); case qtractorMidiEvent::CONTROLLER: return m_pEditor->controllerName(pEvent->controller()); case qtractorMidiEvent::REGPARAM: return m_pEditor->rpnNames().value(pEvent->param()); case qtractorMidiEvent::NONREGPARAM: return m_pEditor->nrpnNames().value(pEvent->param()); case qtractorMidiEvent::CONTROL14: return m_pEditor->control14Name(pEvent->controller()); default: break; } break; case 3: // Value. switch (pEvent->type()) { case qtractorMidiEvent::NOTEON: case qtractorMidiEvent::NOTEOFF: case qtractorMidiEvent::KEYPRESS: return QString::number(pEvent->velocity()); case qtractorMidiEvent::PGMCHANGE: return QString::number(pEvent->param()); case qtractorMidiEvent::CONTROLLER: case qtractorMidiEvent::REGPARAM: case qtractorMidiEvent::NONREGPARAM: case qtractorMidiEvent::CONTROL14: case qtractorMidiEvent::CHANPRESS: return QString::number(pEvent->value()); case qtractorMidiEvent::PITCHBEND: return QString::number(pEvent->pitchBend()); case qtractorMidiEvent::SYSEX: return QString::number(pEvent->sysex_len()); default: break; } break; case 4: // Duration/Data switch (pEvent->type()) { case qtractorMidiEvent::NOTEON: return (m_pEditor->timeScale())->textFromTick( m_iTimeOffset + pEvent->time(), true, pEvent->duration()); case qtractorMidiEvent::SYSEX: { QString sText; unsigned char *data = pEvent->sysex(); unsigned short len = pEvent->sysex_len(); sText += '{'; sText += ' '; for (unsigned short i = 0; i < len; ++i) #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) sText += QString().sprintf("%02x ", data[i]); #else sText += QString::asprintf("%02x ", data[i]); #endif sText += '}'; return sText; } default: break; } break; } } return sDashes; } QString qtractorMidiEventListView::ItemModel::itemToolTip ( const QModelIndex& index ) const { qtractorMidiEvent *pEvent = eventOfIndex(index); if (pEvent) return m_pEditor->eventToolTip(pEvent); else return QString(); } int qtractorMidiEventListView::ItemModel::columnAlignment( int column ) const { switch (column) { case 0: // Time. return int(Qt::AlignHCenter | Qt::AlignVCenter); case 3: // Value. return int(Qt::AlignRight | Qt::AlignVCenter); default: return int(Qt::AlignLeft | Qt::AlignVCenter); } } //---------------------------------------------------------------------------- // qtractorMidiEventListView::ItemDelegate -- Custom (tree) list item delegate. // Constructor. qtractorMidiEventListView::ItemDelegate::ItemDelegate ( QObject *pParent ) : QItemDelegate(pParent) { } // QItemDelegate Interface... void qtractorMidiEventListView::ItemDelegate::paint ( QPainter *pPainter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QItemDelegate::paint(pPainter, option, index); } QSize qtractorMidiEventListView::ItemDelegate::sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const { return QItemDelegate::sizeHint(option, index) + QSize(4, 4); } QWidget *qtractorMidiEventListView::ItemDelegate::createEditor ( QWidget *pParent, const QStyleOptionViewItem& /*option*/, const QModelIndex& index ) const { const ItemModel *pItemModel = static_cast (index.model()); qtractorMidiEvent *pEvent = pItemModel->eventOfIndex(index); if (pEvent == nullptr) return nullptr; qtractorMidiEditor *pMidiEditor = pItemModel->editor(); if (pMidiEditor == nullptr) return nullptr; QWidget *pEditor = nullptr; switch (index.column()) { case 0: // Time. { qtractorTimeSpinBox *pTimeSpinBox = new qtractorTimeSpinBox(pParent); pTimeSpinBox->setTimeScale(pMidiEditor->timeScale()); pEditor = pTimeSpinBox; break; } case 2: // Name. { if (pEvent->type() == qtractorMidiEvent::NOTEON || pEvent->type() == qtractorMidiEvent::KEYPRESS) { QComboBox *pComboBox = new QComboBox(pParent); for (unsigned char note = 0; note < 128; ++note) pComboBox->addItem(pMidiEditor->noteName(note)); pEditor = pComboBox; } break; } case 3: // Value. { QSpinBox *pSpinBox = new QSpinBox(pParent); const qtractorMidiEvent::EventType etype = pEvent->type(); if (etype == qtractorMidiEvent::PITCHBEND) { pSpinBox->setMinimum(-8192); pSpinBox->setMaximum(+8192); } else if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) { pSpinBox->setMinimum(0); pSpinBox->setMaximum(16383); } else { pSpinBox->setMinimum(0); pSpinBox->setMaximum(127); } pEditor = pSpinBox; break; } case 4: // Duration/Data. { if (pEvent->type() == qtractorMidiEvent::NOTEON) { qtractorTimeSpinBox *pTimeSpinBox = new qtractorTimeSpinBox(pParent); pTimeSpinBox->setTimeScale(pMidiEditor->timeScale()); pTimeSpinBox->setDeltaValue(true); pEditor = pTimeSpinBox; } break; } default: break; } #ifdef CONFIG_DEBUG qDebug("qtractorMidiEventListView::ItemDelegate::createEditor(%p, %d, %d) = %p", pParent, index.row(), index.column(), pEditor); #endif return pEditor; } void qtractorMidiEventListView::ItemDelegate::setEditorData ( QWidget *pEditor, const QModelIndex& index ) const { const ItemModel *pItemModel = static_cast (index.model()); qtractorMidiEvent *pEvent = pItemModel->eventOfIndex(index); if (pEvent == nullptr) return; qtractorMidiEditor *pMidiEditor = pItemModel->editor(); if (pMidiEditor == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMidiEventListView::ItemDelegate::setEditorData(%p, %p, %d, %d)", pEditor, pItemModel, index.row(), index.column()); #endif qtractorTimeScale *pTimeScale = pMidiEditor->timeScale(); switch (index.column()) { case 0: // Time. { qtractorTimeSpinBox *pTimeSpinBox = qobject_cast (pEditor); if (pTimeSpinBox) { pTimeSpinBox->setValue(pTimeScale->frameFromTick( pMidiEditor->timeOffset() + pEvent->time())); } break; } case 2: // Name. { QComboBox *pComboBox = qobject_cast (pEditor); if (pComboBox) pComboBox->setCurrentIndex(int(pEvent->note())); break; } case 3: // Value. { QSpinBox *pSpinBox = qobject_cast (pEditor); if (pSpinBox) { if (pEvent->type() == qtractorMidiEvent::PITCHBEND) pSpinBox->setValue(pEvent->pitchBend()); else if (pEvent->type() == qtractorMidiEvent::PGMCHANGE) pSpinBox->setValue(pEvent->param()); else pSpinBox->setValue(pEvent->value()); } break; } case 4: // Duration/Data. { qtractorTimeSpinBox *pTimeSpinBox = qobject_cast (pEditor); if (pTimeSpinBox) { pTimeSpinBox->setValue( pTimeScale->frameFromTick(pEvent->duration())); } break; } default: break; } } void qtractorMidiEventListView::ItemDelegate::setModelData ( QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex& index ) const { const ItemModel *pItemModel = static_cast (pModel); qtractorMidiEvent *pEvent = pItemModel->eventOfIndex(index); if (pEvent == nullptr) return; qtractorMidiEditor *pMidiEditor = pItemModel->editor(); if (pMidiEditor == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMidiEventListView::ItemDelegate::setModelData(%p, %p, %d, %d)", pEditor, pItemModel, index.row(), index.column()); #endif qtractorTimeScale *pTimeScale = pMidiEditor->timeScale(); qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(pMidiEditor->midiClip(), tr("edit %1").arg(pItemModel->headerData( index.column(), Qt::Horizontal, Qt::DisplayRole) .toString().toLower())); switch (index.column()) { case 0: // Time. { qtractorTimeSpinBox *pTimeSpinBox = qobject_cast (pEditor); if (pTimeSpinBox) { unsigned long iTime = pTimeScale->tickFromFrame(pTimeSpinBox->valueFromText()); if (iTime > pMidiEditor->timeOffset()) iTime -= pMidiEditor->timeOffset(); else iTime = 0; unsigned long iDuration = 0; if (pEvent->type() == qtractorMidiEvent::NOTEON) iDuration = pEvent->duration(); pEditCommand->resizeEventTime(pEvent, iTime, iDuration); } break; } case 2: // Name. { QComboBox *pComboBox = qobject_cast (pEditor); if (pComboBox) { const int iNote = pComboBox->currentIndex(); const unsigned long iTime = pEvent->time(); pEditCommand->moveEventNote(pEvent, iNote, iTime); } break; } case 3: // Value. { QSpinBox *pSpinBox = qobject_cast (pEditor); if (pSpinBox) { const int iValue = pSpinBox->value(); pEditCommand->resizeEventValue(pEvent, iValue); } break; } case 4: // Duration/Data. { qtractorTimeSpinBox *pTimeSpinBox = qobject_cast (pEditor); if (pTimeSpinBox) { const unsigned long iTime = pEvent->time(); const unsigned long iDuration = pTimeScale->tickFromFrame(pTimeSpinBox->value()); pEditCommand->resizeEventTime(pEvent, iTime, iDuration); } break; } default: break; } // Do it. pMidiEditor->execute(pEditCommand); } //---------------------------------------------------------------------------- // qtractorMidiEventListView -- Custom (tree) list view. // Constructor. qtractorMidiEventListView::qtractorMidiEventListView ( QWidget *pParent ) : QTreeView(pParent), m_pItemModel(nullptr), m_pItemDelegate(nullptr) { } // Destructor. qtractorMidiEventListView::~qtractorMidiEventListView (void) { if (m_pItemDelegate) delete m_pItemDelegate; if (m_pItemModel) delete m_pItemModel; } // Settlers. void qtractorMidiEventListView::setEditor ( qtractorMidiEditor *pEditor ) { if (m_pItemDelegate) delete m_pItemDelegate; if (m_pItemModel) delete m_pItemModel; m_pItemModel = new ItemModel(pEditor); m_pItemDelegate = new ItemDelegate(); QTreeView::setModel(m_pItemModel); QTreeView::setItemDelegate(m_pItemDelegate); QTreeView::setSelectionMode(QAbstractItemView::ExtendedSelection); QTreeView::setRootIsDecorated(false); QTreeView::setUniformRowHeights(true); QTreeView::setItemsExpandable(false); QTreeView::setAllColumnsShowFocus(true); QTreeView::setAlternatingRowColors(true); QHeaderView *pHeader = QTreeView::header(); // pHeader->setDefaultAlignment(Qt::AlignLeft); pHeader->setDefaultSectionSize(80); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) pHeader->setSectionsMovable(false); #else pHeader->setMovable(false); #endif pHeader->resizeSection(2, 60); // Name pHeader->resizeSection(3, 60); // Value pHeader->setStretchLastSection(true); } qtractorMidiEditor *qtractorMidiEventListView::editor (void) const { return (m_pItemModel ? m_pItemModel->editor() : nullptr); } // Refreshner. void qtractorMidiEventListView::refresh (void) { if (m_pItemModel == nullptr) return; QItemSelectionModel *pSelectionModel = QTreeView::selectionModel(); const QModelIndex& index = pSelectionModel->currentIndex(); m_pItemModel->reset(); pSelectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); } // Locators. qtractorMidiEvent *qtractorMidiEventListView::eventOfIndex ( const QModelIndex& index) const { return (m_pItemModel ? m_pItemModel->eventOfIndex(index) : nullptr); } QModelIndex qtractorMidiEventListView::indexOfEvent ( qtractorMidiEvent *pEvent ) const { return (m_pItemModel ? m_pItemModel->indexOfEvent(pEvent) : QModelIndex()); } QModelIndex qtractorMidiEventListView::indexFromTick ( unsigned long iTick ) const { return (m_pItemModel ? m_pItemModel->indexFromTick(iTick) : QModelIndex()); } unsigned long qtractorMidiEventListView::tickFromIndex ( const QModelIndex& index ) const { return (m_pItemModel ? m_pItemModel->tickFromIndex(index) : 0); } QModelIndex qtractorMidiEventListView::indexFromFrame ( unsigned long iFrame ) const { return (m_pItemModel ? m_pItemModel->indexFromFrame(iFrame) : QModelIndex()); } unsigned long qtractorMidiEventListView::frameFromIndex ( const QModelIndex& index ) const { return (m_pItemModel ? m_pItemModel->frameFromIndex(index) : 0); } void qtractorMidiEventListView::selectEvent ( qtractorMidiEvent *pEvent, bool bSelect ) { if (m_pItemModel == nullptr) return; const QModelIndex& index = m_pItemModel->indexOfEvent(pEvent); if (index.isValid()) { QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Rows; if (bSelect) flags |= QItemSelectionModel::Select; else flags |= QItemSelectionModel::Deselect; QTreeView::selectionModel()->select(index, flags); } } // Custom editor closing stub. void qtractorMidiEventListView::closeEditor ( QWidget */*pEditor*/, QAbstractItemDelegate::EndEditHint /*hint*/ ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiEventListView[%p]::closeEditor(%p, 0x%04x)", this, pEditor, uint(hint)); #endif // FIXME: Avoid spitting out QAbstractItemView::closeEditor // called with an editor that does not belong to this view... // QTreeView::closeEditor(pEditor, hint); } //---------------------------------------------------------------------------- // qtractorMidiEventList -- MIDI Event List dockable window. // Constructor. qtractorMidiEventList::qtractorMidiEventList ( QWidget *pParent ) : QDockWidget(pParent) { // Surely a name is crucial (e.g.for storing geometry settings) QDockWidget::setObjectName("qtractorMidiEventList"); // Create local list view. m_pListView = new qtractorMidiEventListView(); // Cross-selection fake-mutex. m_iSelectUpdate = 0; const QFont& font = QDockWidget::font(); m_pListView->setFont(QFont(font.family(), font.pointSize() - 1)); // Prepare the dockable window stuff. QDockWidget::setWidget(m_pListView); // QDockWidget::setFeatures(QDockWidget::AllDockWidgetFeatures); QDockWidget::setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); // Some specialties to this kind of dock window... QDockWidget::setMinimumWidth(280); // Finally set the default caption and tooltip. const QString& sCaption = tr("Events"); QDockWidget::setWindowTitle(sCaption); QDockWidget::setWindowIcon(QIcon::fromTheme("viewEvents")); QDockWidget::setToolTip(sCaption); } // Destructor. qtractorMidiEventList::~qtractorMidiEventList (void) { // No need to delete child widgets, Qt does it all for us. } // Full update when show up. void qtractorMidiEventList::showEvent ( QShowEvent *pShowEvent ) { QDockWidget::showEvent(pShowEvent); refresh(); } // Just about to notify main-window that we're closing. void qtractorMidiEventList::closeEvent ( QCloseEvent * /*pCloseEvent*/ ) { QDockWidget::hide(); qtractorMidiEditorForm *pMidiEditorForm = static_cast (QDockWidget::parentWidget()); if (pMidiEditorForm) pMidiEditorForm->stabilizeForm(); } // Settlers. void qtractorMidiEventList::setEditor ( qtractorMidiEditor *pEditor ) { m_pListView->setEditor(pEditor); // Set internal list view change connections. QObject::connect(m_pListView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex&,const QModelIndex&)), SLOT(currentRowChangedSlot(const QModelIndex&,const QModelIndex&))); QObject::connect(m_pListView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), SLOT(selectionChangedSlot(const QItemSelection&,const QItemSelection&))); // Set MIDI editor change connections. QObject::connect(pEditor, SIGNAL(selectNotifySignal(qtractorMidiEditor *)), SLOT(selectNotifySlot(qtractorMidiEditor *))); QObject::connect(pEditor, SIGNAL(changeNotifySignal(qtractorMidiEditor *)), SLOT(changeNotifySlot(qtractorMidiEditor *))); } qtractorMidiEditor *qtractorMidiEventList::editor (void) const { return m_pListView->editor(); } // Event list view refreshner. void qtractorMidiEventList::refresh (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiEventList[%p]::refresh()", this); #endif m_pListView->refresh(); selectNotifySlot(m_pListView->editor()); } // Context menu request event handler. void qtractorMidiEventList::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { qtractorMidiEditorForm *pMidiEditorForm = static_cast (QDockWidget::parentWidget()); if (pMidiEditorForm) { pMidiEditorForm->stabilizeForm(); pMidiEditorForm->editMenu()->exec(pContextMenuEvent->globalPos()); } } // Current list view row changed slot. void qtractorMidiEventList::currentRowChangedSlot ( const QModelIndex& index, const QModelIndex& /*previous*/ ) { qtractorMidiEditor *pEditor = m_pListView->editor(); if (pEditor == nullptr) return; if (m_iSelectUpdate > 0) return; ++m_iSelectUpdate; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiEventList[%p]::currentRowChangedSlot()", this); #endif // pEditor->setEditHead(m_pListView->frameFromIndex(index)); pEditor->ensureVisibleFrame(pEditor->editView(), m_pListView->frameFromIndex(index)); pEditor->selectionChangeNotify(); --m_iSelectUpdate; } // Current list view selection changed slot. void qtractorMidiEventList::selectionChangedSlot ( const QItemSelection& selected, const QItemSelection& deselected ) { qtractorMidiEditor *pEditor = m_pListView->editor(); if (pEditor == nullptr) return; if (m_iSelectUpdate > 0) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiEventList[%p]::selectionChangedSlot()", this); #endif ++m_iSelectUpdate; m_pListView->setUpdatesEnabled(false); QListIterator iter1(selected.indexes()); while (iter1.hasNext()) { const QModelIndex& index = iter1.next(); if (index.column() == 0) pEditor->selectEvent(m_pListView->eventOfIndex(index), true); } QListIterator iter2(deselected.indexes()); while (iter2.hasNext()) { const QModelIndex& index = iter2.next(); if (index.column() == 0) pEditor->selectEvent(m_pListView->eventOfIndex(index), false); } m_pListView->setUpdatesEnabled(true); pEditor->selectionChangeNotify(); --m_iSelectUpdate; } // MIDI editor selection changed slot. void qtractorMidiEventList::selectNotifySlot ( qtractorMidiEditor *pEditor ) { if (!isVisible()) return; if (m_iSelectUpdate > 0) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiEventList[%p]::selectNotifySlot()", this); #endif ++m_iSelectUpdate; m_pListView->clearSelection(); const QList& list = pEditor->selectedEvents(); if (list.count() > 0) { m_pListView->setCurrentIndex( m_pListView->indexOfEvent(list.first())); QListIterator iter(list); while (iter.hasNext()) m_pListView->selectEvent(iter.next(), true); } --m_iSelectUpdate; } // MIDI editor selection changed slot. void qtractorMidiEventList::changeNotifySlot ( qtractorMidiEditor * ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiEventList[%p]::changeNotifySlot()", this); #endif refresh(); } // end of qtractorMidiEventList.cpp qtractor-1.5.11/src/PaxHeaders/qtractorTimeScaleForm.cpp0000644000000000000000000000012715124701674020274 xustar0029 mtime=1767080892.80126342 29 atime=1767080892.80126342 29 ctime=1767080892.80126342 qtractor-1.5.11/src/qtractorTimeScaleForm.cpp0000644000175000001440000007545215124701674020275 0ustar00rncbcusers// qtractorTimeScaleForm.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorTimeScaleForm.h" #include "qtractorTimeScaleCommand.h" #include "qtractorAbout.h" #include "qtractorOptions.h" #include "qtractorSession.h" #include "qtractorMainForm.h" #include "qtractorTracks.h" #include "qtractorTrackView.h" #include #include #include #include #include #include #include //---------------------------------------------------------------------- // class qtractorTimeScaleItemDelegate -- Custom time-scale item delegate. // class qtractorTimeScaleItemDelegate : public QItemDelegate { public: // Constructor. qtractorTimeScaleItemDelegate(QObject *pParent = nullptr) : QItemDelegate(pParent) {} protected: // Modified render method. void paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { QStyleOptionViewItem opt(option); switch (index.column()) { case 0: // Bar case 2: // Tempo case 3: // Key opt.displayAlignment = Qt::AlignCenter; break; default: opt.displayAlignment = Qt::AlignLeft | Qt::AlignVCenter; break; } QItemDelegate::paint(painter, opt, index); } QSize sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const { return QItemDelegate::sizeHint(option, index) + QSize(4, 4); } }; //---------------------------------------------------------------------- // class qtractorTimeScaleListItem -- Custom time-scale tempo node item. // class qtractorTimeScaleListItem : public QTreeWidgetItem { public: // Constructors. qtractorTimeScaleListItem(QTreeWidget *pTreeWidget, qtractorTimeScaleListItem *pListItem, qtractorTimeScale *pTimeScale, qtractorTimeScale::Node *pNode, qtractorTimeScale::Marker *pMarker, qtractorTimeScale::DisplayFormat displayFormat) : QTreeWidgetItem(pTreeWidget, pListItem), m_pNode(pNode), m_pMarker(pMarker) { const QChar dash = '-'; if (pNode) { QTreeWidgetItem::setText(0, QString::number(m_pNode->bar + 1)); QTreeWidgetItem::setText(1, pTimeScale->textFromFrameEx( displayFormat, m_pNode->frame)); QTreeWidgetItem::setText(2, QString("%1 %2/%3") .arg(m_pNode->tempo) .arg(m_pNode->beatsPerBar) .arg(1 << m_pNode->beatDivisor)); } else QTreeWidgetItem::setText(2, dash); if (pMarker) { if (pNode == nullptr) { const unsigned int iBar = pTimeScale->barFromFrame(pMarker->frame); QTreeWidgetItem::setText(0, QString::number(iBar + 1)); QTreeWidgetItem::setText(1, pTimeScale->textFromFrameEx( displayFormat, pMarker->frame)); } QTreeWidgetItem::setText(3, qtractorTimeScale::keySignatureName( pMarker->accidentals, pMarker->mode)); if (!pMarker->text.isEmpty()) { QTreeWidgetItem::setText(4, pMarker->text); QTreeWidgetItem::setForeground(4, pMarker->color); } else QTreeWidgetItem::setText(4, dash); } else { QTreeWidgetItem::setText(3, dash); QTreeWidgetItem::setText(4, dash); } } // Node accessor. qtractorTimeScale::Node *node() const { return m_pNode; } // Marker accessor. qtractorTimeScale::Marker *marker() const { return m_pMarker; } private: // Instance variables. qtractorTimeScale::Node *m_pNode; qtractorTimeScale::Marker *m_pMarker; }; //---------------------------------------------------------------------------- // qtractorTimeScaleForm -- UI wrapper form. // Constructor. qtractorTimeScaleForm::qtractorTimeScaleForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); // Initialize locals. m_pTimeScale = nullptr; m_pTempoTap = new QElapsedTimer(); m_iTempoTap = 0; m_fTempoTap = 0.0f; m_iDirtySetup = 0; m_iDirtyCount = 0; m_iDirtyTotal = 0; QHeaderView *pHeader = m_ui.TimeScaleListView->header(); pHeader->setDefaultAlignment(Qt::AlignLeft); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // pHeader->setSectionResizeMode(QHeaderView::Custom); pHeader->setSectionResizeMode(QHeaderView::ResizeToContents); pHeader->setSectionsMovable(false); #else // pHeader->setResizeMode(QHeaderView::Custom); pHeader->setResizeMode(QHeaderView::ResizeToContents); pHeader->setMovable(false); #endif QAbstractItemModel *pHeaderModel = pHeader->model(); pHeaderModel->setHeaderData(0, Qt::Horizontal, Qt::AlignCenter, Qt::TextAlignmentRole); // Bar pHeaderModel->setHeaderData(2, Qt::Horizontal, Qt::AlignCenter, Qt::TextAlignmentRole); // Tempo pHeaderModel->setHeaderData(3, Qt::Horizontal, Qt::AlignCenter, Qt::TextAlignmentRole); // Key m_ui.TimeScaleListView->setItemDelegate( new qtractorTimeScaleItemDelegate(m_ui.TimeScaleListView)); m_ui.TimeScaleListView->setContextMenuPolicy(Qt::CustomContextMenu); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) // Some conveniency cleaner helper... m_ui.MarkerTextLineEdit->setClearButtonEnabled(true); #endif // (Re)initial contents. // Default is main session time-scale of course... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) setTimeScale(pSession->timeScale()); // Try to restore normal window positioning. adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.TimeScaleListView, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), SLOT(selectItem())); QObject::connect(m_ui.TimeScaleListView, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(contextMenu(const QPoint&))); QObject::connect(m_ui.BarSpinBox, SIGNAL(valueChanged(int)), SLOT(barChanged(int))); QObject::connect(m_ui.TimeSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(timeChanged(unsigned long))); QObject::connect(m_ui.TimeSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(refreshItems())); QObject::connect(m_ui.TempoSpinBox, SIGNAL(valueChanged(float, unsigned short, unsigned short)), SLOT(tempoChanged())); QObject::connect(m_ui.TempoSpinBox, SIGNAL(valueChanged(const QString&)), SLOT(tempoChanged())); QObject::connect(m_ui.TempoTapPushButton, SIGNAL(clicked()), SLOT(tempoTap())); QObject::connect(m_ui.TempoFactorPushButton, SIGNAL(clicked()), SLOT(tempoFactor())); QObject::connect(m_ui.KeySignatureAccidentalsComboBox, SIGNAL(activated(int)), SLOT(accidentalsChanged(int))); QObject::connect(m_ui.KeySignatureModeComboBox, SIGNAL(activated(int)), SLOT(modeChanged(int))); QObject::connect(m_ui.MarkerTextLineEdit, SIGNAL(textChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.MarkerColorToolButton, SIGNAL(clicked()), SLOT(markerColor())); QObject::connect(m_ui.RefreshPushButton, SIGNAL(clicked()), SLOT(refresh())); QObject::connect(m_ui.AddPushButton, SIGNAL(clicked()), SLOT(addItem())); QObject::connect(m_ui.UpdatePushButton, SIGNAL(clicked()), SLOT(updateItem())); QObject::connect(m_ui.RemovePushButton, SIGNAL(clicked()), SLOT(removeItem())); QObject::connect(m_ui.ClosePushButton, SIGNAL(clicked()), SLOT(reject())); stabilizeForm(); } // Destructor. qtractorTimeScaleForm::~qtractorTimeScaleForm (void) { delete m_pTempoTap; } // Time-scale accessor. void qtractorTimeScaleForm::setTimeScale ( qtractorTimeScale *pTimeScale ) { m_pTimeScale = pTimeScale; m_ui.TimeSpinBox->setTimeScale(m_pTimeScale); refreshItems(); } qtractorTimeScale *qtractorTimeScaleForm::timeScale (void) const { return m_pTimeScale; } // Select(ed) node by frame (time) void qtractorTimeScaleForm::setFrame ( unsigned long iFrame ) { if (m_pTimeScale == nullptr) return; ++m_iDirtySetup; qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(iFrame); if (pNode) { iFrame = pNode->frameSnapToBar(iFrame); pNode = cursor.seekFrame(iFrame); } if (pNode) { // Make this into view... m_ui.BarSpinBox->setValue(pNode->barFromFrame(iFrame) + 1); m_ui.TimeSpinBox->setValue(iFrame); m_ui.TempoSpinBox->setTempo(pNode->tempo, false); m_ui.TempoSpinBox->setBeatsPerBar(pNode->beatsPerBar, false); m_ui.TempoSpinBox->setBeatDivisor(pNode->beatDivisor, true); } qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(iFrame); if (pMarker && pMarker->frame == iFrame) setCurrentMarker(pMarker); else setCurrentMarker(nullptr); if (pMarker && iFrame >= pMarker->frame) setCurrentKeySignature(pMarker); else setCurrentKeySignature(nullptr); // Done. m_iDirtySetup = 0; // Locate nearest list item... if (pNode) setCurrentItem(pNode, iFrame); ensureVisibleFrame(iFrame); stabilizeForm(); // HACK: Force focus to Bar location entry field... m_ui.BarSpinBox->setFocus(); } unsigned long qtractorTimeScaleForm::frame (void) const { return m_ui.TimeSpinBox->value(); } unsigned short qtractorTimeScaleForm::bar (void) const { return m_ui.BarSpinBox->value() - 1; } // Current dirty flag accessor. bool qtractorTimeScaleForm::isDirty (void) { return (m_iDirtyTotal > 0); } // Refresh all list and views. void qtractorTimeScaleForm::refreshItems (void) { if (m_pTimeScale == nullptr) return; // (Re)Load complete tempo-map listing ... m_ui.TimeScaleListView->clear(); const qtractorTimeScale::DisplayFormat displayFormat = m_ui.TimeSpinBox->displayFormat(); qtractorTimeScale::Node *pNode = m_pTimeScale->nodes().first(); qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().first(); qtractorTimeScaleListItem *pListItem = nullptr; while (pNode) { while (pMarker && pMarker->frame < pNode->frame) { pListItem = new qtractorTimeScaleListItem(m_ui.TimeScaleListView, pListItem, m_pTimeScale, nullptr, pMarker, displayFormat); pMarker = pMarker->next(); } if (pMarker && pMarker->frame == pNode->frame) { pListItem = new qtractorTimeScaleListItem(m_ui.TimeScaleListView, pListItem, m_pTimeScale, pNode, pMarker, displayFormat); pMarker = pMarker->next(); } else { pListItem = new qtractorTimeScaleListItem(m_ui.TimeScaleListView, pListItem, m_pTimeScale, pNode, nullptr, displayFormat); } pNode = pNode->next(); } while (pMarker) { pListItem = new qtractorTimeScaleListItem(m_ui.TimeScaleListView, pListItem, m_pTimeScale, nullptr, pMarker, displayFormat); pMarker = pMarker->next(); } } void qtractorTimeScaleForm::refresh (void) { refreshItems(); timeChanged(frame()); m_iDirtyCount = 0; } // Current node list accessors. void qtractorTimeScaleForm::setCurrentItem ( qtractorTimeScale::Node *pNode, unsigned long iFrame ) { ++m_iDirtySetup; qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(iFrame); const int iItemCount = m_ui.TimeScaleListView->topLevelItemCount(); for (int i = iItemCount - 1; i >= 0; --i) { qtractorTimeScaleListItem *pListItem = static_cast ( m_ui.TimeScaleListView->topLevelItem(i)); if (pListItem && (pListItem->node() == pNode || (pMarker && pListItem->marker() == pMarker && iFrame >= pMarker->frame))) { m_ui.TimeScaleListView->setCurrentItem(pListItem); break; } } m_iDirtySetup = 0; } // Set current marker text & color... void qtractorTimeScaleForm::setCurrentMarker ( qtractorTimeScale::Marker *pMarker ) { QPalette pal; if (pMarker) { pal.setColor(QPalette::Text, pMarker->color); } else { QColor color = Qt::darkGray; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && !pOptions->sMarkerColor.isEmpty()) #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) color = QColor::fromString(pOptions->sMarkerColor); #else color = QColor(pOptions->sMarkerColor); #endif pal.setColor(QPalette::Text, color); } m_ui.MarkerTextLineEdit->setPalette(pal); if (pMarker) m_ui.MarkerTextLineEdit->setText(pMarker->text); else m_ui.MarkerTextLineEdit->clear(); } // Set current key-signature... void qtractorTimeScaleForm::setCurrentKeySignature ( qtractorTimeScale::Marker *pMarker ) { int iAccidentals = qtractorTimeScale::MinAccidentals; int iMode = -1; if (pMarker) { iAccidentals = pMarker->accidentals; iMode = pMarker->mode; } updateKeySignatures(iAccidentals, iMode); } // Refresh key signatures. void qtractorTimeScaleForm::updateKeySignatures ( int iAccidentals, int iMode ) { #ifdef CONFIG_DEBUG qDebug("qtractorTimeScaleForm::updateKeySignatures(%d, %d)", iAccidentals, iMode); #endif const bool bBlockAccidentals = m_ui.KeySignatureAccidentalsComboBox->blockSignals(true); const bool bBlockMode = m_ui.KeySignatureModeComboBox->blockSignals(true); m_ui.KeySignatureAccidentalsComboBox->clear(); int iIndex = 0; int iData = qtractorTimeScale::MinAccidentals; while (qtractorTimeScale::MaxAccidentals >= iData) { m_ui.KeySignatureAccidentalsComboBox->addItem( qtractorTimeScale::keySignatureName(iData, (iMode < 0 ? 0 : iMode), 0)); m_ui.KeySignatureAccidentalsComboBox->setItemData(iIndex++, iData++); } iIndex = m_ui.KeySignatureAccidentalsComboBox->findData(iAccidentals); if (iIndex < 0) { iIndex = 0; iMode = -1; } m_ui.KeySignatureAccidentalsComboBox->setCurrentIndex(iIndex); m_ui.KeySignatureModeComboBox->setCurrentIndex(iMode + 1); m_ui.KeySignatureAccidentalsComboBox->blockSignals(bBlockAccidentals); m_ui.KeySignatureModeComboBox->blockSignals(bBlockMode); } // Make given frame visble at the main tracks view. void qtractorTimeScaleForm::ensureVisibleFrame ( unsigned long iFrame ) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && pMainForm->tracks()) pMainForm->tracks()->trackView()->ensureVisibleFrame(iFrame); } // Time-scale node selection slot. void qtractorTimeScaleForm::selectItem (void) { if (m_pTimeScale == nullptr) return; if (m_iDirtySetup > 0) return; // Check if we need an update?... qtractorTimeScaleListItem *pListItem = static_cast ( m_ui.TimeScaleListView->currentItem()); if (pListItem == nullptr) return; qtractorTimeScale::Node *pNode = pListItem->node(); qtractorTimeScale::Marker *pMarker = pListItem->marker(); if (pNode == nullptr && pMarker == nullptr) return; if (m_iDirtyCount > 0) { QMessageBox::StandardButtons buttons = QMessageBox::Discard | QMessageBox::Cancel; if (m_ui.UpdatePushButton->isEnabled()) buttons |= QMessageBox::Apply; switch (QMessageBox::warning(this, tr("Warning"), tr("Some settings have been changed.\n\n" "Do you want to apply the changes?"), buttons)) { case QMessageBox::Apply: updateItem(); // Fall thru... case QMessageBox::Discard: break; default: // Cancel. return; } } // Get new one into view... ++m_iDirtySetup; if (pNode) { m_ui.BarSpinBox->setValue(pNode->bar + 1); m_ui.TimeSpinBox->setValue(pNode->frame); m_ui.TempoSpinBox->setTempo(pNode->tempo, false); m_ui.TempoSpinBox->setBeatsPerBar(pNode->beatsPerBar, false); m_ui.TempoSpinBox->setBeatDivisor(pNode->beatDivisor, true); ensureVisibleFrame(pNode->frame); } if (pMarker && pNode == nullptr) { const unsigned int iBar = m_pTimeScale->barFromFrame(pMarker->frame); m_ui.BarSpinBox->setValue(iBar + 1); m_ui.TimeSpinBox->setValue(pMarker->frame); ensureVisibleFrame(pMarker->frame); } setCurrentMarker(pMarker); setCurrentKeySignature(pMarker); m_iDirtySetup = 0; m_iDirtyCount = 0; stabilizeForm(); } // Check whether the current view is elligible for action. unsigned int qtractorTimeScaleForm::flags (void) const { if (m_pTimeScale == nullptr) return 0; unsigned int iFlags = 0; const float fTempo = m_ui.TempoSpinBox->tempo(); const unsigned short iBeatsPerBar = m_ui.TempoSpinBox->beatsPerBar(); const unsigned short iBeatDivisor = m_ui.TempoSpinBox->beatDivisor(); const unsigned short iBar = bar(); qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekBar(iBar); if (pNode && pNode->bar == iBar) { iFlags |= UpdateNode; if (pNode->prev()) iFlags |= RemoveNode; } if (pNode && qAbs(pNode->tempo - fTempo) < 0.001f // && pNode->beatType == iBeatType && pNode->beatsPerBar == iBeatsPerBar && pNode->beatDivisor == iBeatDivisor) iFlags &= ~UpdateNode; else iFlags |= AddNode; if (pNode && pNode->bar == iBar) iFlags &= ~AddNode; if (pNode && (pNode = pNode->next()) // real assignment && qAbs(pNode->tempo - fTempo) < 0.001f // && pNode->beatType == iBeatType && pNode->beatsPerBar == iBeatsPerBar && pNode->beatDivisor == iBeatDivisor) iFlags &= ~AddNode; const unsigned long iFrame = m_pTimeScale->frameFromBar(iBar); qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(iFrame); const QString& sMarkerText = m_ui.MarkerTextLineEdit->text().simplified(); const QColor& rgbMarkerColor = m_ui.MarkerTextLineEdit->palette().text().color(); int iAccidentals = qtractorTimeScale::MinAccidentals; const int iIndex = m_ui.KeySignatureAccidentalsComboBox->currentIndex(); if (iIndex > 0) iAccidentals = m_ui.KeySignatureAccidentalsComboBox->itemData(iIndex).toInt(); const int iMode = m_ui.KeySignatureModeComboBox->currentIndex() - 1; if (pMarker && pMarker->frame == iFrame) { if (!sMarkerText.isEmpty()) iFlags |= UpdateMarker; iFlags |= RemoveMarker; if (qtractorTimeScale::isKeySignature(iAccidentals, iMode)) iFlags |= UpdateKeySignature; } if (pMarker && pMarker->text == sMarkerText && pMarker->color == rgbMarkerColor) iFlags &= ~UpdateMarker; else if (!sMarkerText.isEmpty()) iFlags |= AddMarker; if (pMarker && pMarker->accidentals == iAccidentals && pMarker->mode == iMode) iFlags &= ~UpdateKeySignature; else if (qtractorTimeScale::isKeySignature(iAccidentals, iMode)) iFlags |= AddKeySignature; if (pMarker && pMarker->frame == iFrame) { iFlags &= ~AddMarker; iFlags &= ~AddKeySignature; } return iFlags; } // Add node method. void qtractorTimeScaleForm::addItem (void) { if (m_pTimeScale == nullptr) return; // Make it as an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const unsigned int iFlags = flags(); const unsigned long iFrame = frame(); if (iFlags & AddNode) { pSession->execute( new qtractorTimeScaleAddNodeCommand( m_pTimeScale, iFrame, m_ui.TempoSpinBox->tempo(), 2, m_ui.TempoSpinBox->beatsPerBar(), m_ui.TempoSpinBox->beatDivisor())); ++m_iDirtyTotal; } if (iFlags & AddMarker) { pSession->execute( new qtractorTimeScaleAddMarkerCommand( m_pTimeScale, iFrame, m_ui.MarkerTextLineEdit->text().simplified(), m_ui.MarkerTextLineEdit->palette().text().color())); ++m_iDirtyTotal; } if (iFlags & AddKeySignature) { int iAccidentals = qtractorTimeScale::MinAccidentals; const int iIndex = m_ui.KeySignatureAccidentalsComboBox->currentIndex(); if (iIndex > 0) iAccidentals = m_ui.KeySignatureAccidentalsComboBox->itemData(iIndex).toInt(); const int iMode = m_ui.KeySignatureModeComboBox->currentIndex() - 1; if (qtractorTimeScale::isKeySignature(iAccidentals, iMode)) { pSession->execute( new qtractorTimeScaleAddKeySignatureCommand( m_pTimeScale, iFrame, iAccidentals, iMode)); ++m_iDirtyTotal; } } refresh(); } // Update current node. void qtractorTimeScaleForm::updateItem (void) { if (m_pTimeScale == nullptr) return; // Make it as an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const unsigned int iFlags = flags(); const unsigned short iBar = bar(); if (iFlags & UpdateNode) { qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekBar(iBar); if (pNode && pNode->bar == iBar) { const unsigned long iFrame = pNode->frameFromBar(iBar); pSession->execute( new qtractorTimeScaleUpdateNodeCommand( m_pTimeScale, iFrame, m_ui.TempoSpinBox->tempo(), 2, m_ui.TempoSpinBox->beatsPerBar(), m_ui.TempoSpinBox->beatDivisor())); ++m_iDirtyTotal; } } if (iFlags & UpdateMarker) { const unsigned long iFrame = m_pTimeScale->frameFromBar(iBar); qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(iFrame); if (pMarker && pMarker->frame == iFrame) { pSession->execute( new qtractorTimeScaleUpdateMarkerCommand( m_pTimeScale, iFrame, m_ui.MarkerTextLineEdit->text().simplified(), m_ui.MarkerTextLineEdit->palette().text().color())); ++m_iDirtyTotal; } } if (iFlags & UpdateKeySignature) { const unsigned long iFrame = m_pTimeScale->frameFromBar(iBar); qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(iFrame); if (pMarker && pMarker->frame == iFrame) { int iAccidentals = qtractorTimeScale::MinAccidentals; const int iIndex = m_ui.KeySignatureAccidentalsComboBox->currentIndex(); if (iIndex > 0) iAccidentals = m_ui.KeySignatureAccidentalsComboBox->itemData(iIndex).toInt(); const int iMode = m_ui.KeySignatureModeComboBox->currentIndex() - 1; if (qtractorTimeScale::isKeySignature(iAccidentals, iMode)) { pSession->execute( new qtractorTimeScaleUpdateKeySignatureCommand( m_pTimeScale, iFrame, iAccidentals, iMode)); ++m_iDirtyTotal; } } } refresh(); } // Remove current node. void qtractorTimeScaleForm::removeItem (void) { if (m_pTimeScale == nullptr) return; // Make it as an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const unsigned int iFlags = flags(); const unsigned short iBar = bar(); if (iFlags & RemoveNode) { qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekBar(iBar); if (pNode && pNode->bar == iBar && pNode->prev()) { // Prompt user if he/she's sure about this... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bConfirmRemove) { // Show the warning... if (QMessageBox::warning(this, tr("Warning"), tr("About to remove tempo node:\n\n" "%1 (%2) %3 %4/%5\n\n" "Are you sure?") .arg(pNode->bar + 1) .arg(m_pTimeScale->textFromTick(pNode->tick)) .arg(pNode->tempo) .arg(pNode->beatsPerBar) .arg(1 << pNode->beatDivisor), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } // Go!... pSession->execute( new qtractorTimeScaleRemoveNodeCommand(m_pTimeScale, pNode)); ++m_iDirtyTotal; } } if (iFlags & RemoveMarker) { const unsigned long iFrame = m_pTimeScale->frameFromBar(iBar); qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(iFrame); if (pMarker && pMarker->frame == iFrame) { // Go! we just don't ask user about a thing... pSession->execute( new qtractorTimeScaleRemoveMarkerCommand( m_pTimeScale, pMarker)); ++m_iDirtyTotal; } } refresh(); } // Make changes due. void qtractorTimeScaleForm::barChanged ( int iBar ) { if (m_pTimeScale == nullptr) return; if (m_iDirtySetup > 0) return; ++m_iDirtySetup; if (iBar > 0) --iBar; qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekBar(iBar); const unsigned long iFrame = (pNode ? pNode->frameFromBar(iBar) : 0); m_ui.TimeSpinBox->setValue(iFrame); qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(iFrame); if (pMarker && pMarker->frame == iFrame) setCurrentMarker(pMarker); else setCurrentMarker(nullptr); if (pMarker && iFrame >= pMarker->frame) setCurrentKeySignature(pMarker); else setCurrentKeySignature(nullptr); m_iDirtySetup = 0; // Locate nearest list item... if (pNode) setCurrentItem(pNode, iFrame); ensureVisibleFrame(iFrame); ++m_iDirtyCount; stabilizeForm(); } void qtractorTimeScaleForm::timeChanged ( unsigned long iFrame ) { if (m_pTimeScale == nullptr) return; if (m_iDirtySetup > 0) return; ++m_iDirtySetup; qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(iFrame); if (pNode) { iFrame = pNode->frameSnapToBar(iFrame); pNode = cursor.seekFrame(iFrame); } if (pNode) m_ui.BarSpinBox->setValue(pNode->barFromFrame(iFrame) + 1); qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(iFrame); if (pMarker && pMarker->frame == iFrame) setCurrentMarker(pMarker); else setCurrentMarker(nullptr); if (pMarker && iFrame >= pMarker->frame) setCurrentKeySignature(pMarker); else setCurrentKeySignature(nullptr); m_iDirtySetup = 0; // Locate nearest list item... if (pNode) setCurrentItem(pNode, iFrame); ensureVisibleFrame(iFrame); ++m_iDirtyCount; stabilizeForm(); } // Tempo/Time-signature has changed. void qtractorTimeScaleForm::tempoChanged (void) { if (m_iDirtySetup > 0) return; #ifdef CONFIG_DEBUG qDebug("qtractorTimeScaleForm::tempoChanged()"); #endif m_iTempoTap = 0; m_fTempoTap = 0.0f; changed(); } // Key signature has changed. void qtractorTimeScaleForm::accidentalsChanged ( int iIndex ) { if (m_iDirtySetup > 0) return; #ifdef CONFIG_DEBUG qDebug("qtractorTimeScaleForm::accidentalsChanged(%d)", iIndex); #endif int iAccidentals = qtractorTimeScale::MinAccidentals; int iMode = -1; if (iIndex > 0) { iMode = m_ui.KeySignatureModeComboBox->currentIndex() - 1; if (iMode < 0) iMode = 0; iAccidentals = m_ui.KeySignatureAccidentalsComboBox->itemData(iIndex).toInt(); } updateKeySignatures(iAccidentals, iMode); changed(); } void qtractorTimeScaleForm::modeChanged ( int iMode ) { if (m_iDirtySetup > 0) return; #ifdef CONFIG_DEBUG qDebug("qtractorTimeScaleForm::modeChanged(%d)", iMode); #endif int iAccidentals = qtractorTimeScale::MinAccidentals; if (iMode > 0) { iAccidentals = 0; const int iIndex = m_ui.KeySignatureAccidentalsComboBox->currentIndex(); if (iIndex > 0) iAccidentals = m_ui.KeySignatureAccidentalsComboBox->itemData(iIndex).toInt(); } updateKeySignatures(iAccidentals, iMode - 1); changed(); } // Something has changed. void qtractorTimeScaleForm::changed (void) { if (m_iDirtySetup > 0) return; ++m_iDirtyCount; stabilizeForm(); } // Reject settings (Close button slot). void qtractorTimeScaleForm::reject (void) { bool bReject = true; // Check if there's any pending changes... if (m_iDirtyCount > 0) { if (QMessageBox::warning(this, tr("Warning"), tr("Some settings have been changed.\n\n" "Do you want to discard the changes?"), QMessageBox::Discard | QMessageBox::Cancel) == QMessageBox::Cancel) { bReject = false; } } if (bReject) QDialog::reject(); } // Tempo factor perform click. void qtractorTimeScaleForm::tempoFactor (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const float fTempoFactor = float(m_ui.TempoFactorSpinBox->value()); qtractorTimeScaleCommand *pTimeScaleCommand = new qtractorTimeScaleCommand(tr("tempo factor")); qtractorTimeScale::Node *pNode = m_pTimeScale->nodes().last(); for ( ; pNode; pNode = pNode->prev()) { pTimeScaleCommand->addNodeCommand( new qtractorTimeScaleUpdateNodeCommand( m_pTimeScale, pNode->frame, fTempoFactor * pNode->tempo, pNode->beatType, pNode->beatsPerBar, pNode->beatDivisor)); } if (pSession->execute(pTimeScaleCommand)) ++m_iDirtyTotal; refreshItems(); } // Tempo tap click. void qtractorTimeScaleForm::tempoTap (void) { #ifdef CONFIG_DEBUG qDebug("qtractorTimeScaleForm::tempoTap()"); #endif const int iTimeTap = m_pTempoTap->restart(); if (iTimeTap < 200 || iTimeTap > 2000) { // Magic! m_iTempoTap = 0; m_fTempoTap = 0.0f; return; } const float fTempoTap = ::rintf(60000.0f / float(iTimeTap)); #if 0 m_fTempoTap += fTempoTap; if (++m_iTempoTap > 2) { m_fTempoTap /= float(m_iTempoTap); m_ui.TempoSpinBox->setTempo(::rintf(m_fTempoTap), false); m_iTempoTap = 1; // Median-like averaging... } #else if (m_fTempoTap > 0.0f) { m_fTempoTap *= 0.5f; m_fTempoTap += 0.5f * fTempoTap; } else { m_fTempoTap = fTempoTap; } if (++m_iTempoTap > 2) { m_ui.TempoSpinBox->setTempo(::rintf(m_fTempoTap), true); m_iTempoTap = 1; // Median-like averaging... m_fTempoTap = fTempoTap; } #endif } // Marker color selection. void qtractorTimeScaleForm::markerColor (void) { #ifdef CONFIG_DEBUG qDebug("qtractorTimeScaleForm::markerColor()"); #endif QPalette pal(m_ui.MarkerTextLineEdit->palette()); QWidget *pParentWidget = nullptr; qtractorOptions *pOptions = qtractorOptions::getInstance(); QColorDialog::ColorDialogOptions options; if (pOptions && pOptions->bDontUseNativeDialogs) { options |= QColorDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } const QColor& color = QColorDialog::getColor( pal.text().color(), pParentWidget, tr("Marker Color"), options); if (color.isValid()) { pal.setColor(QPalette::Text, color); m_ui.MarkerTextLineEdit->setPalette(pal); if (pOptions) pOptions->sMarkerColor = color.name(); changed(); } } // Stabilize current form state. void qtractorTimeScaleForm::stabilizeForm (void) { const unsigned int iFlags = flags(); // m_ui.RefreshPushButton->setEnabled(m_iDirtyCount > 0); m_ui.AddPushButton->setEnabled(iFlags & (AddNode | AddMarker | AddKeySignature)); m_ui.UpdatePushButton->setEnabled(iFlags & (UpdateNode | UpdateMarker | UpdateKeySignature)); m_ui.RemovePushButton->setEnabled(iFlags & (RemoveNode | RemoveMarker)); } // Time-scale list view context menu handler. void qtractorTimeScaleForm::contextMenu ( const QPoint& /*pos*/ ) { // Build the device context menu... QMenu menu(this); QAction *pAction; const unsigned int iFlags = flags(); pAction = menu.addAction( QIcon::fromTheme("formAdd"), tr("&Add"), this, SLOT(addItem())); pAction->setEnabled(iFlags & (AddNode | AddMarker | AddKeySignature)); pAction = menu.addAction( QIcon::fromTheme("formAccept"), tr("&Update"), this, SLOT(updateItem())); pAction->setEnabled(iFlags & (UpdateNode | UpdateMarker | UpdateKeySignature)); pAction = menu.addAction( QIcon::fromTheme("formRemove"), tr("&Remove"), this, SLOT(removeItem())); pAction->setEnabled(iFlags & (RemoveNode | RemoveMarker)); menu.addSeparator(); pAction = menu.addAction( QIcon::fromTheme("formRefresh"), tr("&Refresh"), this, SLOT(refresh())); // pAction->setEnabled(m_iDirtyCount > 0); // menu.exec(m_ui.BusListView->mapToGlobal(pos)); menu.exec(QCursor::pos()); } // end of qtractorTimeScaleForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorEngineCommand.h0000644000000000000000000000013215124701674017747 xustar0030 mtime=1767080892.784263491 30 atime=1767080892.784263491 30 ctime=1767080892.784263491 qtractor-1.5.11/src/qtractorEngineCommand.h0000644000175000001440000001417615124701674017750 0ustar00rncbcusers// qtractorEngineCommand.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorEngineCommand_h #define __qtractorEngineCommand_h #include "qtractorCommand.h" #include "qtractorEngine.h" // Forward declarations. class qtractorMixerMeter; //---------------------------------------------------------------------- // class qtractorBusCommand - declaration. // class qtractorBusCommand : public qtractorCommand { public: // Constructor. qtractorBusCommand(const QString& sName, qtractorBus *pBus, qtractorBus *pAfterBus = nullptr, qtractorBus::BusMode busMode = qtractorBus::None); // Bus accessors. qtractorBus *bus() const { return m_pBus; } // Bus properties accessors. void setBusMode(qtractorBus::BusMode busMode) { m_busMode = busMode; } qtractorBus::BusMode busMode() const { return m_busMode; } void setBusType(qtractorTrack::TrackType busType) { m_busType = busType; } qtractorTrack::TrackType busType() const { return m_busType; } void setBusName(const QString& sBusName) { m_sBusName = sBusName; } const QString& busName() const { return m_sBusName; } void setMonitor(bool bMonitor) { m_bMonitor = bMonitor; } bool isMonitor() const { return m_bMonitor; } // Special Audio bus properties accessors. void setChannels(unsigned short iChannels) { m_iChannels = iChannels; } unsigned short channels() const { return m_iChannels; } void setAutoConnect(bool bAutoConnect) { m_bAutoConnect = bAutoConnect; } bool isAutoConnect() const { return m_bAutoConnect; } // Special MIDI bus properties accessors. void setInstrumentName(const QString& sInstrumentName) { m_sInstrumentName = sInstrumentName; } const QString& instrumentName() const { return m_sInstrumentName; } protected: // Bus command methods. bool createBus(); bool updateBus(); bool deleteBus(); // Monitor meter accessor. qtractorMixerMeter *meter() const; private: // Instance variables. qtractorBus *m_pBus; qtractorBus *m_pAfterBus; qtractorBus::BusMode m_busMode; qtractorTrack::TrackType m_busType; QString m_sBusName; bool m_bMonitor; unsigned short m_iChannels; bool m_bAutoConnect; QString m_sInstrumentName; }; //---------------------------------------------------------------------- // class qtractorCreateBusCommand - declaration. // class qtractorCreateBusCommand : public qtractorBusCommand { public: // Constructor. qtractorCreateBusCommand(qtractorBus *pAfterBus); // Bus creation command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorUpdateBusCommand - declaration. // class qtractorUpdateBusCommand : public qtractorBusCommand { public: // Constructor. qtractorUpdateBusCommand(qtractorBus *pBus); // Bus update command methods. bool redo(); bool undo() { return redo(); } }; //---------------------------------------------------------------------- // class qtractorDeleteBusCommand - declaration. // class qtractorDeleteBusCommand : public qtractorBusCommand { public: // Constructor. qtractorDeleteBusCommand(qtractorBus *pBus); // Bus deletion command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorMoveBusCommand - declaration. // class qtractorMoveBusCommand : public qtractorBusCommand { public: // Constructor. qtractorMoveBusCommand(qtractorBus *pBus, int iDelta); // Plugin-move command methods. bool redo(); bool undo() { return redo(); } private: // Instance variables. int m_iDelta; }; //---------------------------------------------------------------------- // class qtractorBusMonitorCommand - declaration. // class qtractorBusMonitorCommand : public qtractorBusCommand { public: // Constructor. qtractorBusMonitorCommand(qtractorBus *pBus, bool bMonitor); // Bus-gain command methods. bool redo(); bool undo() { return redo(); } }; //---------------------------------------------------------------------- // class qtractorBusGainCommand - declaration. // class qtractorBusGainCommand : public qtractorBusCommand { public: // Constructor. qtractorBusGainCommand(qtractorBus *pBus, qtractorBus::BusMode busMode, float fGain); // Bus-gain command methods. bool redo(); bool undo() { return redo(); } // Gain value retrieval. float gain() const { return m_fGain; } // Last known gain predicate. float prevGain() const { return m_fPrevGain; } private: // Instance variables. float m_fGain; float m_fPrevGain; }; //---------------------------------------------------------------------- // class qtractorBusPanningCommand - declaration. // class qtractorBusPanningCommand : public qtractorBusCommand { public: // Constructor. qtractorBusPanningCommand(qtractorBus *pBus, qtractorBus::BusMode busMode, float fPanning); // Bus-panning command methods. bool redo(); bool undo() { return redo(); } // Panning value retrieval. float panning() const { return m_fPanning; } // Last known panning predicate. float prevPanning() const { return m_fPrevPanning; } private: // Instance variables. float m_fPanning; float m_fPrevPanning; }; #endif // __qtractorEngineCommand_h // end of qtractorEngineCommand.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiControlObserver.h0000644000000000000000000000013215124701674021176 xustar0030 mtime=1767080892.790263466 30 atime=1767080892.790263466 30 ctime=1767080892.790263466 qtractor-1.5.11/src/qtractorMidiControlObserver.h0000644000175000001440000000771315124701674021176 0ustar00rncbcusers// qtractorMidiControlObserver.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorMidiControlObserver_h #define __qtractorMidiControlObserver_h #include "qtractorObserver.h" #include "qtractorMidiControl.h" // Forward declarations. class qtractorCurveList; //---------------------------------------------------------------------- // class qtractorMidiControlObserver -- MIDI controller observers. // class qtractorMidiControlObserver : public qtractorObserver { public: // Constructor. qtractorMidiControlObserver(qtractorSubject *pSubject); // Destructor. virtual ~qtractorMidiControlObserver(); // Key accessors. void setType(qtractorMidiControl::ControlType ctype) { m_ctype = ctype; } qtractorMidiControl::ControlType type() const { return m_ctype; } void setChannel(unsigned short iChannel) { m_iChannel = iChannel; } unsigned short channel() const { return m_iChannel; } void setParam(unsigned short iParam) { m_iParam = iParam; } unsigned short param() const { return m_iParam; } // Properties accessors. void setLogarithmic(bool bLogarithmic) { m_bLogarithmic = bLogarithmic; } bool isLogarithmic() const { return m_bLogarithmic; } void setFeedback(bool bFeedback) { m_bFeedback = bFeedback; } bool isFeedback() const { return m_bFeedback; } void setInvert(bool bInvert) { m_bInvert = bInvert; } bool isInvert() const { return m_bInvert; } void setHook(bool bHook) { m_bHook = bHook; } bool isHook() const { return m_bHook; } void setLatch(bool bLatch) { m_bLatch = bLatch; } bool isLatch() const { return m_bLatch; } // Normalized scale accessors. void setScaleValue(float fScale) { setValue(valueFromScale(fScale, m_bLogarithmic)); } float scaleValue() const { return scaleFromValue(value(), m_bLogarithmic); } // MIDI mapped value converters. void setMidiValue(unsigned short iMidiValue); unsigned short midiValue() const; // Normalized scale convertors. float valueFromScale(float fScale, bool bLogarithmic) const; float scaleFromValue(float fValue, bool bLogarithmic) const; // Special indirect automation relatives accessors. void setCurveList(qtractorCurveList *pCurveList) { m_pCurveList = pCurveList; } qtractorCurveList *curveList() const { return m_pCurveList; } protected: // Updater. virtual void update(bool bUpdate); // MIDI scale type (7bit vs. 14bit). unsigned short midiScale() const; // Special action/shortcut mode accessors. void setTriggered(bool bTriggered) { m_bTriggered = bTriggered; } bool isTriggered() const { return m_bTriggered; } private: // Key members. qtractorMidiControl::ControlType m_ctype; unsigned short m_iChannel; unsigned short m_iParam; // Property members. bool m_bLogarithmic; bool m_bFeedback; bool m_bInvert; bool m_bHook; bool m_bLatch; // Special action/shortcut mode property. bool m_bTriggered; // Tracking/catch-up members. float m_fMidiValue; bool m_bMidiSync; // Special indirect automation relatives. qtractorCurveList *m_pCurveList; }; #endif // __qtractorMidiControlObserver_h // end of qtractorMidiControlObserver.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiEditor.cpp0000644000000000000000000000013215124701674017627 xustar0030 mtime=1767080892.792263458 30 atime=1767080892.791263462 30 ctime=1767080892.792263458 qtractor-1.5.11/src/qtractorMidiEditor.cpp0000644000175000001440000051612715124701674017633 0ustar00rncbcusers// qtractorMidiEditor.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorMidiEditor.h" #include "qtractorMidiEditList.h" #include "qtractorMidiEditTime.h" #include "qtractorMidiEditView.h" #include "qtractorMidiEditEvent.h" #include "qtractorMidiThumbView.h" #include "qtractorMidiEditCommand.h" #include "qtractorMidiEngine.h" #include "qtractorMidiClip.h" #include "qtractorMidiToolsForm.h" #include "qtractorInstrument.h" #include "qtractorRubberBand.h" #include "qtractorTimeScale.h" #include "qtractorTimeScaleCommand.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) #include #endif // Follow-playhead: maximum iterations on hold. #define QTRACTOR_SYNC_VIEW_HOLD 46 // Minimum event width (default). #define QTRACTOR_MIN_EVENT_WIDTH 5 // An double-dash string reference (formerly empty blank). static QString g_sDashes = "--"; //---------------------------------------------------------------------------- // MIDI Note Names - Default note names hash map. static QHash g_noteNames; void qtractorMidiEditor::initDefaultNoteNames (void) { static struct { unsigned char note; const char *name; } s_aNoteNames[] = { // Diatonic note map... { 0, QT_TR_NOOP("C") }, { 1, QT_TR_NOOP("C#/Db") }, { 2, QT_TR_NOOP("D") }, { 3, QT_TR_NOOP("D#/Eb") }, { 4, QT_TR_NOOP("E") }, { 5, QT_TR_NOOP("F") }, { 6, QT_TR_NOOP("F#/Gb") }, { 7, QT_TR_NOOP("G") }, { 8, QT_TR_NOOP("G#/Ab") }, { 9, QT_TR_NOOP("A") }, { 10, QT_TR_NOOP("A#/Bb") }, { 11, QT_TR_NOOP("B") }, // GM Drum note map... { 35, QT_TR_NOOP("Acoustic Bass Drum") }, { 36, QT_TR_NOOP("Bass Drum 1") }, { 37, QT_TR_NOOP("Side Stick") }, { 38, QT_TR_NOOP("Acoustic Snare") }, { 39, QT_TR_NOOP("Hand Clap") }, { 40, QT_TR_NOOP("Electric Snare") }, { 41, QT_TR_NOOP("Low Floor Tom") }, { 42, QT_TR_NOOP("Closed Hi-Hat") }, { 43, QT_TR_NOOP("High Floor Tom") }, { 44, QT_TR_NOOP("Pedal Hi-Hat") }, { 45, QT_TR_NOOP("Low Tom") }, { 46, QT_TR_NOOP("Open Hi-Hat") }, { 47, QT_TR_NOOP("Low-Mid Tom") }, { 48, QT_TR_NOOP("Hi-Mid Tom") }, { 49, QT_TR_NOOP("Crash Cymbal 1") }, { 50, QT_TR_NOOP("High Tom") }, { 51, QT_TR_NOOP("Ride Cymbal 1") }, { 52, QT_TR_NOOP("Chinese Cymbal") }, { 53, QT_TR_NOOP("Ride Bell") }, { 54, QT_TR_NOOP("Tambourine") }, { 55, QT_TR_NOOP("Splash Cymbal") }, { 56, QT_TR_NOOP("Cowbell") }, { 57, QT_TR_NOOP("Crash Cymbal 2") }, { 58, QT_TR_NOOP("Vibraslap") }, { 59, QT_TR_NOOP("Ride Cymbal 2") }, { 60, QT_TR_NOOP("Hi Bongo") }, { 61, QT_TR_NOOP("Low Bongo") }, { 62, QT_TR_NOOP("Mute Hi Conga") }, { 63, QT_TR_NOOP("Open Hi Conga") }, { 64, QT_TR_NOOP("Low Conga") }, { 65, QT_TR_NOOP("High Timbale") }, { 66, QT_TR_NOOP("Low Timbale") }, { 67, QT_TR_NOOP("High Agogo") }, { 68, QT_TR_NOOP("Low Agogo") }, { 69, QT_TR_NOOP("Cabasa") }, { 70, QT_TR_NOOP("Maracas") }, { 71, QT_TR_NOOP("Short Whistle") }, { 72, QT_TR_NOOP("Long Whistle") }, { 73, QT_TR_NOOP("Short Guiro") }, { 74, QT_TR_NOOP("Long Guiro") }, { 75, QT_TR_NOOP("Claves") }, { 76, QT_TR_NOOP("Hi Wood Block") }, { 77, QT_TR_NOOP("Low Wood Block") }, { 78, QT_TR_NOOP("Mute Cuica") }, { 79, QT_TR_NOOP("Open Cuica") }, { 80, QT_TR_NOOP("Mute Triangle") }, { 81, QT_TR_NOOP("Open Triangle") }, { 0, nullptr } }; if (g_noteNames.isEmpty()) { for (int i = 0; s_aNoteNames[i].name; ++i) { g_noteNames.insert( s_aNoteNames[i].note, tr(s_aNoteNames[i].name)); } } } // Default note name map accessor. const QString qtractorMidiEditor::defaultNoteName ( unsigned char note, bool fDrums ) { initDefaultNoteNames(); if (fDrums) { // Check whether the drum note exists... const QHash::ConstIterator& iter = g_noteNames.constFind(note); if (iter != g_noteNames.constEnd()) return iter.value(); } return g_noteNames.value(note % 12) + QString::number((note / 12) - 1); } //---------------------------------------------------------------------------- // MIDI Controller Names - Default controller names hash map. static QHash g_controllerNames; void qtractorMidiEditor::initDefaultControllerNames (void) { static struct { unsigned char controller; const char *name; } s_aControllerNames[] = { { 0, QT_TR_NOOP("Bank Select (coarse)") }, { 1, QT_TR_NOOP("Modulation Wheel (coarse)") }, { 2, QT_TR_NOOP("Breath Controller (coarse)") }, { 4, QT_TR_NOOP("Foot Pedal (coarse)") }, { 5, QT_TR_NOOP("Portamento Time (coarse)") }, { 6, QT_TR_NOOP("Data Entry (coarse)") }, { 7, QT_TR_NOOP("Volume (coarse)") }, { 8, QT_TR_NOOP("Balance (coarse)") }, { 10, QT_TR_NOOP("Pan Position (coarse)") }, { 11, QT_TR_NOOP("Expression (coarse)") }, { 12, QT_TR_NOOP("Effect Control 1 (coarse)") }, { 13, QT_TR_NOOP("Effect Control 2 (coarse)") }, { 16, QT_TR_NOOP("General Purpose Slider 1") }, { 17, QT_TR_NOOP("General Purpose Slider 2") }, { 18, QT_TR_NOOP("General Purpose Slider 3") }, { 19, QT_TR_NOOP("General Purpose Slider 4") }, { 32, QT_TR_NOOP("Bank Select (fine)") }, { 33, QT_TR_NOOP("Modulation Wheel (fine)") }, { 34, QT_TR_NOOP("Breath Controller (fine)") }, { 36, QT_TR_NOOP("Foot Pedal (fine)") }, { 37, QT_TR_NOOP("Portamento Time (fine)") }, { 38, QT_TR_NOOP("Data Entry (fine)") }, { 39, QT_TR_NOOP("Volume (fine)") }, { 40, QT_TR_NOOP("Balance (fine)") }, { 42, QT_TR_NOOP("Pan Position (fine)") }, { 43, QT_TR_NOOP("Expression (fine)") }, { 44, QT_TR_NOOP("Effect Control 1 (fine)") }, { 45, QT_TR_NOOP("Effect Control 2 (fine)") }, { 64, QT_TR_NOOP("Hold Pedal (on/off)") }, { 65, QT_TR_NOOP("Portamento (on/off)") }, { 66, QT_TR_NOOP("Sostenuto Pedal (on/off)") }, { 67, QT_TR_NOOP("Soft Pedal (on/off)") }, { 68, QT_TR_NOOP("Legato Pedal (on/off)") }, { 69, QT_TR_NOOP("Hold 2 Pedal (on/off)") }, { 70, QT_TR_NOOP("Sound Variation") }, { 71, QT_TR_NOOP("Filter Resonance") }, { 72, QT_TR_NOOP("Release Time") }, { 73, QT_TR_NOOP("Attack Time") }, { 74, QT_TR_NOOP("Brightness") }, { 75, QT_TR_NOOP("Decay Time") }, { 76, QT_TR_NOOP("Vibrato Rate") }, { 77, QT_TR_NOOP("Vibrato Depth") }, { 78, QT_TR_NOOP("Vibrato Delay") }, { 80, QT_TR_NOOP("General Purpose Button 1 (on/off)") }, { 81, QT_TR_NOOP("General Purpose Button 2 (on/off)") }, { 82, QT_TR_NOOP("General Purpose Button 3 (on/off)") }, { 83, QT_TR_NOOP("General Purpose Button 4 (on/off)") }, { 91, QT_TR_NOOP("Effects Level") }, { 92, QT_TR_NOOP("Tremolo Level") }, { 93, QT_TR_NOOP("Chorus Level") }, { 94, QT_TR_NOOP("Celeste Level") }, { 95, QT_TR_NOOP("Phaser Level") }, { 96, QT_TR_NOOP("Data Button Increment") }, { 97, QT_TR_NOOP("Data Button Decrement") }, { 98, QT_TR_NOOP("Non-Registered Parameter (fine)") }, { 99, QT_TR_NOOP("Non-Registered Parameter (coarse)") }, {100, QT_TR_NOOP("Registered Parameter (fine)") }, {101, QT_TR_NOOP("Registered Parameter (coarse)") }, {120, QT_TR_NOOP("All Sound Off") }, {121, QT_TR_NOOP("All Controllers Off") }, {122, QT_TR_NOOP("Local Keyboard (on/off)") }, {123, QT_TR_NOOP("All Notes Off") }, {124, QT_TR_NOOP("Omni Mode Off") }, {125, QT_TR_NOOP("Omni Mode On") }, {126, QT_TR_NOOP("Mono Operation") }, {127, QT_TR_NOOP("Poly Operation") }, { 0, nullptr } }; if (g_controllerNames.isEmpty()) { // Pre-load controller-names hash table... for (int i = 0; s_aControllerNames[i].name; ++i) { g_controllerNames.insert( s_aControllerNames[i].controller, tr(s_aControllerNames[i].name)); } } } // Default controller name accessor. const QString& qtractorMidiEditor::defaultControllerName ( unsigned char controller ) { initDefaultControllerNames(); const QHash::ConstIterator& iter = g_controllerNames.constFind(controller); if (iter == g_controllerNames.constEnd()) return g_sDashes; else return iter.value(); } //---------------------------------------------------------------------------- // MIDI RPN Names - Default RPN names hash map. static QMap g_rpnNames; void qtractorMidiEditor::initDefaultRpnNames (void) { static struct { unsigned short param; const char *name; } s_aRpnNames[] = { { 0, QT_TR_NOOP("Pitch Bend Sensitivity") }, { 1, QT_TR_NOOP("Fine Tune") }, { 2, QT_TR_NOOP("Coarse Tune") }, { 3, QT_TR_NOOP("Tuning Program") }, { 4, QT_TR_NOOP("Tuning Bank") }, { 0, nullptr } }; if (g_rpnNames.isEmpty()) { // Pre-load RPN-names hash table... for (int i = 0; s_aRpnNames[i].name; ++i) { g_rpnNames.insert( s_aRpnNames[i].param, tr(s_aRpnNames[i].name)); } } } // Default RPN map accessor. const QMap& qtractorMidiEditor::defaultRpnNames (void) { initDefaultRpnNames(); return g_rpnNames; } //---------------------------------------------------------------------------- // MIDI NRPN Names - Default NRPN names hash map. static QMap g_nrpnNames; void qtractorMidiEditor::initDefaultNrpnNames (void) { static struct { unsigned short param; const char *name; } s_aNrpnNames[] = { { 136, QT_TR_NOOP("Vibrato Rate") }, { 137, QT_TR_NOOP("Vibrato Depth") }, { 138, QT_TR_NOOP("Vibrato Delay") }, { 160, QT_TR_NOOP("Filter Cutoff") }, { 161, QT_TR_NOOP("Filter Resonance") }, { 227, QT_TR_NOOP("EG Attack") }, { 228, QT_TR_NOOP("EG Decay") }, { 230, QT_TR_NOOP("EG Release") }, // GS Drum NRPN map... { 2560, QT_TR_NOOP("Drum Filter Cutoff") }, { 2688, QT_TR_NOOP("Drum Filter Resonance") }, { 2816, QT_TR_NOOP("Drum EG Attack") }, { 2944, QT_TR_NOOP("Drum EG Decay") }, { 3072, QT_TR_NOOP("Drum Pitch Coarse") }, { 3200, QT_TR_NOOP("Drum Pitch Fine") }, { 3328, QT_TR_NOOP("Drum Level") }, { 3584, QT_TR_NOOP("Drum Pan") }, { 3712, QT_TR_NOOP("Drum Reverb Send") }, { 3840, QT_TR_NOOP("Drum Chorus Send") }, { 3968, QT_TR_NOOP("Drum Variation Send") }, { 0, nullptr } }; initDefaultNoteNames(); if (g_nrpnNames.isEmpty()) { // Pre-load NRPN-names hash table... const QString sDrumNrpnName("%1 (%2)"); for (int i = 0; s_aNrpnNames[i].name; ++i) { const unsigned short param = s_aNrpnNames[i].param; const QString& sName = tr(s_aNrpnNames[i].name); if (param < 2560) { g_nrpnNames.insert(param, sName); } else { QHash::ConstIterator iter = g_noteNames.constBegin(); const QHash::ConstIterator& iter_end = g_noteNames.constEnd(); for ( ; iter != iter_end; ++iter) { const unsigned char note = iter.key(); if (note < 12) continue; g_nrpnNames.insert(param + note, sDrumNrpnName.arg(iter.value()).arg(note)); } } } } } // Default NRPN map accessor. const QMap& qtractorMidiEditor::defaultNrpnNames (void) { initDefaultNrpnNames(); return g_nrpnNames; } //---------------------------------------------------------------------------- // MIDI Control-14 Names - Default controller names hash map. static QHash g_control14Names; void qtractorMidiEditor::initDefaultControl14Names (void) { static struct { unsigned char controller; const char *name; } s_aControl14Names[] = { { 1, QT_TR_NOOP("Modulation Wheel (14bit)") }, { 2, QT_TR_NOOP("Breath Controller (14bit)") }, { 4, QT_TR_NOOP("Foot Pedal (14bit)") }, { 5, QT_TR_NOOP("Portamento Time (14bit)") }, { 7, QT_TR_NOOP("Volume (14bit)") }, { 8, QT_TR_NOOP("Balance (14bit)") }, { 10, QT_TR_NOOP("Pan Position (14bit)") }, { 11, QT_TR_NOOP("Expression (14bit)") }, { 12, QT_TR_NOOP("Effect Control 1 (14bit)") }, { 13, QT_TR_NOOP("Effect Control 2 (14bit)") }, { 16, QT_TR_NOOP("General Purpose Slider 1 (14bit)") }, { 17, QT_TR_NOOP("General Purpose Slider 2 (14bit)") }, { 18, QT_TR_NOOP("General Purpose Slider 3 (14bit)") }, { 19, QT_TR_NOOP("General Purpose Slider 4 (14bit)") }, { 0, nullptr } }; if (g_control14Names.isEmpty()) { // Pre-load controller-names hash table... for (int i = 0; s_aControl14Names[i].name; ++i) { g_control14Names.insert( s_aControl14Names[i].controller, tr(s_aControl14Names[i].name)); } } } // Default control-14 name accessor. const QString& qtractorMidiEditor::defaultControl14Name ( unsigned char controller ) { initDefaultControl14Names(); QHash::ConstIterator iter = g_control14Names.constFind(controller); if (iter == g_control14Names.constEnd()) return g_sDashes; else return iter.value(); } //---------------------------------------------------------------------------- // MIDI Scale Names - Default scale names table. static QStringList g_scaleKeys; static QStringList g_scaleTypes; // Default scale key note names accessor. const QStringList& qtractorMidiEditor::scaleKeyNames (void) { initDefaultNoteNames(); if (g_scaleKeys.isEmpty()) { for (int i = 0; i < 12; ++i) { const unsigned char note = i; g_scaleKeys.append(g_noteNames.value(note)); } } return g_scaleKeys; } // Default scale type names table accessor. const QStringList& qtractorMidiEditor::scaleTypeNames (void) { scaleTabNote(0, 0); // Dummy initializer return g_scaleTypes; } // Scale key/note resolver. int qtractorMidiEditor::scaleTabNote ( int iScale, int n ) { static struct { const char *name; unsigned char note[12]; } s_aScaleTab[] = { { QT_TR_NOOP("Chromatic"), { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 } }, { QT_TR_NOOP("Major"), { 0, 0, 2, 2, 4, 5, 5, 7, 7, 9, 9,11 } }, { QT_TR_NOOP("Minor"), { 0, 0, 2, 3, 3, 5, 5, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Melodic Minor (Asc)"), { 0, 0, 2, 3, 3, 5, 5, 7, 7, 9, 9,11 } }, { QT_TR_NOOP("Melodic Minor (Desc)"), { 0, 0, 2, 3, 3, 5, 5, 7, 8, 8,10,10 } }, { QT_TR_NOOP("Whole Tone"), { 0, 0, 2, 2, 4, 4, 6, 6, 8, 8,10,10 } }, { QT_TR_NOOP("Pentatonic Major"), { 0, 0, 2, 2, 4, 4, 4, 7, 7, 9, 9, 9 } }, { QT_TR_NOOP("Pentatonic Minor"), { 0, 0, 0, 3, 3, 5, 5, 7, 7, 7,10,10 } }, { QT_TR_NOOP("Pentatonic Blues"), { 0, 0, 0, 3, 3, 5, 6, 7, 7, 7,10,10 } }, { QT_TR_NOOP("Pentatonic Neutral"), { 0, 0, 2, 2, 2, 5, 5, 7, 7, 7,10,10 } }, { QT_TR_NOOP("Octatonic (H-W)"), { 0, 1, 1, 3, 4, 4, 6, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Octatonic (W-H)"), { 0, 0, 2, 3, 3, 5, 6, 6, 8, 9, 9,11 } }, { QT_TR_NOOP("Ionian"), { 0, 0, 2, 2, 4, 5, 5, 7, 7, 9, 9,11 } }, // identical to "Major" { QT_TR_NOOP("Dorian"), { 0, 0, 2, 3, 3, 5, 5, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Phrygian"), { 0, 1, 1, 3, 3, 5, 5, 7, 8, 8,10,10 } }, { QT_TR_NOOP("Lydian"), { 0, 0, 2, 2, 4, 4, 6, 7, 7, 9, 9,11 } }, { QT_TR_NOOP("Mixolydian"), { 0, 0, 2, 2, 4, 5, 5, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Aeolian"), { 0, 0, 2, 3, 3, 5, 5, 7, 8, 8,10,10 } }, // identical to "Melodic Minor (Descending)" { QT_TR_NOOP("Locrian"), { 0, 1, 1, 3, 3, 5, 6, 6, 8, 8,10,10 } }, { QT_TR_NOOP("Egyptian"), { 0, 0, 2, 2, 2, 5, 5, 7, 7, 7,10,10 } }, // identical to "Pentatonic Neutral" { QT_TR_NOOP("Eight Tone Spanish"), { 0, 1, 1, 3, 4, 5, 6, 6, 6, 6,10,10 } }, { QT_TR_NOOP("Hawaiian"), { 0, 0, 2, 3, 3, 5, 5, 7, 7, 9, 9,11 } }, // identical to "Melodic Minor (Ascending)" { QT_TR_NOOP("Hindu"), { 0, 0, 2, 2, 4, 5, 5, 7, 8, 8,10,10 } }, { QT_TR_NOOP("Hirajoshi"), { 0, 0, 2, 3, 3, 3, 3, 7, 8, 8, 8, 8 } }, { QT_TR_NOOP("Hungarian Major"), { 0, 0, 0, 3, 4, 4, 6, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Hungarian Minor"), { 0, 0, 2, 3, 3, 3, 6, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Hungarian Gypsy"), { 0, 1, 1, 1, 4, 5, 5, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Japanese (A)"), { 0, 1, 1, 1, 1, 5, 5, 7, 8, 8, 8, 8 } }, { QT_TR_NOOP("Japanese (B)"), { 0, 0, 2, 2, 2, 5, 5, 7, 8, 8, 8, 8 } }, { QT_TR_NOOP("Jewish (Adonai Malakh)"), { 0, 1, 2, 3, 3, 5, 5, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Jewish (Ahaba Rabba)"), { 0, 1, 1, 1, 4, 5, 5, 7, 8, 8,10,10 } }, { QT_TR_NOOP("Jewish (Magen Abot)"), { 0, 1, 1, 3, 4, 4, 6, 6, 8, 8,10,11 } }, { QT_TR_NOOP("Oriental (A)"), { 0, 1, 1, 1, 4, 5, 6, 6, 8, 8,10,10 } }, { QT_TR_NOOP("Oriental (B)"), { 0, 1, 1, 1, 1, 1, 6, 6, 6, 9,10,10 } }, { QT_TR_NOOP("Oriental (C)"), { 0, 1, 1, 1, 4, 5, 6, 6, 6, 9,10,10 } }, { QT_TR_NOOP("Roumanian Minor"), { 0, 0, 2, 3, 3, 3, 6, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Neapolitan"), { 0, 1, 1, 3, 3, 5, 5, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Neapolitan Major"), { 0, 1, 1, 3, 3, 5, 5, 7, 7, 9, 9,11 } }, { QT_TR_NOOP("Neapolitan Minor"), { 0, 1, 1, 1, 1, 5, 5, 7, 8, 8,10,10 } }, // { QT_TR_NOOP("Mohammedan"), { 0, 0, 2, 3, 3, 5, 5, 7, 8, 8, 8,11 } }, // identical to "Harmonic Minor" { QT_TR_NOOP("Overtone"), { 0, 0, 2, 2, 4, 4, 6, 7, 7, 9,10,10 } }, // { QT_TR_NOOP("Diatonic"), { 0, 0, 2, 2, 4, 4, 4, 7, 7, 9, 9, 9 } }, // identical to "Pentatonic Major" // { QT_TR_NOOP("Double Harmonic"), { 0, 1, 1, 1, 4, 5, 5, 7, 8, 8, 8,11 } }, // identical to "Hungarian Gypsy Persian" { QT_TR_NOOP("Eight Tone Spanish"), { 0, 1, 1, 3, 4, 5, 6, 6, 8, 8,10,10 } }, { QT_TR_NOOP("Leading Whole Tone"), { 0, 0, 2, 2, 4, 4, 6, 6, 8, 8,10,11 } }, { QT_TR_NOOP("Nine Tone Scale"), { 0, 0, 2, 3, 4, 4, 6, 7, 8, 9, 9,11 } }, { QT_TR_NOOP("Dominant Seventh"), { 0, 0, 2, 2, 2, 5, 5, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Augmented"), { 0, 0, 0, 3, 4, 4, 4, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Algerian"), { 0, 0, 2, 3, 3, 5, 6, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Arabian (A)"), { 0, 0, 2, 3, 3, 5, 6, 6, 8, 9, 9,11 } }, // identical to "Octatonic (W-H)" { QT_TR_NOOP("Arabian (B)"), { 0, 0, 2, 2, 4, 5, 6, 6, 8, 8,10,10 } }, // { QT_TR_NOOP("Asavari Theta"), { 0, 0, 2, 3, 3, 5, 5, 7, 8, 8,10,10 } }, // identical to "Melodic Minor (Descending)" { QT_TR_NOOP("Balinese"), { 0, 1, 1, 3, 3, 3, 3, 7, 8, 8, 8, 8 } }, // { QT_TR_NOOP("Bilaval Theta"), { 0, 0, 2, 2, 4, 5, 5, 7, 7, 9, 9,11 } }, // identical to "Major" // { QT_TR_NOOP("Bhairav Theta"), { 0, 1, 1, 1, 4, 5, 5, 7, 8, 8, 8,11 } }, // identical to "Hungarian Gypsy Persian" // { QT_TR_NOOP("Bhairavi Theta"), { 0, 1, 1, 3, 3, 5, 5, 7, 8, 8,10,10 } }, // identical to "Phrygian" // { QT_TR_NOOP("Byzantine"), { 0, 1, 1, 1, 4, 5, 5, 7, 8, 8, 8,11 } }, // identical to "Hungarian Gypsy Persian" { QT_TR_NOOP("Chinese"), { 0, 0, 0, 0, 4, 4, 6, 7, 7, 7, 7,11 } }, // { QT_TR_NOOP("Chinese Mongolian"), { 0, 0, 2, 2, 4, 4, 4, 7, 7, 9, 9, 9 } }, // identical to "Pentatonic Major" { QT_TR_NOOP("Diminished"), { 0, 0, 2, 3, 3, 5, 6, 6, 8, 9, 9,11 } }, // identical to "Octatonic (W-H)" // { QT_TR_NOOP("Egyptian"), { 0, 0, 2, 2, 2, 5, 5, 7, 7, 7,10,10 } }, // identical to "Pentatonic Neutral" // { QT_TR_NOOP("Ethiopian (A Raray)"), { 0, 0, 2, 2, 4, 5, 5, 7, 7, 9, 9,11 } }, // identical to "Major" // { QT_TR_NOOP("Ethiopian (Geez & Ezel)"),{ 0, 0, 2, 3, 3, 5, 5, 7, 8, 8,10,10 } }, // identical to "Melodic Minor (Descending)" // { QT_TR_NOOP("Hawaiian"), { 0, 0, 2, 3, 3, 5, 5, 7, 7, 9, 9,11 } }, // identical to "Melodic Minor (Ascending)" // { QT_TR_NOOP("Hindustan"), { 0, 0, 2, 2, 4, 5, 5, 7, 8, 8,10,10 } }, // identical to "Hindu" { QT_TR_NOOP("Japanese (Ichikosucho)"), { 0, 0, 2, 2, 4, 5, 6, 7, 7, 9, 9,11 } }, { QT_TR_NOOP("Japanese (Taishikicho)"), { 0, 0, 2, 2, 4, 5, 6, 7, 7, 9,10,11 } }, { QT_TR_NOOP("Javaneese"), { 0, 1, 1, 3, 3, 5, 5, 7, 7, 9,10,10 } }, // { QT_TR_NOOP("Kafi Theta"), { 0, 0, 2, 3, 3, 5, 5, 7, 7, 9,10,10 } }, // identical to "Dorian" // { QT_TR_NOOP("Kalyan Theta"), { 0, 0, 2, 2, 4, 4, 6, 7, 7, 9, 9,11 } }, // identical to "Lydian" // { QT_TR_NOOP("Khamaj Theta"), { 0, 0, 2, 2, 4, 5, 5, 7, 7, 9,10,10 } }, // identical to "Mixolydian" // { QT_TR_NOOP("Madelynian"), { 0, 1, 1, 3, 3, 5, 6, 6, 8, 8,10,10 } }, // identical to "Locrian" { QT_TR_NOOP("Marva Theta"), { 0, 1, 1, 1, 4, 4, 6, 7, 7, 9, 9,11 } }, #ifdef QTRACTOR_MELA_SCALES { QT_TR_NOOP("Mela Bhavapriya"), { 0, 1, 2, 2, 2, 5, 5, 7, 8, 9, 9, 9 } }, { QT_TR_NOOP("Mela Chakravakam"), { 0, 1, 1, 1, 4, 5, 5, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Mela Chalanata"), { 0, 0, 0, 3, 4, 5, 5, 7, 7, 7,10,11 } }, // { QT_TR_NOOP("Mela Charukesi"), { 0, 0, 2, 2, 4, 5, 5, 7, 8, 8,10,10 } }, // identical to "Hindu" { QT_TR_NOOP("Mela Chitrambari"), { 0, 0, 2, 2, 4, 4, 6, 7, 7, 7,10,11 } }, { QT_TR_NOOP("Mela Dharmavati"), { 0, 0, 2, 3, 3, 3, 6, 7, 7, 9, 9,11 } }, { QT_TR_NOOP("Mela Dhatuvardhani"), { 0, 0, 0, 3, 4, 4, 6, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Mela Dhavalambari"), { 0, 1, 1, 1, 4, 4, 6, 7, 8, 9, 9, 9 } }, // { QT_TR_NOOP("Mela Dhenuka"), { 0, 1, 1, 3, 3, 5, 5, 7, 8, 8, 8,11 } }, // identical to "Neapolitan" // { QT_TR_NOOP("Mela Dhirasankarabharana"),{0, 0, 2, 2, 4, 5, 5, 7, 7, 9, 9,11 } }, // identical to "Major" { QT_TR_NOOP("Mela Divyamani"), { 0, 1, 1, 1, 4, 4, 6, 7, 7, 7,10,11 } }, // { QT_TR_NOOP("Mela Gamanasrama"), { 0, 1, 1, 1, 4, 4, 6, 7, 7, 9, 9,11 } }, // identical to "Marva Theta" { QT_TR_NOOP("Mela Ganamurti"), { 0, 1, 2, 2, 2, 5, 5, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Mela Gangeyabhusani"), { 0, 0, 0, 3, 4, 5, 5, 7, 8, 8, 8,11 } }, // { QT_TR_NOOP("Mela Gaurimanohari"), { 0, 0, 2, 3, 3, 5, 5, 7, 7, 9, 9,11 } }, // identical to "Melodic Minor (Ascending)" { QT_TR_NOOP("Mela Gavambodhi"), { 0, 1, 1, 3, 3, 3, 6, 7, 8, 9, 9, 9 } }, { QT_TR_NOOP("Mela Gayakapriya"), { 0, 1, 1, 1, 4, 5, 5, 7, 8, 9, 9, 9 } }, // { QT_TR_NOOP("Mela Hanumattodi"), { 0, 1, 1, 3, 3, 5, 5, 7, 8, 8,10,10 } }, // identical to "Phrygian" // { QT_TR_NOOP("Mela Harikambhoji"), { 0, 0, 2, 2, 4, 5, 5, 7, 7, 9,10,10 } }, // identical to "Mixolydian" { QT_TR_NOOP("Mela Hatakambari"), { 0, 1, 1, 1, 4, 5, 5, 7, 7, 7,10,11 } }, // { QT_TR_NOOP("Mela Hemavati"), { 0, 0, 2, 3, 3, 3, 6, 7, 7, 9,10,10 } }, // identical to "Roumanian Minor" { QT_TR_NOOP("Mela Jalarnavam"), { 0, 1, 2, 2, 2, 2, 6, 7, 8, 8,10,10 } }, { QT_TR_NOOP("Mela Jhalavarali"), { 0, 1, 2, 2, 2, 2, 6, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Mela Jhankaradhvani"), { 0, 0, 2, 3, 3, 5, 5, 7, 8, 9, 9, 9 } }, { QT_TR_NOOP("Mela Jyotisvarupini"), { 0, 0, 0, 3, 4, 4, 6, 7, 8, 8,10,10 } }, { QT_TR_NOOP("Mela Kamavarardhani"), { 0, 1, 1, 1, 4, 4, 6, 7, 8, 8, 8,11 } }, // { QT_TR_NOOP("Mela Kanakangi"), { 0, 1, 2, 2, 2, 5, 5, 7, 8, 9, 9, 9 } }, // identical to "Mela Bhavapriya" { QT_TR_NOOP("Mela Kantamani"), { 0, 0, 2, 2, 4, 4, 6, 7, 8, 9, 9, 9 } }, // { QT_TR_NOOP("Mela Kharaharapriya"), { 0, 0, 2, 3, 3, 5, 5, 7, 7, 9,10,10 } }, // identical to "Dorian" // { QT_TR_NOOP("Mela Kiravani"), { 0, 0, 2, 3, 3, 5, 5, 7, 8, 8, 8,11 } }, // identical to "Harmonic Minor" // { QT_TR_NOOP("Mela Kokilapriya"), { 0, 1, 1, 3, 3, 5, 5, 7, 7, 9, 9,11 } }, // identical to "Neapolitan Major" { QT_TR_NOOP("Mela Kosalam"), { 0, 0, 0, 3, 4, 4, 6, 7, 7, 9, 9,11 } }, { QT_TR_NOOP("Mela Latangi"), { 0, 0, 2, 2, 4, 4, 6, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Mela Manavati"), { 0, 1, 2, 2, 2, 5, 5, 7, 7, 9, 9,11 } }, { QT_TR_NOOP("Mela Mararanjani"), { 0, 0, 2, 2, 4, 5, 5, 7, 8, 9, 9, 9 } }, // { QT_TR_NOOP("Mela Mayamalavagaula"), { 0, 1, 1, 1, 4, 5, 5, 7, 8, 8, 8,11 } }, // identical to "Hungarian Gypsy Persian" // { QT_TR_NOOP("Mela Mechakalyani"), { 0, 0, 2, 2, 4, 4, 6, 7, 7, 9, 9,11 } }, // identical to "Lydian" { QT_TR_NOOP("Mela Naganandini"), { 0, 0, 2, 2, 4, 5, 5, 7, 7, 7,10,11 } }, { QT_TR_NOOP("Mela Namanarayani"), { 0, 1, 1, 1, 4, 4, 6, 7, 8, 8,10,10 } }, // { QT_TR_NOOP("Mela Nasikabhusani"), { 0, 0, 0, 3, 4, 4, 6, 7, 7, 9,10,10 } }, // identical to "Hungarian Major" // { QT_TR_NOOP("Mela Natabhairavi"), { 0, 0, 2, 3, 3, 5, 5, 7, 8, 8,10,10 } }, // identical to "Melodic Minor (Descending)" // { QT_TR_NOOP("Mela Natakapriya"), { 0, 1, 1, 3, 3, 5, 5, 7, 7, 9,10,10 } }, // identical to "Javaneese" { QT_TR_NOOP("Mela Navanitam"), { 0, 1, 2, 2, 2, 2, 6, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Mela Nitimati"), { 0, 0, 2, 3, 3, 3, 6, 7, 7, 7,10,11 } }, { QT_TR_NOOP("Mela Pavani"), { 0, 1, 2, 2, 2, 2, 6, 7, 7, 9, 9,11 } }, { QT_TR_NOOP("Mela Ragavardhani"), { 0, 0, 0, 3, 4, 5, 5, 7, 8, 8,10,10 } }, { QT_TR_NOOP("Mela Raghupriya"), { 0, 1, 2, 2, 2, 2, 6, 7, 7, 7,10,11 } }, { QT_TR_NOOP("Mela Ramapriya"), { 0, 1, 1, 1, 4, 4, 6, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Mela Rasikapriya"), { 0, 0, 0, 3, 4, 4, 6, 7, 7, 7,10,11 } }, { QT_TR_NOOP("Mela Ratnangi"), { 0, 1, 2, 2, 2, 5, 5, 7, 8, 8,10,10 } }, { QT_TR_NOOP("Mela Risabhapriya"), { 0, 0, 2, 2, 4, 4, 6, 7, 8, 8,10,10 } }, { QT_TR_NOOP("Mela Rupavati"), { 0, 1, 1, 3, 3, 5, 5, 7, 7, 7,10,11 } }, { QT_TR_NOOP("Mela Sadvidhamargini"), { 0, 1, 1, 3, 3, 3, 6, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Mela Salagam"), { 0, 1, 2, 2, 2, 2, 6, 7, 8, 9, 9, 9 } }, { QT_TR_NOOP("Mela Sanmukhapriya"), { 0, 0, 2, 3, 3, 3, 6, 7, 8, 8,10,10 } }, { QT_TR_NOOP("Mela Sarasangi"), { 0, 0, 2, 2, 4, 5, 5, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Mela Senavati"), { 0, 1, 1, 3, 3, 5, 5, 7, 8, 9, 9, 9 } }, // { QT_TR_NOOP("Mela Simhendramadhyama"), { 0, 0, 2, 3, 3, 3, 6, 7, 8, 8, 8,11 } }, // identical to "Hungarian Minor" { QT_TR_NOOP("Mela Subhapantuvarali"), { 0, 1, 1, 3, 3, 3, 6, 7, 8, 8, 8,11 } }, { QT_TR_NOOP("Mela Sucharitra"), { 0, 0, 0, 3, 4, 4, 6, 7, 8, 9, 9, 9 } }, { QT_TR_NOOP("Mela Sulini"), { 0, 0, 0, 3, 4, 5, 5, 7, 7, 9, 9,11 } }, { QT_TR_NOOP("Mela Suryakantam"), { 0, 1, 1, 1, 4, 5, 5, 7, 7, 9, 9,11 } }, // { QT_TR_NOOP("Mela Suvarnangi"), { 0, 1, 2, 2, 2, 2, 6, 7, 7, 9, 9,11 } }, // identical to "Mela Pavani" { QT_TR_NOOP("Mela Syamalangi"), { 0, 0, 2, 3, 3, 3, 6, 7, 8, 9, 9, 9 } }, { QT_TR_NOOP("Mela Tanarupi"), { 0, 1, 2, 2, 2, 5, 5, 7, 7, 7,10,11 } }, // { QT_TR_NOOP("Mela Vaschaspati"), { 0, 0, 2, 2, 4, 4, 6, 7, 7, 9,10,10 } }, // identical to "Overtone" { QT_TR_NOOP("Mela Vagadhisvari"), { 0, 0, 0, 3, 4, 5, 5, 7, 7, 9,10,10 } }, // { QT_TR_NOOP("Mela Vakulabharanam"), { 0, 1, 1, 1, 4, 5, 5, 7, 8, 8,10,10 } }, // identical to "Jewish (Ahaba Rabba)" { QT_TR_NOOP("Mela Vanaspati"), { 0, 1, 2, 2, 2, 5, 5, 7, 7, 9,10,10 } }, { QT_TR_NOOP("Mela Varunapriya"), { 0, 0, 2, 3, 3, 5, 5, 7, 7, 7,10,11 } }, // { QT_TR_NOOP("Mela Visvambari"), { 0, 1, 1, 1, 4, 4, 6, 7, 7, 7,10,11 } }, // identical to "Mela Divyamani" { QT_TR_NOOP("Mela Yagapriya"), { 0, 0, 0, 3, 4, 5, 5, 7, 8, 9, 9, 9 } }, #endif // QTRACTOR_MELA_SCALES // { QT_TR_NOOP("Mohammedan"), { 0, 0, 2, 3, 3, 5, 5, 7, 8, 8, 8,11 } }, // identical to "Harmonic Minor" { QT_TR_NOOP("Persian"), { 0, 1, 1, 1, 4, 5, 6, 6, 8, 8, 8,11 } }, { QT_TR_NOOP("Purvi Theta"), { 0, 1, 1, 1, 4, 4, 6, 7, 8, 8, 8,11 } }, // identical to "Mela Kamavarardhani" { QT_TR_NOOP("Spanish Gypsy"), { 0, 1, 1, 1, 4, 5, 5, 7, 8, 8,10,10 } }, // identical to "Jewish (Ahaba Rabba)" { QT_TR_NOOP("Todi Theta"), { 0, 1, 1, 3, 3, 3, 6, 7, 8, 8, 8,11 } }, // identical to "Mela Subhapantuvarali" // { QT_TR_NOOP("Aux Diminished"), { 0, 0, 2, 3, 3, 5, 6, 6, 8, 9, 9,11 } }, // identical to "Octatonic (W-H)" // { QT_TR_NOOP("Aux Augmented"), { 0, 0, 2, 2, 4, 4, 6, 6, 8, 8,10,10 } }, // identical to "Whole Tone" // { QT_TR_NOOP("Aux Diminished Blues"), { 0, 1, 1, 3, 4, 4, 6, 7, 7, 9,10,10 } }, // identical to "Octatonic (H-W)" { QT_TR_NOOP("Enigmatic"), { 0, 1, 1, 1, 4, 4, 6, 6, 8, 8,10,11 } }, { QT_TR_NOOP("Kumoi"), { 0, 0, 2, 3, 3, 3, 3, 7, 7, 9, 9, 9 } }, { QT_TR_NOOP("Lydian Augmented"), { 0, 0, 2, 2, 4, 4, 6, 6, 8, 9, 9,11 } }, { QT_TR_NOOP("Pelog"), { 0, 1, 1, 3, 3, 3, 3, 7, 8, 8, 8, 8 } }, // identical to "Balinese" { QT_TR_NOOP("Prometheus"), { 0, 0, 2, 2, 4, 4, 6, 6, 6, 9,10,10 } }, { QT_TR_NOOP("Prometheus Neapolitan"), { 0, 1, 1, 1, 4, 4, 6, 6, 6, 9,10,10 } }, { QT_TR_NOOP("Six Tone Symmetrical"), { 0, 1, 1, 1, 4, 5, 5, 5, 8, 9, 9, 9 } }, { QT_TR_NOOP("Super Locrian"), { 0, 1, 1, 3, 4, 4, 6, 6, 8, 8,10,10 } }, { QT_TR_NOOP("Lydian Minor"), { 0, 0, 2, 2, 4, 4, 6, 7, 8, 8,10,10 } }, // identical to "Mela Risabhapriya" { QT_TR_NOOP("Lydian Diminished"), { 0, 0, 2, 3, 3, 3, 6, 7, 7, 9, 9,11 } }, // identical to "Mela Dharmavati" // { QT_TR_NOOP("Major Locrian"), { 0, 0, 2, 2, 4, 5, 6, 6, 8, 8,10,10 } }, // identical to "Arabian (B)" // { QT_TR_NOOP("Hindu"), { 0, 0, 2, 2, 4, 5, 5, 7, 8, 8,10,10 } }, // identical to "Hindu" // { QT_TR_NOOP("Diminished Whole Tone"), { 0, 1, 1, 3, 4, 4, 6, 6, 8, 8,10,10 } }, // identical to "Super Locrian" { QT_TR_NOOP("Half Diminished"), { 0, 0, 2, 3, 3, 5, 6, 6, 8, 8,10,10 } }, { QT_TR_NOOP("Bhairav"), { 0, 1, 1, 1, 4, 5, 5, 7, 8, 8, 8,11 } }, // identical to "Hungarian Gypsy Persian" { QT_TR_NOOP("Yaman"), { 0, 0, 2, 2, 4, 4, 6, 7, 7, 9, 9,11 } }, // identical to "Lydian" { QT_TR_NOOP("Todi"), { 0, 1, 1, 3, 3, 5, 5, 7, 8, 8,10,10 } }, // identical to "Phrygian" { QT_TR_NOOP("Jog"), { 0, 0, 0, 3, 4, 5, 5, 7, 7, 7,10,10 } }, { QT_TR_NOOP("Multani"), { 0, 1, 1, 3, 3, 3, 6, 7, 8, 8, 8,11 } }, // identical to "Mela Subhapantuvarali" { QT_TR_NOOP("Darbari"), { 0, 0, 2, 3, 3, 5, 5, 7, 8, 8,10,10 } }, // identical to "Melodic Minor (Descending)" { QT_TR_NOOP("Malkauns"), { 0, 0, 0, 3, 3, 5, 5, 5, 8, 8,10,10 } }, { QT_TR_NOOP("Bhoopali"), { 0, 0, 2, 2, 4, 4, 4, 7, 7, 9, 9, 9 } }, // identical to "Pentatonic Major" { QT_TR_NOOP("Shivaranjani"), { 0, 0, 2, 3, 3, 3, 3, 7, 7, 9, 9, 9 } }, // identical to "Kumoi" { QT_TR_NOOP("Marwa"), { 0, 1, 1, 1, 4, 4, 6, 6, 6, 9, 9,11 } }, // { QT_TR_NOOP("Blues"), { 0, 0, 0, 3, 3, 5, 6, 7, 7, 7,10,10 } }, // identical to "Pentatonic Blues" { QT_TR_NOOP("Minor 5"), { 0, 0, 0, 3, 3, 5, 5, 7, 7, 7,10,10 } }, // identical to "Pentatonic Minor" { QT_TR_NOOP("Major 5"), { 0, 0, 0, 0, 4, 5, 5, 7, 7, 7, 7,11 } }, { QT_TR_NOOP("5"), { 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7 } }, { QT_TR_NOOP("45"), { 0, 0, 0, 0, 0, 5, 5, 7, 7, 7, 7, 7 } }, { QT_TR_NOOP("457"), { 0, 0, 0, 0, 0, 5, 5, 7, 7, 7,10,10 } }, { QT_TR_NOOP("M 6"), { 0, 0, 2, 3, 3, 5, 5, 7, 7, 7,10,11 } }, // identical to "Mela Varunapriya" { nullptr, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } }; if (g_scaleTypes.isEmpty()) { for (int i = 0; s_aScaleTab[i].name; ++i) g_scaleTypes.append(tr(s_aScaleTab[i].name)); } return s_aScaleTab[iScale].note[n % 12]; } // Scale quantizer method. unsigned char qtractorMidiEditor::snapToScale ( unsigned char note, int iKey, int iScale ) { const int n = int(note) + (12 - iKey); return 12 * ((n / 12) - 1) + iKey + scaleTabNote(iScale, n); } //---------------------------------------------------------------------------- // qtractorMidiEdit::ClipBoard - MIDI editor clipaboard singleton. // Singleton declaration. qtractorMidiEditor::ClipBoard qtractorMidiEditor::g_clipboard; //---------------------------------------------------------------------------- // qtractorMidiEdit::DragTimeScale - Specialized drag/time-scale (draft)... struct qtractorMidiEditor::DragTimeScale { DragTimeScale(qtractorTimeScale *ts, unsigned long offset) : cursor(ts) { node = cursor.seekFrame(offset); t0 = node->tickFromFrame(offset); x0 = ts->pixelFromFrame(offset); } qtractorTimeScale::Cursor cursor; qtractorTimeScale::Node *node; unsigned long t0; int x0; }; //---------------------------------------------------------------------------- // qtractorMidiEditor -- The main MIDI sequence editor widget. // Constructor. qtractorMidiEditor::qtractorMidiEditor ( QWidget *pParent ) : QSplitter(Qt::Vertical, pParent) { // Initialize instance variables... m_pMidiClip = nullptr; // Event fore/background colors. m_foreground = Qt::darkBlue; m_background = Qt::blue; // Common drag state. m_dragState = DragNone; m_dragCursor = DragNone; m_resizeMode = ResizeNone; m_pEventDrag = nullptr; m_bEventDragEdit = false; m_pRubberBand = nullptr; // Zoom mode flag. m_iZoomMode = ZoomAll; // Drum mode (UI). m_bDrumMode = false; // Edit mode flags. m_bEditMode = false; m_bEditModeDraw = false; // Snap-to-beat/bar grid/zebra mode. m_bSnapZebra = false; m_bSnapGrid = false; // Floating tool-tips mode. m_bToolTips = true; // Last default editing values. m_last.note = 0x3c; // middle-C m_last.value = 0x40; m_last.pitchBend = 0; m_last.duration = 0; // Local time-scale. m_pTimeScale = new qtractorTimeScale(); // The local time-scale offset/length. m_iOffset = 0; m_iLength = 0; // Local step-input-head positioning. m_iStepInputHeadX = 0; // Local edit-head/tail positioning. m_iEditHeadX = 0; m_iEditTailX = 0; // Local play-head positioning. m_iPlayHeadX = 0; m_bSyncView = false; // Note autition while editing. m_bSendNotes = false; // Note names display (inside rectangles). m_bNoteNames = false; // Event (note) duration rectangle vs. stick. m_bNoteDuration = false; // Event (note, velocity) coloring. m_bNoteColor = false; m_bValueColor = false; // Which widget holds focus on drag-step/paste? m_pDragStep = nullptr; // Snap-to-scale (aka.in-place scale-quantize) stuff. m_iSnapToScaleKey = 0; m_iSnapToScaleType = 0; // Temporary sync-view/follow-playhead hold state. m_bSyncViewHold = false; m_iSyncViewHold = 0; // Current ghost-track option. m_pGhostTrack = nullptr; // Default minimum event width. m_iMinEventWidth = QTRACTOR_MIN_EVENT_WIDTH; // The main widget splitters. m_pHSplitter = new QSplitter(Qt::Horizontal, this); m_pHSplitter->setObjectName("qtractorMidiEditor::HSplitter"); m_pVSplitter = this; m_pVSplitter->setObjectName("qtractorMidiEditor::VSplitter"); // Create child frame widgets... QWidget *pVBoxLeft = new QWidget(m_pHSplitter); QWidget *pVBoxRight = new QWidget(m_pHSplitter); QWidget *pHBoxBottom = new QWidget(m_pVSplitter); // Create child view widgets... m_pEditListHeader = new QFrame(pVBoxLeft); m_pEditListHeader->setFixedHeight(20); m_pEditList = new qtractorMidiEditList(this, pVBoxLeft); m_pEditList->setMinimumWidth(32); m_pEditTime = new qtractorMidiEditTime(this, pVBoxRight); m_pEditTime->setFixedHeight(20); m_pEditView = new qtractorMidiEditView(this, pVBoxRight); m_pEditEventScale = new qtractorMidiEditEventScale(this, pHBoxBottom); m_pEditEvent = new qtractorMidiEditEvent(this, pHBoxBottom); m_pEditEventFrame = new QFrame(pHBoxBottom); m_pEditList->updateContentsHeight(); m_pThumbView = new qtractorMidiThumbView(this); // Create child box layouts... QVBoxLayout *pVBoxLeftLayout = new QVBoxLayout(pVBoxLeft); pVBoxLeftLayout->setContentsMargins(0, 0, 0, 0); pVBoxLeftLayout->setSpacing(0); pVBoxLeftLayout->addWidget(m_pEditListHeader); pVBoxLeftLayout->addWidget(m_pEditList); pVBoxLeft->setLayout(pVBoxLeftLayout); QVBoxLayout *pVBoxRightLayout = new QVBoxLayout(pVBoxRight); pVBoxRightLayout->setContentsMargins(0, 0, 0, 0); pVBoxRightLayout->setSpacing(0); pVBoxRightLayout->addWidget(m_pEditTime); pVBoxRightLayout->addWidget(m_pEditView); pVBoxRight->setLayout(pVBoxRightLayout); QHBoxLayout *pHBoxBottomLayout = new QHBoxLayout(pHBoxBottom); pHBoxBottomLayout->setContentsMargins(0, 0, 0, 0); pHBoxBottomLayout->setSpacing(0); pHBoxBottomLayout->addWidget(m_pEditEventScale); pHBoxBottomLayout->addWidget(m_pEditEvent); pHBoxBottomLayout->addWidget(m_pEditEventFrame); pHBoxBottom->setLayout(pHBoxBottomLayout); // m_pHSplitter->setOpaqueResize(false); m_pHSplitter->setStretchFactor(m_pHSplitter->indexOf(pVBoxLeft), 0); m_pHSplitter->setHandleWidth(2); // m_pVSplitter->setOpaqueResize(false); m_pVSplitter->setStretchFactor(m_pVSplitter->indexOf(pHBoxBottom), 0); m_pVSplitter->setHandleWidth(2); m_pVSplitter->setWindowIcon(QIcon::fromTheme("qtractorMidiEditor")); m_pVSplitter->setWindowTitle(tr("MIDI Editor")); // To have all views in positional sync. QObject::connect(m_pEditList, SIGNAL(contentsMoving(int,int)), m_pEditView, SLOT(contentsYMovingSlot(int,int))); QObject::connect(m_pEditView, SIGNAL(contentsMoving(int,int)), m_pEditTime, SLOT(contentsXMovingSlot(int,int))); QObject::connect(m_pEditView, SIGNAL(contentsMoving(int,int)), m_pEditList, SLOT(contentsYMovingSlot(int,int))); QObject::connect(m_pEditView, SIGNAL(contentsMoving(int,int)), m_pEditEvent, SLOT(contentsXMovingSlot(int,int))); QObject::connect(m_pEditEvent, SIGNAL(contentsMoving(int,int)), m_pEditTime, SLOT(contentsXMovingSlot(int,int))); QObject::connect(m_pEditEvent, SIGNAL(contentsMoving(int,int)), m_pEditView, SLOT(contentsXMovingSlot(int,int))); QObject::connect(m_pEditView, SIGNAL(contentsMoving(int,int)), m_pThumbView, SLOT(updateThumb())); // Initial splitter sizes. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { QList sizes; // Initial horizontal splitter sizes... sizes.append(48); sizes.append(752); pOptions->loadSplitterSizes(m_pHSplitter, sizes); sizes.clear(); // Initial vertical splitter sizes... sizes.append(420); sizes.append(180); pOptions->loadSplitterSizes(m_pVSplitter, sizes); } // Track splitter moves... QObject::connect(m_pHSplitter, SIGNAL(splitterMoved(int, int)), SLOT(horizontalSplitterSlot())); QObject::connect(m_pVSplitter, SIGNAL(splitterMoved(int, int)), SLOT(verticalSplitterSlot())); } // Destructor. qtractorMidiEditor::~qtractorMidiEditor (void) { resetDragState(nullptr); // Save splitter sizes... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { pOptions->saveSplitterSizes(m_pHSplitter); pOptions->saveSplitterSizes(m_pVSplitter); } // Release local instances. delete m_pTimeScale; } // Editing sequence accessor. void qtractorMidiEditor::setMidiClip ( qtractorMidiClip *pMidiClip ) { // So, this is the brand new object to edit... m_pMidiClip = pMidiClip; // Reset ghost-track anyway... m_pGhostTrack = nullptr; if (m_pMidiClip) { // Now set the editing MIDI sequence alright... setOffset(m_pMidiClip->clipStart()); setLength(m_pMidiClip->clipLength()); // Set its most outstanding properties... qtractorTrack *pTrack = m_pMidiClip->track(); if (pTrack) { setForeground(pTrack->foreground()); setBackground(pTrack->background()); const int iEditorDrumMode = m_pMidiClip->editorDrumMode(); if (iEditorDrumMode < 0) setDrumMode(pTrack->isMidiDrums()); else setDrumMode(iEditorDrumMode > 0); } // And the last but not least... qtractorMidiSequence *pSeq = m_pMidiClip->sequence(); if (pSeq) { // Reset some internal state... m_cursor.reset(pSeq); m_cursorAt.reset(pSeq); // Reset as last on middle note and snap duration... m_last.note = (pSeq->noteMin() + pSeq->noteMax()) >> 1; if (m_last.note == 0) m_last.note = 0x3c; // Default to middle-C. } // Set ghost-track by name... const QString& sGhostTrackName = m_pMidiClip->ghostTrackName(); qtractorSession *pSession = pTrack->session(); if (pSession && !sGhostTrackName.isEmpty()) { for (pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() == qtractorTrack::Midi && (pTrack->trackName() == sGhostTrackName || pTrack->shortTrackName() == sGhostTrackName)) { m_pGhostTrack = pTrack; break; } } } // Set zoom ratios... const unsigned short iHorizontalZoom = pMidiClip->editorHorizontalZoom(); if (iHorizontalZoom != 100) setHorizontalZoom(iHorizontalZoom); const unsigned short iVerticalZoom = pMidiClip->editorVerticalZoom(); if (iVerticalZoom != 100) setVerticalZoom(iVerticalZoom); // Set splitter sizes... const QList& hsizes = pMidiClip->editorHorizontalSizes(); if (!hsizes.isEmpty()) setHorizontalSizes(hsizes); const QList& vsizes = pMidiClip->editorVerticalSizes(); if (!vsizes.isEmpty()) setVerticalSizes(vsizes); // Connect to clip's command (undo/redo) sinal-slot stack... qtractorCommandList *pCommands = pMidiClip->commands(); if (pCommands) { QObject::connect(pCommands, SIGNAL(updateNotifySignal(unsigned int)), SLOT(updateNotifySlot(unsigned int))); } // Got clip! } else { // Reset those little things too.. setDrumMode(false); setOffset(0); setLength(0); } } qtractorMidiClip *qtractorMidiEditor::midiClip (void) const { return m_pMidiClip; } // MIDI clip property accessors. const QString& qtractorMidiEditor::filename (void) const { return m_pMidiClip->filename(); } unsigned short qtractorMidiEditor::trackChannel (void) const { return (m_pMidiClip ? m_pMidiClip->trackChannel() : 0); } unsigned short qtractorMidiEditor::format (void) const { return (m_pMidiClip ? m_pMidiClip->format() : 0); } qtractorMidiSequence *qtractorMidiEditor::sequence (void) const { return (m_pMidiClip ? m_pMidiClip->sequence() : nullptr); } // Event foreground (outline) color. void qtractorMidiEditor::setForeground ( const QColor& fore ) { m_foreground = fore; } const QColor& qtractorMidiEditor::foreground (void) const { return m_foreground; } // Event background (fill) color. void qtractorMidiEditor::setBackground ( const QColor& back ) { m_background = back; } const QColor& qtractorMidiEditor::background (void) const { return m_background; } // Zoom (view) mode. void qtractorMidiEditor::setZoomMode ( int iZoomMode ) { m_iZoomMode = iZoomMode; } int qtractorMidiEditor::zoomMode (void) const { return m_iZoomMode; } // Zoom ratio accessors. void qtractorMidiEditor::setHorizontalZoom ( unsigned short iHorizontalZoom ) { m_pTimeScale->setHorizontalZoom(iHorizontalZoom); m_pTimeScale->updateScale(); if (m_pMidiClip) m_pMidiClip->setEditorHorizontalZoom(iHorizontalZoom); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) // HACK: Adjust minimum event width,according // to current screen size and horizontal zoom... m_iMinEventWidth = QTRACTOR_MIN_EVENT_WIDTH; QScreen *pScreen = QSplitter::screen(); if (pScreen) { const int ws = pScreen->size().width(); m_iMinEventWidth += (((ws * iHorizontalZoom) / 360000) & 0x7e); } #endif } unsigned short qtractorMidiEditor::horizontalZoom (void) const { return m_pTimeScale->horizontalZoom(); } void qtractorMidiEditor::setVerticalZoom ( unsigned short iVerticalZoom ) { // Hold and try setting new item height... const int iZoomStep = int(iVerticalZoom) - int(verticalZoom()); int iItemHeight = (iVerticalZoom * qtractorMidiEditList::ItemHeightBase) / 100; if (iItemHeight < qtractorMidiEditList::ItemHeightMax && iZoomStep > 0) ++iItemHeight; else if (iItemHeight > qtractorMidiEditList::ItemHeightMin && iZoomStep < 0) --iItemHeight; m_pEditList->setItemHeight(iItemHeight); m_pTimeScale->setVerticalZoom(iVerticalZoom); if (m_pMidiClip) m_pMidiClip->setEditorVerticalZoom(iVerticalZoom); } unsigned short qtractorMidiEditor::verticalZoom (void) const { return m_pTimeScale->verticalZoom(); } // Splitter sizes accessors. void qtractorMidiEditor::setHorizontalSizes ( const QList& sizes ) { const bool bBlockSignals = m_pHSplitter->blockSignals(true); m_pHSplitter->setSizes(sizes); m_pHSplitter->blockSignals(bBlockSignals); } QList qtractorMidiEditor::horizontalSizes (void) const { return m_pHSplitter->sizes(); } void qtractorMidiEditor::setVerticalSizes ( const QList& sizes ) { const bool bBlockSignals = m_pVSplitter->blockSignals(true); m_pVSplitter->setSizes(sizes); m_pVSplitter->blockSignals(bBlockSignals); } QList qtractorMidiEditor::verticalSizes (void) const { return m_pVSplitter->sizes(); } // Drum mode (UI). void qtractorMidiEditor::setDrumMode ( bool bDrumMode ) { m_bDrumMode = bDrumMode; updateInstrumentNames(); // updateContents(); } bool qtractorMidiEditor::isDrumMode (void) const { return m_bDrumMode; } // Edit (creational) mode. void qtractorMidiEditor::setEditMode ( bool bEditMode ) { m_bEditMode = bEditMode; resetDragState(nullptr); unsetEditCursor(); // updateContents(); } bool qtractorMidiEditor::isEditMode (void) const { return m_bEditMode; } // Edit draw (notes) mode. void qtractorMidiEditor::setEditModeDraw ( bool bEditModeDraw ) { m_bEditModeDraw = bEditModeDraw; unsetEditCursor(); } bool qtractorMidiEditor::isEditModeDraw (void) const { return m_bEditModeDraw; } // Snap-to-bar zebra mode. void qtractorMidiEditor::setSnapZebra ( bool bSnapZebra ) { m_bSnapZebra = bSnapZebra; // updateContents(); } bool qtractorMidiEditor::isSnapZebra (void) const { return m_bSnapZebra; } // Snap-to-beat grid mode. void qtractorMidiEditor::setSnapGrid ( bool bSnapGrid ) { m_bSnapGrid = bSnapGrid; // updateContents(); } bool qtractorMidiEditor::isSnapGrid (void) const { return m_bSnapGrid; } // Floating tool-tips mode. void qtractorMidiEditor::setToolTips ( bool bToolTips ) { m_bToolTips = bToolTips; } bool qtractorMidiEditor::isToolTips (void) const { return m_bToolTips; } // Local time scale accessor. qtractorTimeScale *qtractorMidiEditor::timeScale (void) const { return m_pTimeScale; } unsigned long qtractorMidiEditor::timeOffset (void) const { return (m_pTimeScale ? m_pTimeScale->tickFromFrame(m_iOffset) : 0); } // Time-scale offset (in frames) accessors. void qtractorMidiEditor::setOffset ( unsigned long iOffset ) { m_iOffset = iOffset; } unsigned long qtractorMidiEditor::offset (void) const { return m_iOffset; } // Time-scale length (in frames) accessors. void qtractorMidiEditor::setLength ( unsigned long iLength ) { m_iLength = iLength; } unsigned long qtractorMidiEditor::length (void) const { return m_iLength; } // Ghost track accessors. void qtractorMidiEditor::setGhostTrack ( qtractorTrack *pGhostTrack ) { m_pGhostTrack = pGhostTrack; if (m_pMidiClip) { QString sGhostTrackName; if (m_pGhostTrack) sGhostTrackName = m_pGhostTrack->shortTrackName(); m_pMidiClip->setGhostTrackName(sGhostTrackName); } } qtractorTrack *qtractorMidiEditor::ghostTrack (void) const { return m_pGhostTrack; } // Minimum event width accessors. int qtractorMidiEditor::minEventWidth (void) const { return m_iMinEventWidth; } // Clip recording/overdub status. bool qtractorMidiEditor::isClipRecordEx (void) const { qtractorTrack *pTrack = (m_pMidiClip ? m_pMidiClip->track() : nullptr); return (pTrack && pTrack->isClipRecordEx() && pTrack->clipRecord() == m_pMidiClip); } // Check whether step-input is on. bool qtractorMidiEditor::isStepInputHead (void) const { if (m_pMidiClip == nullptr) return false; qtractorTrack *pTrack = m_pMidiClip->track(); if (pTrack == nullptr) return false; if (!pTrack->isClipRecordEx()) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; return !pSession->isPlaying(); } // Step-input positioning. void qtractorMidiEditor::setStepInputHead ( unsigned long iStepInputHead, bool bSyncView ) { if (bSyncView) bSyncView = m_bSyncView; const int iStepInputHeadX = m_pTimeScale->pixelFromFrame(iStepInputHead) - m_pTimeScale->pixelFromFrame(m_iOffset); setSyncViewHoldOn(false); drawPositionX(m_iStepInputHeadX, iStepInputHeadX, bSyncView); } int qtractorMidiEditor::stepInputHeadX (void) const { return m_iStepInputHeadX; } // Edit-head/tail positioning. void qtractorMidiEditor::setEditHead ( unsigned long iEditHead, bool bSyncView ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; if (iEditHead > pSession->editTail()) setEditTail(iEditHead, bSyncView); else setSyncViewHoldOn(true); if (bSyncView) pSession->setEditHead(iEditHead); const int iEditHeadX = m_pTimeScale->pixelFromFrame(iEditHead) - m_pTimeScale->pixelFromFrame(m_iOffset); drawPositionX(m_iEditHeadX, iEditHeadX, bSyncView); } int qtractorMidiEditor::editHeadX (void) const { return m_iEditHeadX; } void qtractorMidiEditor::setEditTail ( unsigned long iEditTail, bool bSyncView ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; if (iEditTail < pSession->editHead()) setEditHead(iEditTail, bSyncView); else setSyncViewHoldOn(true); if (bSyncView) pSession->setEditTail(iEditTail); const int iEditTailX = m_pTimeScale->pixelFromFrame(iEditTail) - m_pTimeScale->pixelFromFrame(m_iOffset); drawPositionX(m_iEditTailX, iEditTailX, bSyncView); } int qtractorMidiEditor::editTailX (void) const { return m_iEditTailX; } // Play-head positioning. void qtractorMidiEditor::setPlayHead ( unsigned long iPlayHead, bool bSyncView ) { if (bSyncView) bSyncView = m_bSyncView; const int iPlayHeadX = m_pTimeScale->pixelFromFrame(iPlayHead) - m_pTimeScale->pixelFromFrame(m_iOffset); drawPositionX(m_iPlayHeadX, iPlayHeadX, bSyncView); } int qtractorMidiEditor::playHeadX (void) const { return m_iPlayHeadX; } // Update time-scale to master session. void qtractorMidiEditor::updateTimeScale (void) { if (m_pMidiClip == nullptr) return; if (m_pTimeScale == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; m_pTimeScale->sync(*pSession->timeScale()); setOffset(m_pMidiClip->clipStart()); setLength(m_pMidiClip->clipLength()); setPlayHead(pSession->playHead(), false); setEditHead(pSession->editHead(), false); setEditTail(pSession->editTail(), false); } // Play-head follow-ness. void qtractorMidiEditor::setSyncView ( bool bSyncView ) { m_bSyncView = bSyncView; m_iSyncViewHold = 0; } bool qtractorMidiEditor::isSyncView (void) const { return m_bSyncView; } // Note autition while editing. void qtractorMidiEditor::setSendNotes ( bool bSendNotes ) { m_bSendNotes = bSendNotes; } bool qtractorMidiEditor::isSendNotes (void) const { return m_bSendNotes; } bool qtractorMidiEditor::isSendNotesEx (void) const { if (m_pMidiClip == nullptr) return false; qtractorTrack *pTrack = m_pMidiClip->track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; if (pSession->isPlaying()) return false; return isSendNotes(); } // Note names display (inside rectangles). void qtractorMidiEditor::setNoteNames ( bool bNoteNames ) { m_bNoteNames = bNoteNames; } bool qtractorMidiEditor::isNoteNames (void) const { return m_bNoteNames; } // Event value stick vs. duration rectangle. void qtractorMidiEditor::setNoteDuration ( bool bNoteDuration ) { m_bNoteDuration = bNoteDuration; } bool qtractorMidiEditor::isNoteDuration (void) const { return m_bNoteDuration; } // Event (note, velocity) coloring. void qtractorMidiEditor::setNoteColor ( bool bNoteColor ) { m_bNoteColor = bNoteColor; } bool qtractorMidiEditor::isNoteColor (void) const { return m_bNoteColor; } void qtractorMidiEditor::setValueColor ( bool bValueColor ) { m_bValueColor = bValueColor; } bool qtractorMidiEditor::isValueColor (void) const { return m_bValueColor; } // Snap-to-scale/quantize key accessor. void qtractorMidiEditor::setSnapToScaleKey ( int iSnapToScaleKey ) { m_iSnapToScaleKey = iSnapToScaleKey; } int qtractorMidiEditor::snapToScaleKey (void) const { return m_iSnapToScaleKey; } // Snap-to-scale/quantize type accessor. void qtractorMidiEditor::setSnapToScaleType ( int iSnapToScaleType ) { m_iSnapToScaleType = iSnapToScaleType; } int qtractorMidiEditor::snapToScaleType (void) const { return m_iSnapToScaleType; } // Vertical line position drawing. void qtractorMidiEditor::drawPositionX ( int& iPositionX, int x, bool bSyncView ) { // Update track-view position... const int x0 = m_pEditView->contentsX(); const int w = m_pEditView->width(); const int h = m_pEditView->height(); const int h2 = m_pEditEvent->height(); const int wm = (w >> 3); // Time-line header extents... const int h0 = m_pEditTime->height(); const int d0 = (h0 >> 1); // Restore old position... int x1 = iPositionX - x0; if (iPositionX != x && x1 >= 0 && x1 < w + d0) { // Override old view line... (m_pEditEvent->viewport())->update(QRect(x1, 0, 1, h2)); (m_pEditView->viewport())->update(QRect(x1, 0, 1, h)); (m_pEditTime->viewport())->update(QRect(x1 - d0, d0, h0, d0)); } // New position is in... iPositionX = x; // Force position to be in view? if (bSyncView && (x < x0 || x > x0 + w - wm) && m_dragState == DragNone && m_dragCursor == DragNone // && QApplication::mouseButtons() == Qt::NoButton && --m_iSyncViewHold < 0) { // Move it... m_pEditView->setContentsPos(x - wm, m_pEditView->contentsY()); m_iSyncViewHold = 0; } else { // Draw the line, by updating the new region... x1 = x - x0; if (x1 >= 0 && x1 < w + d0) { (m_pEditEvent->viewport())->update(QRect(x1, 0, 1, h2)); (m_pEditView->viewport())->update(QRect(x1, 0, 1, h)); (m_pEditTime->viewport())->update(QRect(x1 - d0, d0, h0, d0)); } } } // Child widgets accessors. QFrame *qtractorMidiEditor::editListHeader (void) const { return m_pEditListHeader; } qtractorMidiEditList *qtractorMidiEditor::editList (void) const { return m_pEditList; } qtractorMidiEditTime *qtractorMidiEditor::editTime (void) const { return m_pEditTime; } qtractorMidiEditView *qtractorMidiEditor::editView (void) const { return m_pEditView; } qtractorMidiEditEvent *qtractorMidiEditor::editEvent (void) const { return m_pEditEvent; } qtractorMidiEditEventScale *qtractorMidiEditor::editEventScale (void) const { return m_pEditEventScale; } QFrame *qtractorMidiEditor::editEventFrame (void) const { return m_pEditEventFrame; } qtractorMidiThumbView *qtractorMidiEditor::thumbView (void) const { return m_pThumbView; } // Horizontal zoom factor. void qtractorMidiEditor::horizontalZoomStep ( int iZoomStep ) { int iHorizontalZoom = horizontalZoom() + iZoomStep; if (iHorizontalZoom < ZoomMin) iHorizontalZoom = ZoomMin; else if (iHorizontalZoom > ZoomMax) iHorizontalZoom = ZoomMax; if (iHorizontalZoom == horizontalZoom()) return; // Fix the local horizontal view zoom. setHorizontalZoom(iHorizontalZoom); } // Vertical zoom factor. void qtractorMidiEditor::verticalZoomStep ( int iZoomStep ) { int iVerticalZoom = verticalZoom() + iZoomStep; if (iVerticalZoom < ZoomMin) iVerticalZoom = ZoomMin; else if (iVerticalZoom > ZoomMax) iVerticalZoom = ZoomMax; if (iVerticalZoom == verticalZoom()) return; // Fix the local vertical view zoom. setVerticalZoom(iVerticalZoom); } // Zoom view slots. void qtractorMidiEditor::zoomIn (void) { ZoomCenter zc; zoomCenterPre(zc); if (m_iZoomMode & ZoomHorizontal) horizontalZoomStep(+ ZoomStep); if (m_iZoomMode & ZoomVertical) verticalZoomStep(+ ZoomStep); zoomCenterPost(zc); } void qtractorMidiEditor::zoomOut (void) { ZoomCenter zc; zoomCenterPre(zc); if (m_iZoomMode & ZoomHorizontal) horizontalZoomStep(- ZoomStep); if (m_iZoomMode & ZoomVertical) verticalZoomStep(- ZoomStep); zoomCenterPost(zc); } void qtractorMidiEditor::zoomReset (void) { ZoomCenter zc; zoomCenterPre(zc); if (m_iZoomMode & ZoomHorizontal) horizontalZoomStep(ZoomBase - m_pTimeScale->horizontalZoom()); if (m_iZoomMode & ZoomVertical) verticalZoomStep(ZoomBase - m_pTimeScale->verticalZoom()); zoomCenterPost(zc); } // Zoom step evaluator. int qtractorMidiEditor::zoomStep (void) const { const Qt::KeyboardModifiers& modifiers = QApplication::keyboardModifiers(); if (modifiers & Qt::ControlModifier) return ZoomMax; if (modifiers & Qt::ShiftModifier) return ZoomBase >> 1; return ZoomStep; } void qtractorMidiEditor::horizontalZoomInSlot (void) { ZoomCenter zc; zoomCenterPre(zc); horizontalZoomStep(+ zoomStep()); zoomCenterPost(zc); } void qtractorMidiEditor::horizontalZoomOutSlot (void) { ZoomCenter zc; zoomCenterPre(zc); horizontalZoomStep(- zoomStep()); zoomCenterPost(zc); } void qtractorMidiEditor::verticalZoomInSlot (void) { ZoomCenter zc; zoomCenterPre(zc); verticalZoomStep(+ zoomStep()); zoomCenterPost(zc); } void qtractorMidiEditor::verticalZoomOutSlot (void) { ZoomCenter zc; zoomCenterPre(zc); verticalZoomStep(- zoomStep()); zoomCenterPost(zc); } void qtractorMidiEditor::horizontalZoomResetSlot (void) { ZoomCenter zc; zoomCenterPre(zc); horizontalZoomStep(ZoomBase - m_pTimeScale->horizontalZoom()); zoomCenterPost(zc); } void qtractorMidiEditor::verticalZoomResetSlot (void) { ZoomCenter zc; zoomCenterPre(zc); verticalZoomStep(ZoomBase - m_pTimeScale->verticalZoom()); zoomCenterPost(zc); } // Splitters moved slots. void qtractorMidiEditor::horizontalSplitterSlot (void) { if (m_pMidiClip) m_pMidiClip->setEditorHorizontalSizes(m_pHSplitter->sizes()); } void qtractorMidiEditor::verticalSplitterSlot (void) { if (m_pMidiClip) m_pMidiClip->setEditorVerticalSizes(m_pVSplitter->sizes()); } // Tell whether we can undo last command... bool qtractorMidiEditor::canUndo (void) const { qtractorCommandList *pCommands = commands(); return (pCommands ? pCommands->lastCommand() != nullptr : false); } // Tell whether we can redo last command... bool qtractorMidiEditor::canRedo (void) const { qtractorCommandList *pCommands = commands(); return (pCommands ? pCommands->nextCommand() != nullptr : false); } // Undo last edit command. void qtractorMidiEditor::undoCommand (void) { qtractorCommandList *pCommands = commands(); if (pCommands) pCommands->undo(); } // Redo last edit command. void qtractorMidiEditor::redoCommand (void) { qtractorCommandList *pCommands = commands(); if (pCommands) pCommands->redo(); } // Whether there's any item currently selected. bool qtractorMidiEditor::isSelected (void) const { return (m_select.items().count() > 0); } // Whether there's any item on the clipboard. bool qtractorMidiEditor::isClipboard (void) { // Tell whether there's any item on the clipboard. return (g_clipboard.items.count() > 0); } // Cut current selection to clipboard. void qtractorMidiEditor::cutClipboard (void) { if (m_pMidiClip == nullptr) return; if (!isSelected()) return; g_clipboard.clear(); qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(m_pMidiClip, tr("cut")); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); g_clipboard.items.append(new qtractorMidiEvent(*pEvent)); pEditCommand->removeEvent(pEvent); } // Make it as an undoable command... execute(pEditCommand); } // Copy current selection to clipboard. void qtractorMidiEditor::copyClipboard (void) { if (m_pMidiClip == nullptr) return; if (!isSelected()) return; g_clipboard.clear(); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) g_clipboard.items.append(new qtractorMidiEvent(*iter.key())); selectionChangeNotify(); } // Retrieve current paste period. // (as from current clipboard width) unsigned long qtractorMidiEditor::pastePeriod (void) const { const unsigned long t0 = m_pTimeScale->tickFromFrame(m_iOffset); unsigned long t1 = 0; unsigned long t2 = 0; int k = 0; QListIterator iter(g_clipboard.items); while (iter.hasNext()) { qtractorMidiEvent *pEvent = iter.next(); unsigned long t = t0 + pEvent->time(); if (t1 > t || k == 0) t1 = t; t += pEvent->duration(); if (t2 < t) t2 = t; ++k; } return m_pTimeScale->frameFromTick(t2) - m_pTimeScale->frameFromTick(t1); } // Paste from clipboard. void qtractorMidiEditor::pasteClipboard ( unsigned short iPasteCount, unsigned long iPastePeriod ) { if (m_pMidiClip == nullptr) return; if (!isClipboard()) return; // Reset any current selection, whatsoever... clearSelect(); resetDragState(nullptr); // Multi-paste period... if (iPastePeriod < 1) iPastePeriod = pastePeriod(); qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iOffset); unsigned long t0 = pNode->tickFromFrame(m_iOffset); const int x0 = m_pTimeScale->pixelFromFrame(m_iOffset); const int dx = m_pTimeScale->pixelFromFrame(iPastePeriod); // This is the edit-view spacifics... const int ch = m_pEditView->contentsHeight(); // + 1; const int h1 = m_pEditList->itemHeight(); const int h2 = (h1 >> 1); const int h4 = (h1 << 1); // This is the edit-event zero-line... const qtractorMidiEvent::EventType eventType = m_pEditEvent->eventType(); const int h0 = ((m_pEditEvent->viewport())->height() & ~1); // even. const int y0 = (eventType == qtractorMidiEvent::PITCHBEND ? h0 >> 1 : h0); int k, x1; const unsigned long d0 = t0; QListIterator iter(g_clipboard.items); for (unsigned short i = 0; i < iPasteCount; ++i) { iter.toFront(); k = x1 = 0; while (iter.hasNext()) { qtractorMidiEvent *pEvent = iter.next(); // Common event coords... int y; unsigned long t1 = t0 + pEvent->time(); unsigned long t2 = t1 + pEvent->duration(); pNode = cursor.seekTick(t1); int x = pNode->pixelFromTick(t1); pNode = cursor.seekTick(t2); int w1 = pNode->pixelFromTick(t2) - x; if (w1 < m_iMinEventWidth) w1 = m_iMinEventWidth; // View item... QRect rectView; if (pEvent->type() == m_pEditView->eventType()) { y = ch - h1 * (pEvent->note() + 1); if (m_bDrumMode) rectView.setRect(x - x0 - h1, y - h2, h4, h4); else rectView.setRect(x - x0, y, w1, h1); } // Event item... QRect rectEvent; const qtractorMidiEvent::EventType etype = pEvent->type(); if (etype == m_pEditEvent->eventType()) { if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) y = y0 - (y0 * pEvent->value()) / 16384; else if (etype == qtractorMidiEvent::PITCHBEND) y = y0 - (y0 * pEvent->pitchBend()) / 8192; else if (etype == qtractorMidiEvent::PGMCHANGE) y = y0 - (y0 * pEvent->param()) / 128; else y = y0 - (y0 * pEvent->value()) / 128; if (!m_bNoteDuration || m_bDrumMode) w1 = m_iMinEventWidth; if (y < y0) rectEvent.setRect(x - x0, y, w1, y0 - y); else if (y > y0) rectEvent.setRect(x - x0, y0, w1, y - y0); else rectEvent.setRect(x - x0, y0 - 2, w1, 4); } m_select.addItem(pEvent, rectEvent, rectView, t0 - d0); if (x1 > x || k == 0) x1 = x; ++k; } pNode = cursor.seekTick(x1 + dx); t0 += pNode->tickFromPixel(x1 + dx); pNode = cursor.seekTick(x1); t0 -= pNode->tickFromPixel(x1); } // Stabilize new floating selection... m_select.update(false); // Make sure we've a anchor... if (m_pEventDrag == nullptr) m_pEventDrag = m_select.anchorEvent(); // Formally ellect this one as the target view... qtractorScrollView *pScrollView = nullptr; qtractorMidiEditSelect::Item *pItem = m_select.findItem(m_pEventDrag); if (pItem) { if (m_pEventDrag->type() == m_pEditView->eventType()) { m_rectDrag = pItem->rectView; pScrollView = m_pEditView; } else { m_rectDrag = pItem->rectEvent; pScrollView = m_pEditEvent; } } // That's right :) if (pScrollView == nullptr) { m_dragState = DragStep; // HACK: Force selection clearance! clearSelect(); resetDragState(nullptr); return; } // We'll start a brand new floating state... m_dragState = m_dragCursor = DragPaste; m_posDrag = m_rectDrag.topLeft(); m_posStep = QPoint(0, 0); // This is the one which is holding focus on drag-step/paste... m_pDragStep = pScrollView; // It doesn't matter which one, both pasteable views are due... setEditCursor(QCursor(QIcon::fromTheme("editPaste").pixmap(22), 12, 12)); // Make sure the mouse pointer is properly located... const QPoint& pos = pScrollView->viewportToContents( pScrollView->viewport()->mapFromGlobal(QCursor::pos())); // Let's-a go... updateDragMove(pScrollView, pos + m_posStep); } // Execute event removal. void qtractorMidiEditor::deleteSelect (void) { if (m_pMidiClip == nullptr) return; if (!isSelected()) return; qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(m_pMidiClip, tr("delete")); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) pEditCommand->removeEvent(iter.key()); execute(pEditCommand); } // Select all/none contents. void qtractorMidiEditor::selectAll ( qtractorScrollView *pScrollView, bool bSelect, bool bToggle ) { // Select all/none view contents. if (bSelect) { const QRect rect(0, 0, pScrollView->contentsWidth(), pScrollView->contentsHeight()); selectRect(pScrollView, rect, bToggle, true); } else { clearSelect(); resetDragState(pScrollView); selectionChangeNotify(); } // Make sure main view keeps focus... QWidget::activateWindow(); pScrollView->setFocus(); } // Select range view contents. void qtractorMidiEditor::selectRange ( qtractorScrollView *pScrollView, bool bToggle, bool bCommit ) { const int x = m_iEditHeadX; const int y = 0; const int w = m_iEditTailX - m_iEditHeadX; const int h = pScrollView->contentsHeight(); selectRect(pScrollView, QRect(x, y, w, h), bToggle, bCommit); } // Select everything between a given view rectangle. void qtractorMidiEditor::selectRect ( qtractorScrollView *pScrollView, const QRect& rect, bool bToggle, bool bCommit ) { int flags = SelectNone; if (bToggle) flags |= SelectToggle; if (bCommit) flags |= SelectCommit; updateDragSelect(pScrollView, rect.normalized(), flags); resetDragState(pScrollView); selectionChangeNotify(); } // Add/remove one single event to current selection. void qtractorMidiEditor::selectEvent ( qtractorMidiEvent *pEvent, bool bSelect ) { if (pEvent == nullptr) return; QRect rectUpdateView(m_select.rectView()); QRect rectUpdateEvent(m_select.rectEvent()); // Select item (or toggle)... QRect rectEvent, rectView; updateEventRects(pEvent, rectEvent, rectView); m_select.selectItem(pEvent, rectEvent, rectView, true, !bSelect); // Commit selection... m_select.update(true); const QSize pad(2, 2); rectUpdateView = rectUpdateView.united(m_select.rectView()); m_pEditView->viewport()->update(QRect( m_pEditView->contentsToViewport(rectUpdateView.topLeft()), rectUpdateView.size() + pad)); rectUpdateEvent = rectUpdateEvent.united(m_select.rectEvent()); m_pEditEvent->viewport()->update(QRect( m_pEditEvent->contentsToViewport(rectUpdateEvent.topLeft()), rectUpdateEvent.size() + pad)); } // Retrieve current selection. QList qtractorMidiEditor::selectedEvents (void) const { QList list; const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) list.append(iter.key()); return list; } // Ensure point visibility depending on view. void qtractorMidiEditor::ensureVisible ( qtractorScrollView *pScrollView, const QPoint& pos ) { const int w = pScrollView->width(); const int wm = (w >> 3); if (pos.x() > w - wm) m_pEditView->updateContentsWidth(pos.x() + wm); if (static_cast (pScrollView) == m_pEditEvent) pScrollView->ensureVisible(pos.x(), 0, 16, 0); else pScrollView->ensureVisible(pos.x(), pos.y(), 16, 16); } // Make given frame position visible in view. void qtractorMidiEditor::ensureVisibleFrame ( qtractorScrollView *pScrollView, unsigned long iFrame ) { const int x0 = pScrollView->contentsX(); const int y = pScrollView->contentsY(); const int w = pScrollView->viewport()->width(); const int w3 = w - (w >> 3); int x = m_pTimeScale->pixelFromFrame(iFrame) - m_pTimeScale->pixelFromFrame(m_iOffset); if (x < x0) x -= w3; else if (x > x0 + w3) x += w3; pScrollView->ensureVisible(x, y, 0, 0); // pScrollView->setFocus(); } // Clear all selection. void qtractorMidiEditor::clearSelect (void) { const QRect rectUpdateView(m_select.rectView()); const QRect rectUpdateEvent(m_select.rectEvent()); m_select.clear(); m_pEditView->viewport()->update(QRect( m_pEditView->contentsToViewport(rectUpdateView.topLeft()), rectUpdateView.size())); m_pEditEvent->viewport()->update(QRect( m_pEditEvent->contentsToViewport(rectUpdateEvent.topLeft()), rectUpdateEvent.size())); } // Update all selection rectangular areas. void qtractorMidiEditor::updateSelect ( bool bSelectReset ) { qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iOffset); const unsigned long t0 = pNode->tickFromFrame(m_iOffset); const int x0 = m_pTimeScale->pixelFromFrame(m_iOffset); // This is the edit-view specifics... const int ch = m_pEditView->contentsHeight(); // + 1; const int h1 = m_pEditList->itemHeight(); const int h2 = (h1 >> 1); const int h4 = (h1 << 1); // This is the edit-event zero-line... const qtractorMidiEvent::EventType eventType = m_pEditEvent->eventType(); const int h0 = ((m_pEditEvent->viewport())->height() & ~1); // even. const int y0 = (eventType == qtractorMidiEvent::PITCHBEND ? h0 >> 1 : h0); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); qtractorMidiEditSelect::Item *pItem = iter.value(); // Common event coords... int y; const unsigned long t1 = t0 + pEvent->time(); const unsigned long t2 = t1 + pEvent->duration(); pNode = cursor.seekTick(t1); int x = pNode->pixelFromTick(t1); pNode = cursor.seekTick(t2); int w1 = pNode->pixelFromTick(t2) - x; if (w1 < m_iMinEventWidth) w1 = m_iMinEventWidth; // View item... const qtractorMidiEvent::EventType etype = pEvent->type(); if (etype == m_pEditView->eventType()) { y = ch - h1 * (pEvent->note() + 1); if (m_bDrumMode) pItem->rectView.setRect(x - x0 - h1, y - h2, h4, h4); else pItem->rectView.setRect(x - x0, y, w1, h1); } else pItem->rectView.setRect(0, 0, 0, 0); // Event item... if (etype == m_pEditEvent->eventType()) { if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) y = y0 - (y0 * pEvent->value()) / 16384; else if (etype == qtractorMidiEvent::PITCHBEND) y = y0 - (y0 * pEvent->pitchBend()) / 8192; else if (etype == qtractorMidiEvent::PGMCHANGE) y = y0 - (y0 * pEvent->param()) / 128; else y = y0 - (y0 * pEvent->value()) / 128; if (!m_bNoteDuration || m_bDrumMode) w1 = m_iMinEventWidth; if (y < y0) pItem->rectEvent.setRect(x - x0, y, w1, y0 - y); else if (y > y0) pItem->rectEvent.setRect(x - x0, y0, w1, y - y0); else pItem->rectEvent.setRect(x - x0, y0 - 2, w1, 4); } else pItem->rectEvent.setRect(0, 0, 0, 0); } // Final touch. m_select.commit(); if (bSelectReset) { m_rectDrag = m_select.rectView(); m_posDrag = m_rectDrag.topLeft(); resetDragState(nullptr); } } // Whether there's any events beyond the insertion point (edit-head). bool qtractorMidiEditor::isInsertable (void) const { if (m_pMidiClip == nullptr) return false; qtractorMidiSequence *pSeq = m_pMidiClip->sequence(); if (pSeq == nullptr) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; return m_pTimeScale->tickFromFrame(pSession->editHead()) < m_pTimeScale->tickFromFrame(m_iOffset) + pSeq->duration(); } // Whether there's any selected range (edit-head/tail). bool qtractorMidiEditor::isSelectable (void) const { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; return (pSession->editHead() < pSession->editTail()); } // Insert edit range. void qtractorMidiEditor::insertEditRange (void) { if (m_pMidiClip == nullptr) return; qtractorMidiSequence *pSeq = m_pMidiClip->sequence(); if (pSeq == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const unsigned long iEditHead = pSession->editHead(); const unsigned long iEditTail = pSession->editTail(); const unsigned long iInsertStart = m_pTimeScale->tickFromFrame(iEditHead); unsigned long iInsertEnd = 0; if (iEditHead < iEditTail) { iInsertEnd = m_pTimeScale->tickFromFrame(iEditTail); } else { const unsigned short iBar = m_pTimeScale->barFromFrame(iEditHead); iInsertEnd = m_pTimeScale->tickFromFrame(m_pTimeScale->frameFromBar(iBar + 1)); } if (iInsertStart >= iInsertEnd) return; const unsigned long iInsertDuration = iInsertEnd - iInsertStart; const unsigned long t0 = m_pTimeScale->tickFromFrame(m_iOffset); int iUpdate = 0; qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(m_pMidiClip, tr("insert range")); qtractorMidiEvent *pEvent = pSeq->events().first(); while (pEvent) { const unsigned long iTime = pEvent->time(); const unsigned long iDuration = pEvent->duration(); const unsigned long iEventStart = t0 + iTime; const unsigned long iEventEnd = iEventStart + iDuration; // Slip/move event... if (iEventEnd >= iInsertStart) { if (iEventStart < iInsertStart) { if (iEventEnd > iInsertStart) { // Resize left-event... pEditCommand->resizeEventTime(pEvent, iTime, iInsertStart - iEventStart); // Insert right-event... qtractorMidiEvent *pEventEx = new qtractorMidiEvent(*pEvent); pEventEx->setTime(iInsertEnd - t0); pEventEx->setDuration(iEventEnd - iInsertStart); pEditCommand->insertEvent(pEventEx); ++iUpdate; } } else { // Move whole-event... pEditCommand->resizeEventTime(pEvent, iTime + iInsertDuration, iDuration); ++iUpdate; } } pEvent = pEvent->next(); } if (iUpdate > 0) execute(pEditCommand); else delete pEditCommand; } // Remove edit range. void qtractorMidiEditor::removeEditRange (void) { if (m_pMidiClip == nullptr) return; qtractorMidiSequence *pSeq = m_pMidiClip->sequence(); if (pSeq == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const unsigned long iEditHead = pSession->editHead(); const unsigned long iEditTail = pSession->editTail(); const unsigned long iRemoveStart = m_pTimeScale->tickFromFrame(iEditHead); unsigned long iRemoveEnd = 0; if (iEditHead < iEditTail) { iRemoveEnd = m_pTimeScale->tickFromFrame(iEditTail); } else { unsigned short iBar = m_pTimeScale->barFromFrame(iEditHead); iRemoveEnd = m_pTimeScale->tickFromFrame(m_pTimeScale->frameFromBar(iBar + 1)); } if (iRemoveStart >= iRemoveEnd) return; const unsigned long iRemoveDuration = iRemoveEnd - iRemoveStart; const unsigned long t0 = m_pTimeScale->tickFromFrame(m_iOffset); int iUpdate = 0; qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(m_pMidiClip, tr("remove range")); qtractorMidiEvent *pEvent = pSeq->events().first(); while (pEvent) { const unsigned long iTime = pEvent->time(); const unsigned long iDuration = pEvent->duration(); const unsigned long iEventStart = t0 + iTime; const unsigned long iEventEnd = iEventStart + iDuration; // Slip/move event... if (iEventEnd >= iRemoveStart) { if (iEventStart < iRemoveStart) { // Resize left-event... pEditCommand->resizeEventTime(pEvent, iTime, iRemoveStart - iEventStart); if (iEventEnd > iRemoveEnd) { // Insert right-event... qtractorMidiEvent *pEventEx = new qtractorMidiEvent(*pEvent); pEventEx->setTime(iRemoveStart - t0); pEventEx->setDuration(iEventEnd - iRemoveEnd); pEditCommand->insertEvent(pEventEx); } } else if (iEventEnd > iRemoveEnd) { if (iEventStart < iRemoveEnd) { pEditCommand->resizeEventTime(pEvent, iRemoveStart - t0, iEventEnd - iRemoveEnd); } else { pEditCommand->resizeEventTime(pEvent, iTime - iRemoveDuration, iDuration); } } else pEditCommand->removeEvent(pEvent); ++iUpdate; } pEvent = pEvent->next(); } if (iUpdate > 0) execute(pEditCommand); else delete pEditCommand; } // Update/sync integral contents. void qtractorMidiEditor::updateContents (void) { // Update dependent views. m_pEditList->updateContentsHeight(); m_pEditView->updateContentsWidth(); updateSelect(false); // Trigger a complete view update... m_pEditList->updateContents(); m_pEditTime->updateContents(); m_pEditView->updateContents(); m_pEditEvent->updateContents(); m_pThumbView->updateContents(); } // Try to center vertically the edit-view... void qtractorMidiEditor::centerContents (void) { // Update dependent views. m_pEditList->updateContentsHeight(); m_pEditView->updateContentsWidth(); updateSelect(true); // Do the centering... qtractorMidiSequence *pSeq = nullptr; if (m_pMidiClip) pSeq = m_pMidiClip->sequence(); if (pSeq) { const int h2 = m_pEditList->itemHeight() * (pSeq->noteMin() + pSeq->noteMax()); int cy = m_pEditView->contentsHeight(); if (h2 > 0) cy -= ((h2 + (m_pEditView->viewport())->height()) >> 1); else cy >>= 1; if (cy < 0) cy = 0; m_pEditView->setContentsPos(m_pEditView->contentsX(), cy); } // Update visual cursors anyway... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { setPlayHead(pSession->playHead(), false); setEditHead(pSession->editHead(), false); setEditTail(pSession->editTail(), false); } // Trigger a complete view update... m_pEditList->updateContents(); m_pEditTime->updateContents(); m_pEditView->updateContents(); m_pEditEvent->updateContents(); m_pThumbView->updateContents(); } // Zoom centering prepare method. // (usually before zoom change) void qtractorMidiEditor::zoomCenterPre ( ZoomCenter& zc ) const { if (m_pTimeScale == nullptr) return; const int x0 = m_pTimeScale->pixelFromFrame(m_iOffset); const int cx = m_pEditView->contentsX(); const int cy = m_pEditView->contentsY(); QWidget *pViewport = m_pEditView->viewport(); const QRect& rect = pViewport->rect(); const QPoint& pos = pViewport->mapFromGlobal(QCursor::pos()); zc.x = 0; zc.y = 0; if (rect.contains(pos)) { if (m_iZoomMode & ZoomHorizontal) zc.x = pos.x(); if (m_iZoomMode & ZoomVertical) zc.y = pos.y(); } else { if (m_iZoomMode & ZoomHorizontal) { const int w2 = (rect.width() >> 1); if (cx > w2) zc.x = w2; } if (m_iZoomMode & ZoomVertical) { const int h2 = (rect.height() >> 1); if (cy > h2) zc.y = h2; } } zc.item = (cy + zc.y) / m_pEditList->itemHeight(); zc.frame = m_pTimeScale->frameFromPixel(cx + zc.x + x0); } // Zoom centering post methods. // (usually after zoom change) void qtractorMidiEditor::zoomCenterPost ( const ZoomCenter& zc ) { if (m_pTimeScale == nullptr) return; const int x0 = m_pTimeScale->pixelFromFrame(m_iOffset); int cx = m_pTimeScale->pixelFromFrame(zc.frame) - x0; int cy = zc.item * m_pEditList->itemHeight(); // Update dependent views. m_pEditList->updateContentsHeight(); m_pEditView->updateContentsWidth(); updateSelect(true); if (m_iZoomMode & ZoomHorizontal) { if (cx > zc.x) cx -= zc.x; else cx = 0; } if (m_iZoomMode & ZoomVertical) { if (cy > zc.y) cy -= zc.y; else cy = 0; } // Do the centering... m_pEditView->setContentsPos(cx, cy); // Update visual cursors anyway... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { setPlayHead(pSession->playHead(), false); setEditHead(pSession->editHead(), false); setEditTail(pSession->editTail(), false); } // Trigger a complete view update... m_pEditList->updateContents(); m_pEditTime->updateContents(); m_pEditView->updateContents(); m_pEditEvent->updateContents(); m_pThumbView->updateContents(); } // Reset event cursors. void qtractorMidiEditor::reset ( bool bSelectClear ) { if (bSelectClear) m_select.clear(); // Reset some internal state... m_cursor.clear(); m_cursorAt.clear(); } // Intra-clip tick/time positioning reset. qtractorMidiEvent *qtractorMidiEditor::seekEvent ( qtractorMidiSequence *pSeq, unsigned long iTime ) { // Reset seek-forward... return m_cursor.reset(pSeq, iTime); } // Get event from given contents position. qtractorMidiEvent *qtractorMidiEditor::eventAt ( qtractorScrollView *pScrollView, const QPoint& pos, QRect *pRect ) { if (m_pMidiClip == nullptr) return nullptr; qtractorMidiSequence *pSeq = m_pMidiClip->sequence(); if (pSeq == nullptr) return nullptr; const bool bEditView = (static_cast (m_pEditView) == pScrollView); qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iOffset); const unsigned long t0 = pNode->tickFromFrame(m_iOffset); const int x0 = m_pTimeScale->pixelFromFrame(m_iOffset); pNode = cursor.seekPixel(x0 + pos.x()); unsigned long iTime = pNode->tickFromPixel(x0 + pos.x()); iTime = (iTime > t0 ? iTime - t0 : 0); // This is the edit-view specifics... const int ch = m_pEditView->contentsHeight(); // + 1; const int h1 = m_pEditList->itemHeight(); const int h2 = (h1 >> 1); const int h4 = (h1 << 1); // This is the edit-event zero-line... const qtractorMidiEvent::EventType eventType = m_pEditEvent->eventType(); const int h0 = ((m_pEditEvent->viewport())->height() & ~1); // even. const int y0 = (eventType == qtractorMidiEvent::PITCHBEND ? h0 >> 1 : h0); const bool bEventParam = (eventType == qtractorMidiEvent::CONTROLLER || eventType == qtractorMidiEvent::REGPARAM || eventType == qtractorMidiEvent::NONREGPARAM || eventType == qtractorMidiEvent::CONTROL14); const unsigned short eventParam = m_pEditEvent->eventParam(); qtractorMidiEvent *pEvent = m_cursorAt.reset(pSeq, iTime); qtractorMidiEvent *pEventAt = nullptr; while (pEvent && iTime >= pEvent->time()) { if (((bEditView && pEvent->type() == m_pEditView->eventType()) || (!bEditView && (pEvent->type() == m_pEditEvent->eventType() && (!bEventParam || pEvent->param() == eventParam))))) { // Common event coords... const unsigned long t1 = t0 + pEvent->time(); const unsigned long t2 = t1 + pEvent->duration(); pNode = cursor.seekTick(t1); const int x = pNode->pixelFromTick(t1); pNode = cursor.seekTick(t2); int w1 = pNode->pixelFromTick(t2) - x; if (w1 < m_iMinEventWidth) w1 = m_iMinEventWidth; QRect rect; int y; if (bEditView) { // View item... y = ch - h1 * (pEvent->note() + 1); if (m_bDrumMode) rect.setRect(x - x0 - h1, y - h2, h4, h4); else rect.setRect(x - x0, y, w1, h1); } else { // Event item... const qtractorMidiEvent::EventType etype = pEvent->type(); if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) y = y0 - (y0 * pEvent->value()) / 16384; else if (etype == qtractorMidiEvent::PITCHBEND) y = y0 - (y0 * pEvent->pitchBend()) / 8192; else if (etype == qtractorMidiEvent::PGMCHANGE) y = y0 - (y0 * pEvent->param()) / 128; else y = y0 - (y0 * pEvent->value()) / 128; if (!m_bNoteDuration || m_bDrumMode) w1 = m_iMinEventWidth; if (y < y0) rect.setRect(x - x0, y, w1, y0 - y); else if (y > y0) rect.setRect(x - x0, y0, w1, y - y0); else rect.setRect(x - x0, y0 - 2, w1, 4); } // Do we have a point? if (rect.contains(pos)) { if (pRect) *pRect = rect; pEventAt = pEvent; // Whether event is also selected... if (m_select.findItem(pEventAt)) break; } } // Maybe next one... pEvent = pEvent->next(); } return pEventAt; } // Start immediate some drag-edit mode... qtractorMidiEvent *qtractorMidiEditor::dragEditEvent ( qtractorScrollView *pScrollView, const QPoint& pos, const Qt::KeyboardModifiers& modifiers ) { if (m_pMidiClip == nullptr) return nullptr; qtractorMidiSequence *pSeq = m_pMidiClip->sequence(); if (pSeq == nullptr) return nullptr; const bool bEditView = (static_cast (m_pEditView) == pScrollView); qtractorMidiEvent::EventType eventType = (bEditView ? m_pEditView->eventType() : m_pEditEvent->eventType()); const int ch = m_pEditView->contentsHeight(); int h1 = m_pEditList->itemHeight(); unsigned char note = (ch - pos.y()) / h1; if (m_iSnapToScaleType > 0 && !m_bDrumMode) note = snapToScale(note, m_iSnapToScaleKey, m_iSnapToScaleType); // Check for note/pitch changes... if (bEditView && m_bEventDragEdit && m_pEventDrag && (eventType == qtractorMidiEvent::NOTEON || eventType == qtractorMidiEvent::KEYPRESS) && m_pEventDrag->note() == note && !m_bDrumMode) return nullptr; // Must be inside the visible event canvas and // not about to drag(draw) event-value resizing... if (!bEditView && !m_bEventDragEdit && m_pEventDrag == nullptr && isDragEventResize(modifiers)) return nullptr; const int h0 = ((m_pEditEvent->viewport())->height() & ~1); // even. const int y0 = (eventType == qtractorMidiEvent::PITCHBEND ? h0 >> 1 : h0); // Compute onset time from given horizontal position... const int x0 = m_pTimeScale->pixelFromFrame(m_iOffset); int x1 = x0 + pos.x(); if (x1 < x0) x1 = x0; int y1 = 0; qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iOffset); const unsigned long t0 = pNode->tickFromFrame(m_iOffset); // This would the new event onset time... pNode = cursor.seekPixel(x1); const unsigned short p = (bEditView && m_bDrumMode ? 1 : 8); unsigned long t1 = pNode->tickSnap(pNode->tickFromPixel(x1), p); x1 = pNode->pixelFromTick(t1) - x0; if (t1 >= t0) t1 -= t0; // Check for time/onset changes and whether it's already drawn... if (m_bEventDragEdit && m_pEventDrag) { const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); qtractorMidiEditSelect::Item *pItem = iter.value(); if ((pItem->flags & 1) == 0) continue; const unsigned long t2 = pEvent->time(); const unsigned long t3 = t2 + (pEvent->type() == qtractorMidiEvent::NOTEON ? pEvent->duration() : 1); if (bEditView) { if (pEvent == m_pEventDrag) continue; if (pEvent->note() == note && t1 >= t2 && t1 < t3) pItem->flags &= ~3; // Remove... } else if (t1 >= t2 && t1 < t3) return pEvent; } // Current note events bumping up or down... if (bEditView && (!m_bEditModeDraw || m_bDrumMode)) { qtractorMidiEvent *pEvent = m_pEventDrag; qtractorMidiEditSelect::Item *pItem = m_select.findItem(pEvent); if (pItem && (pItem->flags & 1)) { const unsigned long t2 = pEvent->time(); const unsigned long t3 = t2 + (pEvent->type() == qtractorMidiEvent::NOTEON ? pEvent->duration() : 1); if ((t1 >= t2 && t1 < t3) || !m_bEditModeDraw) { if (pEvent->note() == note) { // Move in time.... pEvent->setTime(t1); pItem->rectView.moveLeft(x1 - h1); pItem->rectEvent.moveLeft(x1); m_select.updateItem(pItem); m_rectDrag = pItem->rectView; m_posDrag = m_rectDrag.center(); } else { // Bump in pitch... pEvent->setNote(note); y1 = ch - h1 * (note + 1); if (m_bDrumMode) y1 -= (h1 >> 1); pItem->rectView.moveTop(y1); m_select.updateItem(pItem); m_rectDrag = pItem->rectView; if (m_bDrumMode) { m_posDrag = m_rectDrag.center(); } else { m_posDrag = pos; // m_rectDrag.topLeft(); resizeEvent(pEvent, timeDelta(pScrollView), 0); } if (m_bSendNotes) m_pEditList->dragNoteOn(note, pEvent->velocity()); } // Done. return nullptr; } } } // No new events if ain't drawing... if (!m_bEditModeDraw) return nullptr; } // Create a brand new event... qtractorMidiEvent *pEvent = new qtractorMidiEvent(t1, eventType); // Compute value from given vertical position... y1 = pos.y(); if (y1 < 1) y1 = 1; else if (y1 > h0) y1 = h0; const qtractorMidiEvent::EventType etype = pEvent->type(); switch (etype) { case qtractorMidiEvent::NOTEON: case qtractorMidiEvent::KEYPRESS: // Set note event value... if (bEditView) { pEvent->setNote(note); pEvent->setVelocity(m_last.value); } else { pEvent->setNote(m_last.note); if (y0 > 0) pEvent->setVelocity((128 * (y0 - y1)) / y0); else pEvent->setVelocity(m_last.value); } // Default duration... if (pEvent->type() == qtractorMidiEvent::NOTEON) { unsigned long iDuration = pNode->ticksPerBeat; if (m_pTimeScale->snapPerBeat() > 0) iDuration /= m_pTimeScale->snapPerBeat(); pEvent->setDuration(iDuration); // Mark that we've a note pending... if (m_bSendNotes) m_pEditList->dragNoteOn(pEvent->note(), pEvent->velocity()); } break; case qtractorMidiEvent::REGPARAM: case qtractorMidiEvent::NONREGPARAM: // Set RPN/NRPN event... pEvent->setParam(m_pEditEvent->eventParam()); if (y0 > 0) pEvent->setValue((16384 * (y0 - y1)) / y0); else pEvent->setValue(m_last.value); break; case qtractorMidiEvent::CONTROL14: // Set Control-14 event... pEvent->setController(m_pEditEvent->eventParam()); if (y0 > 0) pEvent->setValue((16384 * (y0 - y1)) / y0); else pEvent->setValue(m_last.value); break; case qtractorMidiEvent::PITCHBEND: // Set pitchbend event value... if (y0 > 0) pEvent->setPitchBend((8192 * (y0 - y1)) / y0); else pEvent->setPitchBend(m_last.pitchBend); break; case qtractorMidiEvent::PGMCHANGE: // Set program change event... if (y0 > 0) pEvent->setParam((128 * (y0 - y1)) / y0); else pEvent->setParam(m_last.value); break; case qtractorMidiEvent::CONTROLLER: // Set controller event... pEvent->setController(m_pEditEvent->eventParam()); // Fall thru... default: // Set generic event value... if (y0 > 0) pEvent->setValue((128 * (y0 - y1)) / y0); else pEvent->setValue(m_last.value); break; } // Now try to get the visual rectangular coordinates... const unsigned long t2 = pEvent->time() + pEvent->duration(); pNode = cursor.seekTick(t2); int w1 = pNode->pixelFromTick(t2) - x1; if (w1 < m_iMinEventWidth) w1 = m_iMinEventWidth; // View item... QRect rectView; if (etype == m_pEditView->eventType() && (etype == qtractorMidiEvent::NOTEON || etype == qtractorMidiEvent::KEYPRESS)) { y1 = ch - h1 * (pEvent->note() + 1); if (m_bDrumMode) { const int h2 = (h1 >> 1); const int h4 = (h1 << 1); rectView.setRect(x1 - h1, y1 - h2, h4, h4); } else { rectView.setRect(x1, y1, w1, h1); } } // Event item... QRect rectEvent; if (etype == m_pEditEvent->eventType()) { if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) { y1 = y0 - (y0 * pEvent->value()) / 16384; h1 = y0 - y1; m_resizeMode = ResizeValue14; } else if (etype == qtractorMidiEvent::PITCHBEND) { y1 = y0 - (y0 * pEvent->pitchBend()) / 8192; if (y1 > y0) { h1 = y1 - y0; y1 = y0; } else { h1 = y0 - y1; } } else if (etype == qtractorMidiEvent::PGMCHANGE) { y1 = y0 - (y0 * pEvent->param()) / 128; h1 = y0 - y1; m_resizeMode = ResizePgmChange; } else { y1 = y0 - (y0 * pEvent->value()) / 128; h1 = y0 - y1; m_resizeMode = ResizeValue; } if (!m_bNoteDuration || m_bDrumMode) w1 = m_iMinEventWidth; if (h1 < 3) h1 = 3; rectEvent.setRect(x1, y1, w1, h1); } // Set the correct target rectangle... m_rectDrag = (bEditView ? rectView : rectEvent); m_posDrag = (bEditView && m_bDrumMode ? m_rectDrag.center() : pos); // Just add this one the selection... if (!m_bEventDragEdit || m_pEventDrag == nullptr) clearSelect(); m_select.selectItem(pEvent, rectEvent, rectView, true, false); // Set the proper resize-mode... if (bEditView && etype == qtractorMidiEvent::NOTEON && !m_bDrumMode) { m_resizeMode = ResizeNoteRight; } else { if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) m_resizeMode = ResizeValue14; else if (etype == qtractorMidiEvent::PITCHBEND) m_resizeMode = ResizePitchBend; else if (etype == qtractorMidiEvent::PGMCHANGE) m_resizeMode = ResizePgmChange; else m_resizeMode = ResizeValue; } // Let it be a drag resize mode... return pEvent; } // Track drag-move-select cursor and mode... qtractorMidiEvent *qtractorMidiEditor::dragMoveEvent ( qtractorScrollView *pScrollView, const QPoint& pos, const Qt::KeyboardModifiers& modifiers ) { qtractorMidiEvent *pEvent = eventAt(pScrollView, pos, &m_rectDrag); // Make the anchor event, if any, visible yet... m_resizeMode = ResizeNone; if (pEvent) { const bool bEditView = (static_cast (m_pEditView) == pScrollView); Qt::CursorShape shape = Qt::PointingHandCursor; const qtractorMidiEvent::EventType etype = pEvent->type(); if (bEditView) { if (etype == qtractorMidiEvent::NOTEON && !m_bDrumMode) { if (pos.x() > m_rectDrag.right() - 4) { m_resizeMode = ResizeNoteRight; shape = Qt::SplitHCursor; } else if (pos.x() < m_rectDrag.left() + 4) { m_resizeMode = ResizeNoteLeft; shape = Qt::SplitHCursor; } } } else { if (etype == qtractorMidiEvent::NOTEON) { if (pos.y() < m_rectDrag.top() + 4) { m_resizeMode = ResizeValue; shape = Qt::SplitVCursor; } else if (m_bNoteDuration && !m_bDrumMode) { if (pos.x() > m_rectDrag.right() - 4) { m_resizeMode = ResizeNoteRight; shape = Qt::SplitHCursor; } else if (pos.x() < m_rectDrag.left() + 4) { m_resizeMode = ResizeNoteLeft; shape = Qt::SplitHCursor; } } } else if (etype == qtractorMidiEvent::PITCHBEND) { const int y0 = (((m_pEditEvent->viewport())->height() & ~1) >> 1); if ((pos.y() < y0 && pos.y() < m_rectDrag.top() + 4) || (pos.y() > y0 && pos.y() > m_rectDrag.bottom() - 4)) { m_resizeMode = ResizePitchBend; shape = Qt::SplitVCursor; } } else if (pos.y() < m_rectDrag.top() + 4) { if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) m_resizeMode = ResizeValue14; else if (etype == qtractorMidiEvent::PGMCHANGE) m_resizeMode = ResizePgmChange; else m_resizeMode = ResizeValue; shape = Qt::SplitVCursor; } if (m_resizeMode == ResizeNone && isDragEventResize(modifiers)) { shape = Qt::ArrowCursor; pEvent = nullptr; } } if ((m_resizeMode == ResizeNoteRight || m_resizeMode == ResizePitchBend || m_resizeMode == ResizePgmChange || m_resizeMode == ResizeValue14 || m_resizeMode == ResizeValue) && (modifiers & Qt::ControlModifier)) m_dragCursor = DragRescale; else m_dragCursor = DragResize; setEditCursor(QCursor(shape)); } else if (m_dragState == DragNone) { m_dragCursor = DragNone; unsetEditCursor(); } return pEvent; } // Start drag-move-selecting... void qtractorMidiEditor::dragMoveStart ( qtractorScrollView *pScrollView, const QPoint& pos, const Qt::KeyboardModifiers& modifiers ) { // Are we already step-moving or pasting something? switch (m_dragState) { case DragStep: // One-click change from drag-step to drag-move... m_dragState = DragMove; m_posDrag = m_rectDrag.center(); m_posStep = QPoint(0, 0); m_pDragStep = nullptr; updateDragMove(pScrollView, pos + m_posStep); // Fall thru... case DragPaste: return; default: break; } // Force null state. resetDragState(pScrollView); // Remember what and where we'll be dragging/selecting... m_dragState = DragStart; m_posDrag = pos; m_pEventDrag = dragMoveEvent(pScrollView, m_posDrag, modifiers); // Check whether we're about to create something... if (m_pEventDrag == nullptr && m_bEditMode && (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) == 0) { m_dragCursor = m_dragState; m_pEventDrag = dragEditEvent(pScrollView, m_posDrag, modifiers); m_bEventDragEdit = (m_pEventDrag != nullptr); unsetEditCursor(); } else if (m_resizeMode == ResizeNone) { m_dragCursor = m_dragState; if (m_pEventDrag) { setEditCursor(QCursor( static_cast (m_pEditView) == pScrollView ? Qt::SizeAllCursor : Qt::SizeHorCursor)); } else { setEditCursor(QCursor(Qt::CrossCursor)); } } // Maybe we'll have a note pending... if (m_bSendNotes && m_pEventDrag && m_pEventDrag->type() == qtractorMidiEvent::NOTEON) m_pEditList->dragNoteOn(m_pEventDrag->note(), m_pEventDrag->velocity()); } // Update drag-move-selection... void qtractorMidiEditor::dragMoveUpdate ( qtractorScrollView *pScrollView, const QPoint& pos, const Qt::KeyboardModifiers& modifiers ) { const bool bEditView = (static_cast (m_pEditView) == pScrollView); int flags = SelectNone; switch (m_dragState) { case DragStart: // Did we moved enough around? if ((pos - m_posDrag).manhattanLength() < QApplication::startDragDistance()) break; // Are we about to move/resize something around? if (m_pEventDrag) { // Take care of selection modifier... if ((modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) == 0 && !m_select.findItem(m_pEventDrag)) flags |= SelectClear; if (m_resizeMode == ResizeNone) { // Start moving... take care of yet initial selection... updateDragSelect(pScrollView, QRect(m_posDrag, QSize(1, 1)), flags | SelectCommit); // Start drag-moving... m_dragState = DragMove; updateDragMove(pScrollView, pos + m_posStep); } else { // Start resizing... take care of yet initial selection... if (!m_bEventDragEdit && !m_select.findItem(m_pEventDrag)) { updateDragSelect(pScrollView, QRect(m_posDrag, QSize(1, 1)), flags | SelectCommit); } // Start drag-resizing/rescaling... if ((m_resizeMode == ResizeNoteRight || m_resizeMode == ResizePitchBend || m_resizeMode == ResizePgmChange || m_resizeMode == ResizeValue14 || m_resizeMode == ResizeValue) && (modifiers & Qt::ControlModifier)) { m_dragState = DragRescale; updateDragRescale(pScrollView, pos); } else { m_dragState = DragResize; updateDragResize(pScrollView, pos); } } break; } // About to drag(draw) event-value resizing... if (!bEditView && isDragEventResize(modifiers)) { m_dragState = DragEventResize; updateDragEventResize(pos); break; } // Just about to start rubber-banding... m_dragState = DragSelect; // Take care of no-selection modifier... if ((modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) == 0) flags |= SelectClear; // Fall thru... case DragSelect: { // Set new rubber-band extents... ensureVisible(pScrollView, pos); if (modifiers & Qt::ControlModifier) flags |= SelectToggle; const QRect& rect = QRect(m_posDrag, pos).normalized(); updateDragSelect(pScrollView, rect, flags); showToolTip(pScrollView, rect); break; } case DragMove: case DragPaste: // Drag-moving... updateDragMove(pScrollView, pos + m_posStep); break; case DragRescale: // Drag-rescaling... updateDragRescale(pScrollView, pos); break; case DragResize: // Drag-resizing... updateDragResize(pScrollView, pos); // Drag-edit/drawing... if (m_bEventDragEdit && m_pEventDrag) { qtractorMidiEvent *pEvent = dragEditEvent(pScrollView, pos, modifiers); if (pEvent && pEvent != m_pEventDrag) { if (!bEditView || !m_bDrumMode) { resizeEvent(m_pEventDrag, timeDelta(pScrollView), valueDelta(pScrollView)); m_posDelta = QPoint(0, 0); } m_pEventDrag = pEvent; } } break; case DragEventResize: // Drag(draw) resizing... updateDragEventResize(pos); break; case DragStep: case DragNone: default: // Just make cursor tell something... dragMoveEvent(pScrollView, pos, modifiers); break; } // Let note hovering shine... const int iNote = (pScrollView->contentsHeight() - pos.y()) / m_pEditList->itemHeight(); m_pEditList->dragNoteOn(iNote, -1); } // Commit drag-move-selection... void qtractorMidiEditor::dragMoveCommit ( qtractorScrollView *pScrollView, const QPoint& pos, const Qt::KeyboardModifiers& modifiers ) { int flags = qtractorMidiEditor::SelectCommit; bool bModifier = (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)); switch (m_dragState) { case DragStart: // Were we about to edit-resize something? if (m_bEventDragEdit) { m_dragState = DragResize; executeDragResize(pScrollView, pos); break; } // Take care of selection modifier... if (!bModifier) flags |= SelectClear; // Shall we move the playhead?... if (m_pEventDrag == nullptr) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bShiftKeyModifier) bModifier = !bModifier; if (bModifier) { // Direct snap positioning... const unsigned long iFrame = frameSnap(m_iOffset + m_pTimeScale->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); // Playhead positioning... setPlayHead(iFrame); // Immediately commited... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->setPlayHead(iFrame); } } // Fall thru... case DragSelect: // Terminate selection... ensureVisible(pScrollView, pos); if (modifiers & Qt::ControlModifier) flags |= SelectToggle; updateDragSelect(pScrollView, QRect(m_posDrag, pos).normalized(), flags); selectionChangeNotify(); break; case DragMove: // Move it... executeDragMove(pScrollView, pos); break; case DragPaste: // Paste it... executeDragPaste(pScrollView, pos); break; case DragRescale: // Rescale it... executeDragRescale(pScrollView, pos); break; case DragResize: // Resize it... executeDragResize(pScrollView, pos); break; case DragEventResize: // Resize(by drawing) it... executeDragEventResize(pos); break; case DragStep: case DragNone: default: break; } // Force null state. resetDragState(pScrollView); } // Trap for help/tool-tip and leave events. bool qtractorMidiEditor::dragMoveFilter ( qtractorScrollView *pScrollView, QObject *pObject, QEvent *pEvent ) { if (static_cast (pObject) == pScrollView->viewport()) { if (pEvent->type() == QEvent::ToolTip && m_bToolTips) { QHelpEvent *pHelpEvent = static_cast (pEvent); if (pHelpEvent) { const QPoint& pos = pScrollView->viewportToContents(pHelpEvent->pos()); qtractorMidiEvent *pMidiEvent = eventAt(pScrollView, pos); if (pMidiEvent) { QToolTip::showText( pHelpEvent->globalPos(), eventToolTip(pMidiEvent), pScrollView->viewport()); return true; } else if (pScrollView == static_cast (m_pEditView)) { const QString sToolTip("%1 (%2)"); const int ch = m_pEditList->contentsHeight(); const int note = (ch - pos.y()) / m_pEditList->itemHeight(); QToolTip::showText( pHelpEvent->globalPos(), sToolTip.arg(noteName(note)).arg(note), pScrollView->viewport()); return true; } } } else if (pEvent->type() == QEvent::Leave && m_dragState != DragPaste && m_dragState != DragStep) { m_dragCursor = DragNone; unsetEditCursor(); m_pEditList->dragNoteOff(); return true; } } // Not handled here. return false; } // Compute current drag time/duration snap (in ticks). long qtractorMidiEditor::timeSnap ( long iTime ) const { if (iTime < 1) iTime = 0; qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iOffset); const unsigned long t0 = pNode->tickFromFrame(m_iOffset); const unsigned long t1 = t0 + iTime; pNode = cursor.seekTick(t1); const long iTimeSnap = long(pNode->tickSnap(t1)) - long(t0); return (iTimeSnap > 0 ? iTimeSnap : 0); } long qtractorMidiEditor::durationSnap ( long iTime, long iDuration ) const { if (iTime < 1) iTime = 0; if (iDuration < 1) iDuration = 0; qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iOffset); const unsigned long t0 = pNode->tickFromFrame(m_iOffset); const unsigned long t1 = t0 + iTime; const unsigned long t2 = t1 + iDuration; pNode = cursor.seekTick(t2); long iDurationSnap = long(pNode->tickSnap(t2)) - long(t1); if (iDurationSnap < 1) { const unsigned short iSnapPerBeat = m_pTimeScale->snapPerBeat(); if (iSnapPerBeat > 0) iDurationSnap = pNode->ticksPerBeat / iSnapPerBeat; else iDurationSnap = 1; } return iDurationSnap; } // Compute current drag time delta (in ticks). long qtractorMidiEditor::timeDelta ( qtractorScrollView *pScrollView ) const { DragTimeScale dts(m_pTimeScale, m_iOffset); int x1, x2; unsigned long t1, t2; if (m_pEventDrag) { t1 = dts.t0 + m_pEventDrag->time(); if (m_resizeMode == ResizeNoteRight) t1 += m_pEventDrag->duration(); dts.node = dts.cursor.seekTick(t1); x1 = dts.node->pixelFromTick(t1); } else { const bool bEditView = static_cast (m_pEditView) == pScrollView; const QRect& rect = (bEditView ? m_select.rectView() : m_select.rectEvent()); x1 = dts.x0 + rect.x(); if (m_resizeMode == ResizeNoteRight) x1 += rect.width(); dts.node = dts.cursor.seekPixel(x1); t1 = dts.node->tickFromPixel(x1); } x2 = x1 + m_posDelta.x(); dts.node = dts.cursor.seekPixel(x2); t2 = dts.node->tickFromPixel(x2); return long(dts.node->tickSnap(t2)) - long(t1); // return long(t2) - long(t1); } // Compute current drag note delta. int qtractorMidiEditor::noteDelta ( qtractorScrollView *pScrollView ) const { int iNoteDelta = 0; if (pScrollView == static_cast (m_pEditView)) { const int h1 = m_pEditList->itemHeight(); if (h1 > 0) iNoteDelta = -(m_posDelta.y() / h1); } return iNoteDelta; } // Compute current drag value delta. int qtractorMidiEditor::valueDelta ( qtractorScrollView *pScrollView ) const { int iValueDelta = 0; if (pScrollView == static_cast (m_pEditEvent)) { const int h0 = ((m_pEditEvent->viewport())->height() & ~1); // even. if (h0 > 1) { if (m_resizeMode == ResizePitchBend) iValueDelta = -(m_posDelta.y() * 8192) / (h0 >> 1); else if (m_resizeMode == ResizeValue14) iValueDelta = -(m_posDelta.y() * 16384) / h0; else iValueDelta = -(m_posDelta.y() * 128) / h0; } } return iValueDelta; } // Apply the event drag-resize (also editing). void qtractorMidiEditor::resizeEvent ( qtractorMidiEvent *pEvent, long iTimeDelta, int iValueDelta, qtractorMidiEditCommand *pEditCommand ) { long iTime, iDuration; int iValue; switch (m_resizeMode) { case ResizeNoteLeft: iTime = timeSnap(long(pEvent->time()) + iTimeDelta); iDuration = long(pEvent->duration()) + (long(pEvent->time()) - iTime); if (iDuration < 1) iDuration = durationSnap(iTime, iDuration); if (m_bEventDragEdit) { pEvent->setTime(iTime); pEvent->setDuration(iDuration); if (pEditCommand) pEditCommand->insertEvent(pEvent); } else if (pEditCommand) pEditCommand->resizeEventTime(pEvent, iTime, iDuration); if (pEvent == m_pEventDrag) { m_last.note = pEvent->note(); m_last.duration = iDuration; } break; case ResizeNoteRight: iTime = pEvent->time(); iDuration = durationSnap(iTime, long(pEvent->duration()) + iTimeDelta); if (m_bEventDragEdit) { pEvent->setDuration(iDuration); if (pEditCommand) pEditCommand->insertEvent(pEvent); } else if (pEditCommand) pEditCommand->resizeEventTime(pEvent, iTime, iDuration); if (pEvent == m_pEventDrag) { m_last.note = pEvent->note(); m_last.duration = iDuration; } break; case ResizeValue: iValue = safeValue(pEvent->value() + iValueDelta); if (m_bEventDragEdit) { pEvent->setValue(iValue); if (pEditCommand) pEditCommand->insertEvent(pEvent); } else if (pEditCommand) pEditCommand->resizeEventValue(pEvent, iValue); if (pEvent == m_pEventDrag) m_last.value = iValue; break; case ResizeValue14: iValue = safeValue14(pEvent->value() + iValueDelta); if (m_bEventDragEdit) { pEvent->setValue(iValue); if (pEditCommand) pEditCommand->insertEvent(pEvent); } else if (pEditCommand) pEditCommand->resizeEventValue(pEvent, iValue); if (pEvent == m_pEventDrag) m_last.value = iValue; break; case ResizePitchBend: iValue = safePitchBend(pEvent->pitchBend() + iValueDelta); if (m_bEventDragEdit) { pEvent->setPitchBend(iValue); if (pEditCommand) pEditCommand->insertEvent(pEvent); } else if (pEditCommand) pEditCommand->resizeEventValue(pEvent, iValue); if (pEvent == m_pEventDrag) m_last.pitchBend = iValue; break; case ResizePgmChange: iValue = safeValue(pEvent->param() + iValueDelta); if (m_bEventDragEdit) { pEvent->setParam(iValue); if (pEditCommand) pEditCommand->insertEvent(pEvent); } else if (pEditCommand) pEditCommand->resizeEventValue(pEvent, iValue); if (pEvent == m_pEventDrag) m_last.value = iValue; // Fall thru... default: break; } if (m_bEventDragEdit && pEditCommand == nullptr) updateEvent(pEvent); } // Update event selection rectangle. void qtractorMidiEditor::updateEvent ( qtractorMidiEvent *pEvent ) { qtractorMidiEditSelect::Item *pItem = m_select.findItem(pEvent); if (pItem == nullptr) return; // Update selection visual rectangles... updateEventRects(pEvent, pItem->rectEvent, pItem->rectView); m_select.updateItem(pItem); } // Update event visual rectangles. void qtractorMidiEditor::updateEventRects ( qtractorMidiEvent *pEvent, QRect& rectEvent, QRect& rectView ) const { qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iOffset); const unsigned long t0 = pNode->tickFromFrame(m_iOffset); const int x0 = m_pTimeScale->pixelFromFrame(m_iOffset); // This is the edit-view spacifics... const int h1 = m_pEditList->itemHeight(); const int ch = m_pEditView->contentsHeight(); // + 1; // This is the edit-event zero-line... const qtractorMidiEvent::EventType eventType = m_pEditEvent->eventType(); const int h0 = ((m_pEditEvent->viewport())->height() & ~1); // even. const int y0 = (eventType == qtractorMidiEvent::PITCHBEND ? h0 >> 1 : h0); // Common event coords... const unsigned long t1 = t0 + pEvent->time(); const unsigned long t2 = t1 + pEvent->duration(); pNode = cursor.seekTick(t1); int x = pNode->pixelFromTick(t1); pNode = cursor.seekTick(t2); int w1 = pNode->pixelFromTick(t2) - x; if (w1 < m_iMinEventWidth) w1 = m_iMinEventWidth; // View item... int y; if (pEvent->type() == m_pEditView->eventType()) { y = ch - h1 * (pEvent->note() + 1); if (m_bDrumMode) { const int h2 = (h1 >> 1); const int h4 = (h1 << 1); rectView.setRect(x - x0 - h1, y - h2, h4, h4); } else rectView.setRect(x - x0, y, w1, h1); } else rectView.setRect(0, 0, 0, 0); // Event item... const qtractorMidiEvent::EventType etype = pEvent->type(); if (etype == eventType) { if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) y = y0 - (y0 * pEvent->value()) / 16384; else if (etype == qtractorMidiEvent::PITCHBEND) y = y0 - (y0 * pEvent->pitchBend()) / 8192; else if (etype == qtractorMidiEvent::PGMCHANGE) y = y0 - (y0 * pEvent->param()) / 128; else y = y0 - (y0 * pEvent->value()) / 128; if (!m_bNoteDuration || m_bDrumMode) w1 = m_iMinEventWidth; if (y < y0) rectEvent.setRect(x - x0, y, w1, y0 - y); else if (y > y0) rectEvent.setRect(x - x0, y0, w1, y - y0); else rectEvent.setRect(x - x0, y0 - 2, w1, 4); } else rectEvent.setRect(0, 0, 0, 0); } // Update the event selection list. void qtractorMidiEditor::updateDragSelect ( qtractorScrollView *pScrollView, const QRect& rectSelect, int flags ) { if (m_pMidiClip == nullptr) return; qtractorMidiSequence *pSeq = m_pMidiClip->sequence(); if (pSeq == nullptr) return; // Rubber-banding only applicable whenever // the selection rectangle is not that empty... const bool bRectSelect = (rectSelect.width() > 1 || rectSelect.height() > 1); if (bRectSelect) { // Create rubber-band, if not already... if (m_pRubberBand == nullptr) { m_pRubberBand = new qtractorRubberBand( QRubberBand::Rectangle, pScrollView->viewport()); m_pRubberBand->show(); } // Rubber-band selection... m_pRubberBand->setGeometry(QRect( pScrollView->contentsToViewport(rectSelect.topLeft()), rectSelect.size())); } // Do the drag-select update properly... const bool bEditView = (static_cast (m_pEditView) == pScrollView); QRect rectUpdateView(m_select.rectView()); QRect rectUpdateEvent(m_select.rectEvent()); if (flags & SelectClear) m_select.clear(); qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iOffset); const unsigned long t0 = pNode->tickFromFrame(m_iOffset); int x0 = m_pTimeScale->pixelFromFrame(m_iOffset); int x1, x2; if (bRectSelect) { x1 = pScrollView->contentsX(); x2 = x1 + (pScrollView->viewport())->width(); if (x1 > rectSelect.left()) x1 = rectSelect.left(); if (x2 < rectSelect.right()) x2 = rectSelect.right(); } else { x1 = x2 = rectSelect.x(); } #if 0 if (--x0 < 0) x0 = 0; if (--x1 < 0) x1 = 0; ++x2; #endif pNode = cursor.seekPixel(x0 + x1); unsigned long t1 = pNode->tickFromPixel(x0 + x1); const unsigned long iTickStart = (t1 > t0 ? t1 - t0 : 0); pNode = cursor.seekPixel(x0 + x2); unsigned long t2 = pNode->tickFromPixel(x0 + x2); const unsigned long iTickEnd = (t2 > t0 ? t2 - t0 : 0); // This is the edit-view spacifics... const int ch = m_pEditView->contentsHeight(); // + 1; const int h1 = m_pEditList->itemHeight(); const int h2 = (h1 >> 1); const int h4 = (h1 << 1); // This is the edit-event zero-line... const qtractorMidiEvent::EventType eventType = m_pEditEvent->eventType(); const int h0 = ((m_pEditEvent->viewport())->height() & ~1); // even. const int y0 = (eventType == qtractorMidiEvent::PITCHBEND ? h0 >> 1 : h0); const bool bEventParam = (eventType == qtractorMidiEvent::CONTROLLER || eventType == qtractorMidiEvent::REGPARAM || eventType == qtractorMidiEvent::NONREGPARAM || eventType == qtractorMidiEvent::CONTROL14); const unsigned short eventParam = m_pEditEvent->eventParam(); qtractorMidiEvent *pEvent = m_cursorAt.seek(pSeq, iTickStart); qtractorMidiEvent *pEventAt = nullptr; QRect rectViewAt; QRect rectEventAt; while (pEvent && iTickEnd >= pEvent->time()) { if (((bEditView && pEvent->type() == m_pEditView->eventType()) || (!bEditView && (pEvent->type() == m_pEditEvent->eventType() && (!bEventParam || pEvent->param() == eventParam))))) { // Assume unselected... bool bSelect = false; // Common event coords... int y; t1 = t0 + pEvent->time(); t2 = t1 + pEvent->duration(); pNode = cursor.seekTick(t1); int x = pNode->pixelFromTick(t1); pNode = cursor.seekTick(t2); int w1 = pNode->pixelFromTick(t2) - x; if (w1 < m_iMinEventWidth) w1 = m_iMinEventWidth; // View item... QRect rectView; if (pEvent->type() == m_pEditView->eventType()) { y = ch - h1 * (pEvent->note() + 1); if (m_bDrumMode) rectView.setRect(x - x0 - h1, y - h2, h4, h4); else rectView.setRect(x - x0, y, w1, h1); if (bEditView) bSelect = rectSelect.intersects(rectView); } // Event item... QRect rectEvent; const qtractorMidiEvent::EventType etype = pEvent->type(); if (etype == eventType) { if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) y = y0 - (y0 * pEvent->value()) / 16384; else if (pEvent->type() == qtractorMidiEvent::PITCHBEND) y = y0 - (y0 * pEvent->pitchBend()) / 8192; else if (pEvent->type() == qtractorMidiEvent::PGMCHANGE) y = y0 - (y0 * pEvent->param()) / 128; else y = y0 - (y0 * pEvent->value()) / 128; if (!m_bNoteDuration || m_bDrumMode) w1 = m_iMinEventWidth; if (y < y0) rectEvent.setRect(x - x0, y, w1, y0 - y); else if (y > y0) rectEvent.setRect(x - x0, y0, w1, y - y0); else rectEvent.setRect(x - x0, y0 - 2, w1, 4); if (!bEditView) bSelect = rectSelect.intersects(rectEvent); } // Select item... if (bRectSelect) { m_select.selectItem(pEvent, rectEvent, rectView, bSelect, flags & SelectToggle); } else if (bSelect) { pEventAt = pEvent; rectViewAt = rectView; rectEventAt = rectEvent; } } // Lookup next... pEvent = pEvent->next(); } // Most evident single selection... if (pEventAt /* && !bRectSelect*/) { m_select.selectItem(pEventAt, rectEventAt, rectViewAt, true, flags & SelectToggle); } // Commit selection... m_select.update(flags & SelectCommit); const QSize pad(2, 2); rectUpdateView = rectUpdateView.united(m_select.rectView()); m_pEditView->viewport()->update(QRect( m_pEditView->contentsToViewport(rectUpdateView.topLeft()), rectUpdateView.size() + pad)); rectUpdateEvent = rectUpdateEvent.united(m_select.rectEvent()); m_pEditEvent->viewport()->update(QRect( m_pEditEvent->contentsToViewport(rectUpdateEvent.topLeft()), rectUpdateEvent.size() + pad)); } // Drag-move current selection. void qtractorMidiEditor::updateDragMove ( qtractorScrollView *pScrollView, const QPoint& pos ) { ensureVisible(pScrollView, pos); const bool bEditView = (static_cast (m_pEditView) == pScrollView); QRect rectUpdateView(m_select.rectView().translated(m_posDelta)); QRect rectUpdateEvent(m_select.rectEvent().translated(m_posDelta.x(), 0)); const QPoint delta(pos - m_posDrag); QRect rect(bEditView ? m_select.rectView() : m_select.rectEvent()); const int h1 = (bEditView ? m_pEditList->itemHeight() : 1); const int cw = pScrollView->contentsWidth(); const int ch = pScrollView->contentsHeight(); int dx = delta.x(); const int x1 = rect.x() + dx + (m_bDrumMode ? h1 : 0); if (x1 < 0) dx = -rect.x() - (m_bDrumMode ? h1 : 0); if (x1 + rect.width() > cw) dx = cw - rect.right(); int x0 = m_rectDrag.x(); if (m_bDrumMode) x0 += h1; x0 += m_pTimeScale->pixelFromFrame(m_iOffset); m_posDelta.setX(pixelSnap(x0 + dx) - x0); // Get anchor event... qtractorMidiEvent *pEventDrag = m_pEventDrag; if (pEventDrag == nullptr) pEventDrag = m_select.anchorEvent(); int iValueDelta = 0; if (bEditView) { int y0 = rect.y(); if (m_bDrumMode) y0 += (h1 >> 1); int y1 = y0 + delta.y(); if (y1 < 0) y1 = 0; else if (y1 + rect.height() > ch) y1 = ch - rect.height(); unsigned char note = 127 - (y1 / h1); if (m_iSnapToScaleType > 0 && !m_bDrumMode) note = snapToScale(note, m_iSnapToScaleKey, m_iSnapToScaleType); m_posDelta.setY(h1 * (127 - note) - y0); } else if (m_dragState == DragStep) { const int dy = (delta.y() - m_posStepDelta.y()); const int h0 = ((pScrollView->viewport())->height() & ~1); // even. const int y0 = (h0 >> 1); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); qtractorMidiEditSelect::Item *pItem = iter.value(); if ((pItem->flags & 1) == 0) continue; int y1 = pItem->rectEvent.y(); if (pEvent->type() == qtractorMidiEvent::PITCHBEND) { if (y0 < pItem->rectEvent.bottom()) y1 = pItem->rectEvent.bottom(); y1 += dy; if (y1 >= y0) { if (y1 > ch) y1 = ch; pItem->rectEvent.setBottom(y1); y1 = y0; } else { pItem->rectEvent.setBottom(y0); } } else { y1 += dy; } if (y1 < 0) y1 = 0; else if (y1 > ch) y1 = ch; pItem->rectEvent.setY(y1); pItem->flags |= 4; m_select.updateItem(pItem); if (m_bToolTips && pEvent == pEventDrag && h0 > 1) { if (pEvent->type() == qtractorMidiEvent::PITCHBEND) iValueDelta = -(delta.y() * 8192) / (h0 >> 1); else if (pEvent->type() == qtractorMidiEvent::REGPARAM || pEvent->type() == qtractorMidiEvent::NONREGPARAM || pEvent->type() == qtractorMidiEvent::CONTROL14) iValueDelta = -(delta.y() * 16384) / h0; else iValueDelta = -(delta.y() * 128) / h0; } } m_posStepDelta.setY(delta.y()); } const QSize pad(2, 2); rectUpdateView = rectUpdateView.united( m_select.rectView().translated(m_posDelta)); m_pEditView->viewport()->update(QRect( m_pEditView->contentsToViewport(rectUpdateView.topLeft()), rectUpdateView.size() + pad)); rectUpdateEvent = rectUpdateEvent.united( m_select.rectEvent().translated(m_posDelta.x(), 0)); m_pEditEvent->viewport()->update(QRect( m_pEditEvent->contentsToViewport(rectUpdateEvent.topLeft()), rectUpdateEvent.size() + pad)); // Maybe we've change some note pending... if (m_bSendNotes && pEventDrag && pEventDrag->type() == qtractorMidiEvent::NOTEON) { int iNote = int(pEventDrag->note()); if (h1 > 0) iNote -= (m_posDelta.y() / h1); m_pEditList->dragNoteOn(iNote, pEventDrag->velocity()); } // Show anchor event tooltip... if (m_bToolTips && pEventDrag) { QToolTip::showText( QCursor::pos(), eventToolTip(pEventDrag, timeDelta(pScrollView), noteDelta(pScrollView), iValueDelta), pScrollView->viewport()); } } // Drag-rescale current selection. void qtractorMidiEditor::updateDragRescale ( qtractorScrollView *pScrollView, const QPoint& pos ) { ensureVisible(pScrollView, pos); const QPoint delta(pos - m_posDrag); int x0, x1; int y0, y1; int dx = 0; int dy = 0; switch (m_resizeMode) { case ResizeNoteRight: dx = delta.x(); x0 = m_rectDrag.right() + m_pTimeScale->pixelFromFrame(m_iOffset); x1 = m_rectDrag.right() + dx; if (x1 < m_rectDrag.left()) dx = -(m_rectDrag.width()); dx = pixelSnap(x0 + dx) - x0; break; case ResizeValue: case ResizeValue14: case ResizePitchBend: case ResizePgmChange: dy = delta.y(); y0 = m_rectDrag.bottom(); y1 = y0 + dy; dy = y1 - y0; // Fall thru... default: break; } m_posDelta.setX(dx); m_posDelta.setY(dy); if (dx || dy) { m_pEditView->viewport()->update(); m_pEditEvent->viewport()->update(); } // Show anchor event tooltip... if (m_bToolTips) { qtractorMidiEvent *pEvent = m_pEventDrag; if (pEvent == nullptr) pEvent = m_select.anchorEvent(); if (pEvent) { QToolTip::showText( QCursor::pos(), eventToolTip(pEvent, timeDelta(pScrollView), 0, valueDelta(pScrollView)), pScrollView->viewport()); } } } // Drag-resize current selection (also editing). void qtractorMidiEditor::updateDragResize ( qtractorScrollView *pScrollView, const QPoint& pos ) { ensureVisible(pScrollView, pos); QRect rectUpdateView(m_select.rectView().translated(m_posDelta.x(), 0)); QRect rectUpdateEvent(m_select.rectEvent().translated(m_posDelta)); const QPoint delta(pos - m_posDrag); int x0, x1; int y0, y1; int dx = 0; int dy = 0; switch (m_resizeMode) { case ResizeNoteLeft: dx = delta.x(); x0 = m_rectDrag.left() + m_pTimeScale->pixelFromFrame(m_iOffset); x1 = m_rectDrag.left() + dx; if (x1 > m_rectDrag.right()) { dx -= m_rectDrag.width(); m_resizeMode = ResizeNoteRight; m_posDrag.setX(m_rectDrag.right()); x0 += m_rectDrag.width(); } dx = pixelSnap(x0 + dx) - x0; break; case ResizeNoteRight: dx = delta.x(); x0 = m_rectDrag.right() + m_pTimeScale->pixelFromFrame(m_iOffset); x1 = m_rectDrag.right() + dx; if (x1 < m_rectDrag.left()) { dx += m_rectDrag.width(); m_resizeMode = ResizeNoteLeft; m_posDrag.setX(m_rectDrag.left()); x0 -= m_rectDrag.width(); } dx = pixelSnap(x0 + dx) - x0; break; case ResizeValue: if (m_bDrumMode && m_bEventDragEdit // HACK: Fake note resizes... && static_cast (m_pEditView) == pScrollView) break; // Fall thru... case ResizeValue14: case ResizePitchBend: case ResizePgmChange: dy = delta.y(); y0 = m_rectDrag.bottom(); y1 = y0 + dy; dy = y1 - y0; // Fall thru... default: break; } m_posDelta.setX(dx); m_posDelta.setY(dy); const QSize pad(2, 2); rectUpdateView = rectUpdateView.united( m_select.rectView().translated(m_posDelta.x(), 0)); m_pEditView->viewport()->update(QRect( m_pEditView->contentsToViewport(rectUpdateView.topLeft()), rectUpdateView.size() + pad)); rectUpdateEvent = rectUpdateEvent.united( m_select.rectEvent().translated(m_posDelta)); m_pEditEvent->viewport()->update(QRect( m_pEditEvent->contentsToViewport(rectUpdateEvent.topLeft()), rectUpdateEvent.size() + pad)); // Show anchor event tooltip... if (m_bToolTips) { qtractorMidiEvent *pEvent = m_pEventDrag; if (pEvent == nullptr) pEvent = m_select.anchorEvent(); if (pEvent) { QToolTip::showText( QCursor::pos(), eventToolTip(pEvent, timeDelta(pScrollView), 0, valueDelta(pScrollView)), pScrollView->viewport()); } } } // Drag(draw) event value-resize check. bool qtractorMidiEditor::isDragEventResize ( Qt::KeyboardModifiers modifiers ) const { if (!m_bEditMode/* || !m_bEditModeDraw*/) return false; if (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) return false; const qtractorMidiEvent::EventType eventType = m_pEditEvent->eventType(); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); qtractorMidiEditSelect::Item *pItem = iter.value(); if ((pItem->flags & 1) == 0) continue; if (pEvent->type() == eventType) return true; } return false; } // Drag(draw) event value-resize to current selection... void qtractorMidiEditor::updateDragEventResize ( const QPoint& pos ) { m_pEditEvent->ensureVisible(pos.x(), 0, 16, 0); const QPoint delta(pos - m_posDrag); if (delta.manhattanLength() < QApplication::startDragDistance()) return; const qtractorMidiEvent::EventType eventType = m_pEditEvent->eventType(); const int h0 = ((m_pEditEvent->viewport())->height() & ~1); // even. const int y0 = (eventType == qtractorMidiEvent::PITCHBEND ? h0 >> 1 : h0); if (y0 < 1) return; const QRect& rectDrag = QRect(m_posDrag, m_posDragEventResize).normalized(); QRect rectUpdateEvent(m_select.rectEvent().united(rectDrag)); const int xmin = (m_bEditModeDraw || delta.x() < 0 ? pos.x() : m_posDrag.x()); const int xmax = (m_bEditModeDraw || delta.x() > 0 ? pos.x() : m_posDrag.x()); const int ymin = 1; const int ymax = h0; const float m = (m_bEditModeDraw ? 0.0f : float(delta.y()) / float(delta.x())); const float b = float(pos.y()) - m * float(pos.x()); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); qtractorMidiEditSelect::Item *pItem = iter.value(); if ((pItem->flags & 1) == 0) continue; if (pEvent->type() != eventType) continue; const QRect& rectEvent = pItem->rectEvent; if (rectEvent.right() < xmin || rectEvent.left() > xmax) continue; int y = int(m * float(rectEvent.x()) + b); if (y < ymin) y = ymin; else if (y > ymax) y = ymax; if (pEvent->type() == qtractorMidiEvent::PITCHBEND) { if (y > y0) { pItem->rectEvent.setBottom(y); y = y0; } else { pItem->rectEvent.setBottom(y0); } } pItem->rectEvent.setTop(y); pItem->flags |= 4; m_select.updateItem(pItem); } rectUpdateEvent = rectUpdateEvent.united(m_select.rectEvent()); m_pEditEvent->viewport()->update(QRect( m_pEditEvent->contentsToViewport(rectUpdateEvent.topLeft()), rectUpdateEvent.size())); m_posDragEventResize = pos; } // Finalize the event drag-move. void qtractorMidiEditor::executeDragMove ( qtractorScrollView *pScrollView, const QPoint& pos ) { if (m_pMidiClip == nullptr) return; updateDragMove(pScrollView, pos + m_posStep); const bool bEditView = (static_cast (m_pEditView) == pScrollView); const long iTimeDelta = timeDelta(pScrollView); const int iNoteDelta = noteDelta(pScrollView); const qtractorMidiEvent::EventType eventType = m_pEditEvent->eventType(); const int h0 = ((m_pEditEvent->viewport())->height() & ~1); // even. const int y0 = (eventType == qtractorMidiEvent::PITCHBEND ? h0 >> 1 : h0); qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(m_pMidiClip, tr("move")); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); qtractorMidiEditSelect::Item *pItem = iter.value(); if ((pItem->flags & 1) == 0) continue; const long iTime = long(pEvent->time() + pItem->delta) + iTimeDelta; // if (pEvent == m_pEventDrag) // iTime = timeSnap(iTime); if (bEditView) { const int iNote = safeNote(pEvent->note() + iNoteDelta); pEditCommand->moveEventNote(pEvent, iNote, iTime); } else if (m_dragState == DragStep && m_posStepDelta.y() != 0) { int iValue = 0; int y = pItem->rectEvent.y(); const qtractorMidiEvent::EventType etype = pEvent->type(); if (etype == qtractorMidiEvent::PITCHBEND) { if (y >= y0) y = pItem->rectEvent.bottom(); iValue = safePitchBend((8192 * (y0 - y)) / y0); } else { if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) iValue = safeValue14((16384 * (y0 - y)) / y0); else iValue = safeValue((128 * (y0 - y)) / y0); } pEditCommand->moveEventValue(pEvent, iValue, iTime); } else { pEditCommand->moveEventTime(pEvent, iTime); } } // Make it as an undoable command... execute(pEditCommand); } // Finalize the event drag-resize (also editing). void qtractorMidiEditor::executeDragResize ( qtractorScrollView *pScrollView, const QPoint& pos ) { if (m_pMidiClip == nullptr) return; updateDragResize(pScrollView, pos); const long iTimeDelta = timeDelta(pScrollView); const int iValueDelta = valueDelta(pScrollView); qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(m_pMidiClip, m_bEventDragEdit ? tr("edit") : tr("resize")); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); qtractorMidiEditSelect::Item *pItem = iter.value(); if ((pItem->flags & 1) == 0) continue; if (!m_bEventDragEdit || m_pEventDrag == pEvent) resizeEvent(pEvent, iTimeDelta, iValueDelta, pEditCommand); else resizeEvent(pEvent, 0, 0, pEditCommand); } // On edit mode we own the new events... if (m_bEventDragEdit) { m_bEventDragEdit = false; m_pEventDrag = nullptr; m_select.clear(); } // Make it as an undoable command... execute(pEditCommand); } // Finalize the event drag-rescale. void qtractorMidiEditor::executeDragRescale ( qtractorScrollView *pScrollView, const QPoint& pos ) { if (m_pMidiClip == nullptr) return; if (m_pEventDrag == nullptr) return; updateDragRescale(pScrollView, pos); DragTimeScale *pDts = nullptr; int x1; unsigned long t1 = 0, t2; unsigned long d1 = 0, d2; int v1 = 0, v2; long iTimeDelta = 0; int iValueDelta = 0; switch (m_resizeMode) { case ResizeNoteRight: pDts = new DragTimeScale(m_pTimeScale, m_iOffset); t1 = pDts->t0 + m_pEventDrag->time(); pDts->node = pDts->cursor.seekTick(t1); x1 = pDts->node->pixelFromTick(t1); d1 = m_pEventDrag->duration(); x1 += m_posDelta.x(); pDts->node = pDts->cursor.seekPixel(x1); t2 = pDts->node->tickFromPixel(x1); iTimeDelta = long(pDts->node->tickSnap(t2)) - long(t1); break; case ResizeValue: case ResizeValue14: v1 = m_pEventDrag->value(); iValueDelta = valueDelta(pScrollView); break; case ResizePitchBend: v1 = m_pEventDrag->pitchBend(); iValueDelta = valueDelta(pScrollView); break; case ResizePgmChange: v1 = m_pEventDrag->param(); iValueDelta = valueDelta(pScrollView); // Fall thru... default: break; } qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(m_pMidiClip, tr("rescale")); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); qtractorMidiEditSelect::Item *pItem = iter.value(); if ((pItem->flags & 1) == 0) continue; switch (m_resizeMode) { case ResizeNoteRight: if (pDts && d1 > 0) { d2 = pEvent->duration() * (d1 + iTimeDelta) / d1; if (d2 < 1) d2 = 1; t2 = pDts->t0 + pEvent->time(); if (t2 > t1) t2 = t1 + (t2 - t1) * (d1 + iTimeDelta) / d1; if (t2 < pDts->t0) t2 = pDts->t0; pEditCommand->resizeEventTime(pEvent, t2 - pDts->t0, d2); if (pEvent == m_pEventDrag) { m_last.note = pEvent->note(); m_last.duration = d2; } } break; case ResizeValue: if (v1) { v2 = safeValue(pEvent->value() * (v1 + iValueDelta) / v1); pEditCommand->resizeEventValue(pEvent, v2); if (pEvent == m_pEventDrag) m_last.value = v2; } break; case ResizeValue14: if (v1) { v2 = safeValue14(pEvent->value() * (v1 + iValueDelta) / v1); pEditCommand->resizeEventValue(pEvent, v2); if (pEvent == m_pEventDrag) m_last.value = v2; } break; case ResizePitchBend: if (v1) { v2 = safePitchBend(pEvent->pitchBend() * (v1 + iValueDelta) / v1); pEditCommand->resizeEventValue(pEvent, v2); if (pEvent == m_pEventDrag) m_last.pitchBend = v2; } break; case ResizePgmChange: if (v1) { v2 = safeValue(pEvent->param() * (v1 + iValueDelta) / v1); pEditCommand->resizeEventValue(pEvent, v2); if (pEvent == m_pEventDrag) m_last.value = v2; } // Fall thru... default: break; } } // Local cleanup. if (pDts) delete pDts; // Make it as an undoable command... execute(pEditCommand); } // Finalize the event drag-paste. void qtractorMidiEditor::executeDragPaste ( qtractorScrollView *pScrollView, const QPoint& pos ) { if (m_pMidiClip == nullptr) return; updateDragMove(pScrollView, pos + m_posStep); const bool bEditView = (static_cast (m_pEditView) == pScrollView); const long iTimeDelta = timeDelta(pScrollView); const int iNoteDelta = (bEditView ? noteDelta(pScrollView) : 0); qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(m_pMidiClip, tr("paste")); QList events; const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = new qtractorMidiEvent(*iter.key()); qtractorMidiEditSelect::Item *pItem = iter.value(); if ((pItem->flags & 1) == 0) continue; const long iTime = long(pEvent->time() + pItem->delta) + iTimeDelta; // if (pEvent == m_pEventDrag) // iTime = timeSnap(iTime); pEvent->setTime(iTime); if (bEditView) pEvent->setNote(safeNote(pEvent->note() + iNoteDelta)); else if (m_pEditEvent->eventType() == qtractorMidiEvent::CONTROLLER) pEvent->setController(m_pEditEvent->eventParam()); pEditCommand->insertEvent(pEvent); events.append(pEvent); } // Make it as an undoable command... execute(pEditCommand); // Remake current selection alright... if (!events.isEmpty()) { m_select.clear(); QListIterator event_iter(events); while (event_iter.hasNext()) { qtractorMidiEvent *pEvent = event_iter.next(); if (pEvent) { QRect rectEvent, rectView; updateEventRects(pEvent, rectEvent, rectView); m_select.addItem(pEvent, rectEvent, rectView); } } m_select.update(false); } } // Apply drag(draw) event value-resize to current selection. void qtractorMidiEditor::executeDragEventResize ( const QPoint& pos ) { if (m_pMidiClip == nullptr) return; updateDragEventResize(pos); const qtractorMidiEvent::EventType eventType = m_pEditEvent->eventType(); const int h0 = ((m_pEditEvent->viewport())->height() & ~1); // even. const int y0 = (eventType == qtractorMidiEvent::PITCHBEND ? h0 >> 1 : h0); if (y0 < 1) return; qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(m_pMidiClip, tr("resize")); const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); qtractorMidiEditSelect::Item *pItem = iter.value(); if ((pItem->flags & 1) == 0) continue; if ((pItem->flags & 4) == 0) continue; if (pEvent->type() != eventType) continue; int iValue = 0; int y = pItem->rectEvent.y(); const qtractorMidiEvent::EventType etype = pEvent->type(); if (etype == qtractorMidiEvent::PITCHBEND) { if (y >= y0) y = pItem->rectEvent.bottom(); iValue = safePitchBend((8192 * (y0 - y + 1)) / y0); pEditCommand->resizeEventValue(pEvent, iValue); if (pEvent == m_pEventDrag) m_last.pitchBend = iValue; } else { if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) iValue = safeValue14((16384 * (y0 - y)) / y0); else iValue = safeValue((128 * (y0 - y)) / y0); pEditCommand->resizeEventValue(pEvent, iValue); if (pEvent == m_pEventDrag) m_last.value = iValue; } pItem->flags &= ~4; } // Make it as an undoable command... execute(pEditCommand); } // Visualize the event selection drag-move. void qtractorMidiEditor::paintDragState ( qtractorScrollView *pScrollView, QPainter *pPainter ) { const bool bEditView = (static_cast (m_pEditView) == pScrollView); #ifdef CONFIG_DEBUG_0 const QRect& rectSelect = (bEditView ? m_select.rectView() : m_select.rectEvent()); if (!rectSelect.isEmpty()) { pPainter->fillRect(QRect( pScrollView->contentsToViewport(rectSelect.topLeft()), rectSelect.size()), QColor(255, 0, 255, 40)); } #endif int x1, y1; DragTimeScale *pDts = nullptr; unsigned long t1 = 0, t2; unsigned long d1 = 0, d2; int v1 = 0; long iTimeDelta = 0; int iValueDelta = 0; if (m_dragState == DragRescale && m_pEventDrag) { switch (m_resizeMode) { case ResizeNoteRight: pDts = new DragTimeScale(m_pTimeScale, m_iOffset); t1 = pDts->t0 + m_pEventDrag->time(); pDts->node = pDts->cursor.seekTick(t1); x1 = pDts->node->pixelFromTick(t1); d1 = m_pEventDrag->duration(); x1 += m_posDelta.x(); pDts->node = pDts->cursor.seekPixel(x1); t2 = pDts->node->tickFromPixel(x1); iTimeDelta = long(pDts->node->tickSnap(t2)) - long(t1); break; case ResizeValue: case ResizeValue14: v1 = m_pEventDrag->value(); iValueDelta = valueDelta(pScrollView); break; case ResizePitchBend: v1 = m_pEventDrag->pitchBend(); iValueDelta = valueDelta(pScrollView); break; case ResizePgmChange: v1 = m_pEventDrag->param(); iValueDelta = valueDelta(pScrollView); // Fall thru... default: break; } } QVector diamond; if (m_bDrumMode) { const int h1 = (m_pEditList->itemHeight() >> 1) + 2; diamond.append(QPoint(-h1, 0)); diamond.append(QPoint( 0, -h1)); diamond.append(QPoint(+h1, 0)); diamond.append(QPoint( 0, +h1)); } const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); const QColor rgbaSelect(0, 0, 255, 120); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.key(); qtractorMidiEditSelect::Item *pItem = iter.value(); if ((pItem->flags & 1) == 0) continue; QRect rect = (bEditView ? pItem->rectView : pItem->rectEvent); if (!m_bEventDragEdit || pEvent == m_pEventDrag) { if (m_dragState == DragRescale) { switch (m_resizeMode) { case ResizeNoteRight: if (pDts && d1 > 0) { t2 = pDts->t0 + pEvent->time(); if (t2 > t1) t2 = t1 + (t2 - t1) * (d1 + iTimeDelta) / d1; if (t2 < pDts->t0) t2 = pDts->t0; pDts->node = pDts->cursor.seekTick(t2); rect.setLeft( pDts->node->pixelFromTick(t2) - pDts->x0); if (bEditView || (m_bNoteDuration && !m_bDrumMode)) { d2 = pEvent->duration() * (d1 + iTimeDelta) / d1; if (d2 < 1) d2 = 1; rect.setRight( pDts->node->pixelFromTick(t2 + d2) - pDts->x0); } else rect.setWidth(5); } break; case ResizeValue: case ResizeValue14: case ResizePgmChange: if (v1) { y1 = rect.height() * (v1 + iValueDelta) / v1; y1 = rect.bottom() - y1; if (y1 < 0) y1 = 0; rect.setTop(y1); } break; case ResizePitchBend: if (v1) { const int y0 = ((m_pEditEvent->viewport())->height() & ~1) >> 1; y1 = rect.height() * (v1 + iValueDelta) / v1; if (y0 > rect.top()) { y1 = rect.bottom() - y1; if (y1 < 0) y1 = 0; if (y1 > rect.bottom()) { rect.setTop(rect.bottom()); rect.setBottom(y1); } else { rect.setTop(y1); } } else if (y0 < rect.bottom()) { y1 = rect.top() + y1; if (y1 < 0) y1 = 0; if (y1 < rect.top()) { rect.setBottom(rect.top()); rect.setTop(y1); } else { rect.setBottom(y1); } } } // Fall thru... default: break; } } else if (m_dragState == DragResize) { switch (m_resizeMode) { case ResizeNoteLeft: x1 = rect.left() + m_posDelta.x(); if (x1 < 0) x1 = 0; if (x1 > rect.right()) x1 = rect.right(); rect.setLeft(x1); if (!bEditView && (!m_bNoteDuration || m_bDrumMode)) rect.setWidth(5); break; case ResizeNoteRight: if (bEditView || (m_bNoteDuration && !m_bDrumMode)) { x1 = rect.right() + m_posDelta.x(); if (x1 < rect.left()) x1 = rect.left(); rect.setRight(x1); } break; case ResizeValue: case ResizeValue14: case ResizePgmChange: if (!bEditView) { y1 = rect.top() + m_posDelta.y(); if (y1 < 0) y1 = 0; if (y1 > rect.bottom()) y1 = rect.bottom(); rect.setTop(y1); } break; case ResizePitchBend: if (!bEditView) { const int y0 = ((m_pEditEvent->viewport())->height() & ~1) >> 1; if (y0 > rect.top()) { y1 = rect.top() + m_posDelta.y(); if (y1 < 0) y1 = 0; if (y1 > rect.bottom()) { rect.setTop(rect.bottom()); rect.setBottom(y1); } else { rect.setTop(y1); } } else if (y0 < rect.bottom()) { y1 = rect.bottom() + m_posDelta.y(); if (y1 < 0) y1 = 0; if (y1 < rect.top()) { rect.setBottom(rect.top()); rect.setTop(y1); } else { rect.setBottom(y1); } } } // Fall thru... default: break; } } // Draw for selection/move... else if (bEditView) rect.translate(m_posDelta); else rect.translate(m_posDelta.x(), 0); } // Paint the damn bastard... pPainter->setPen(rgbaSelect); if (pEvent == m_pEventDrag) pPainter->setBrush(rgbaSelect.lighter()); else pPainter->setBrush(rgbaSelect); if (bEditView && m_bDrumMode) { pPainter->drawPolygon(QPolygon(diamond).translated( pScrollView->contentsToViewport(rect.center() + QPoint(1, 1)))); // ++diamond; } else { pPainter->drawRect(QRect( pScrollView->contentsToViewport(rect.topLeft()), rect.size())); } } // Local cleanup. if (pDts) delete pDts; // Paint drag(draw) event-value line... if (!bEditView && m_dragState == DragEventResize && !m_bEditModeDraw) { QPen pen(Qt::DotLine); pen.setColor(Qt::blue); pPainter->setPen(pen); pPainter->drawLine( pScrollView->contentsToViewport(m_posDrag), pScrollView->contentsToViewport(m_posDragEventResize)); } } // Reset drag/select/move state. void qtractorMidiEditor::resetDragState ( qtractorScrollView *pScrollView ) { if (m_bEventDragEdit) { const qtractorMidiEditSelect::ItemList& items = m_select.items(); qtractorMidiEditSelect::ItemList::ConstIterator iter = items.constBegin(); const qtractorMidiEditSelect::ItemList::ConstIterator& iter_end = items.constEnd(); for ( ; iter != iter_end; ++iter) delete iter.key(); m_select.clear(); } m_pEventDrag = nullptr; m_bEventDragEdit = false; m_posDelta = QPoint(0, 0); m_posStep = QPoint(0, 0); m_posStepDelta = QPoint(0, 0); m_pDragStep = nullptr; m_posDragEventResize = QPoint(0, 0); if (m_pRubberBand) { m_pRubberBand->hide(); delete m_pRubberBand; m_pRubberBand = nullptr; } if (pScrollView) { if (m_dragState != DragNone) { m_dragCursor = DragNone; unsetEditCursor(); } if (m_dragState == DragMove || m_dragState == DragResize || m_dragState == DragRescale || m_dragState == DragPaste || m_dragState == DragStep) { // m_select.clear(); updateContents(); } } if (m_pEditList) m_pEditList->dragNoteOff(); m_dragState = DragNone; m_resizeMode = ResizeNone; } // Edit tools form page selector. void qtractorMidiEditor::executeTool ( int iToolIndex ) { if (m_pMidiClip == nullptr) return; qtractorMidiToolsForm toolsForm(this); toolsForm.setToolIndex(iToolIndex); if (toolsForm.exec()) { qtractorMidiEditCommand *pMidiEditCommand = toolsForm.midiEditCommand(m_pMidiClip, &m_select, m_pTimeScale->tickFromFrame(m_iOffset)); qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand = toolsForm.timeScaleNodeCommand(); while (pTimeScaleNodeCommand) { pMidiEditCommand->addTimeScaleNodeCommand(pTimeScaleNodeCommand); pTimeScaleNodeCommand = toolsForm.timeScaleNodeCommand(); } execute(pMidiEditCommand); } QWidget::activateWindow(); m_pEditView->setFocus(); } // Command list accessor. qtractorCommandList *qtractorMidiEditor::commands (void) const { return (m_pMidiClip ? m_pMidiClip->commands() : nullptr); } // Command executioner... bool qtractorMidiEditor::execute ( qtractorCommand *pCommand ) { qtractorCommandList *pCommands = commands(); return (pCommands ? pCommands->exec(pCommand) : false); } // Update instrument default note names (nb. drum key names). void qtractorMidiEditor::updateDefaultDrumNoteNames (void) { initDefaultNoteNames(); QHash::ConstIterator iter = g_noteNames.constBegin(); const QHash::ConstIterator& iter_end = g_noteNames.constEnd(); for ( ; iter != iter_end; ++iter) { const unsigned char note = iter.key(); if (note >= 12) m_noteNames.insert(note, iter.value()); } } // Update instrument default controller names. void qtractorMidiEditor::updateDefaultControllerNames (void) { initDefaultControllerNames(); QHash::ConstIterator iter = g_controllerNames.constBegin(); const QHash::ConstIterator& iter_end = g_controllerNames.constEnd(); for ( ; iter != iter_end; ++iter) m_controllerNames.insert(iter.key(), iter.value()); } // Update instrument default RPN contrioller names. void qtractorMidiEditor::updateDefaultRpnNames (void) { const QMap& rpns = defaultRpnNames(); QMap::ConstIterator rpns_iter = rpns.constBegin(); const QMap::ConstIterator& rpns_end = rpns.constEnd(); for ( ; rpns_iter != rpns_end; ++rpns_iter) m_rpnNames.insert(rpns_iter.key(), rpns_iter.value()); } // Update instrument default NRPN contrioller names. void qtractorMidiEditor::updateDefaultNrpnNames (void) { const QMap& nrpns = defaultNrpnNames(); QMap::ConstIterator nrpns_iter = nrpns.constBegin(); const QMap::ConstIterator& nrpns_end = nrpns.constEnd(); for ( ; nrpns_iter != nrpns_end; ++nrpns_iter) m_nrpnNames.insert(nrpns_iter.key(), nrpns_iter.value()); } // Update instrument defined names for current clip/track. void qtractorMidiEditor::updateInstrumentNames (void) { m_noteNames.clear(); m_programNames.clear(); m_controllerNames.clear(); m_rpnNames.clear(); m_nrpnNames.clear(); // Update deafault controller names... updateDefaultControllerNames(); updateDefaultRpnNames(); updateDefaultNrpnNames(); if (m_pMidiClip == nullptr) return; qtractorTrack *pTrack = m_pMidiClip->track(); if (pTrack == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorInstrumentList *pInstruments = pSession->instruments(); if (pInstruments == nullptr) return; // Get instrument name from patch descriptor... QString sInstrumentName; qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus) sInstrumentName = pMidiBus->patch(pTrack->midiChannel()).instrumentName; // Do we have any?... if (sInstrumentName.isEmpty() || !pInstruments->contains(sInstrumentName)) { // Default drumk-key note names: // at least have a GM Drums help... if (m_bDrumMode || pTrack->isMidiDrums()) updateDefaultDrumNoteNames(); // No instrument definition... return; } // Finally, got instrument descriptor... const qtractorInstrument& instr = pInstruments->value(sInstrumentName); const int iBank = pTrack->midiBank(); const int iProg = pTrack->midiProg(); // Default drumk-key note names: // at least have a GM Drums help... if (m_bDrumMode || pTrack->isMidiDrums() || instr.isDrum(iBank, iProg)) updateDefaultDrumNoteNames(); // Key note names... const qtractorInstrumentData& notes = instr.notes(iBank, iProg); qtractorInstrumentData::ConstIterator notes_iter = notes.constBegin(); const qtractorInstrumentData::ConstIterator& notes_end = notes.constEnd(); for ( ; notes_iter != notes_end; ++notes_iter) m_noteNames.insert(notes_iter.key(), notes_iter.value()); // Program names... const qtractorInstrumentData& programs = instr.patch(iBank); qtractorInstrumentData::ConstIterator programs_iter = programs.constBegin(); const qtractorInstrumentData::ConstIterator& programs_end = programs.constEnd(); for ( ; programs_iter != programs_end; ++programs_iter) { m_programNames.insert( programs_iter.key(), programs_iter.value()); } // Controller names... const qtractorInstrumentData& controllers = instr.controllers(); qtractorInstrumentData::ConstIterator controllers_iter = controllers.constBegin(); const qtractorInstrumentData::ConstIterator& controllers_end = controllers.constEnd(); for ( ; controllers_iter != controllers_end; ++controllers_iter) { m_controllerNames.insert( controllers_iter.key(), controllers_iter.value()); } // RPN names... const qtractorInstrumentData& rpns = instr.rpns(); qtractorInstrumentData::ConstIterator rpns_iter = rpns.constBegin(); const qtractorInstrumentData::ConstIterator& rpns_end = rpns.constEnd(); for ( ; rpns_iter != rpns_end; ++rpns_iter) { m_rpnNames.insert( rpns_iter.key(), rpns_iter.value()); } // NRPN names... const qtractorInstrumentData& nrpns = instr.nrpns(); qtractorInstrumentData::ConstIterator nrpns_iter = nrpns.constBegin(); const qtractorInstrumentData::ConstIterator& nrpns_end = nrpns.constEnd(); for ( ; nrpns_iter != nrpns_end; ++nrpns_iter) { m_nrpnNames.insert( nrpns_iter.key(), nrpns_iter.value()); } } // Note name map accessor. const QString qtractorMidiEditor::noteName ( unsigned char note ) const { QHash::ConstIterator iter = m_noteNames.constFind(note); if (iter == m_noteNames.constEnd()) return defaultNoteName(note); else return iter.value(); } // Program map accessors. const QString& qtractorMidiEditor::programName ( unsigned char prog ) const { QHash::ConstIterator iter = m_programNames.constFind(prog); if (iter == m_programNames.constEnd()) return g_sDashes;//defaultProgramName(prog); else return iter.value(); } // Controller name map accessor. const QString& qtractorMidiEditor::controllerName ( unsigned char controller ) const { QHash::ConstIterator iter = m_controllerNames.constFind(controller); if (iter == m_controllerNames.constEnd()) return g_sDashes;//defaultControllerName(controller); else return iter.value(); } // RPN/NRPN map accessors. const QMap& qtractorMidiEditor::rpnNames (void) const { return m_rpnNames; } const QMap& qtractorMidiEditor::nrpnNames (void) const { return m_nrpnNames; } // Control-14 map accessors. const QString& qtractorMidiEditor::control14Name ( unsigned char controller ) const { return defaultControl14Name(controller); } // Command execution notification slot. void qtractorMidiEditor::updateNotifySlot ( unsigned int flags ) { if (flags & qtractorCommand::Refresh) updateContents(); if (flags & qtractorCommand::Reset) emit changeNotifySignal(nullptr); else emit changeNotifySignal(this); } // Emit selection/changes. void qtractorMidiEditor::selectionChangeNotify (void) { setSyncViewHoldOn(true); emit selectNotifySignal(this); m_pThumbView->update(); } // Emit note on/off. void qtractorMidiEditor::sendNote ( int iNote, int iVelocity, bool bForce ) { if (iVelocity == 1) iVelocity = m_last.value; emit sendNoteSignal(iNote, iVelocity, bForce); } // Safe/capped value helpers. int qtractorMidiEditor::safeNote ( int iNote ) const { return safeValue(iNote); } int qtractorMidiEditor::safeValue ( int iValue ) const { if (iValue < 0) iValue = 0; else if (iValue > 127) iValue = 127; return iValue; } int qtractorMidiEditor::safeValue14 ( int iValue14 ) const { if (iValue14 < 0) iValue14 = 0; else if (iValue14 > 16383) iValue14 = 16383; return iValue14; } int qtractorMidiEditor::safePitchBend ( int iPitchBend ) const { if (iPitchBend < -8191) iPitchBend = -8191; else if (iPitchBend > +8191) iPitchBend = +8191; return iPitchBend; } // (Un)set edit mode cursors. void qtractorMidiEditor::setEditCursor ( const QCursor& cursr ) { m_pEditView->viewport()->setCursor(cursr); m_pEditEvent->viewport()->setCursor(cursr); } void qtractorMidiEditor::unsetEditCursor (void) { if (m_bEditMode) { setEditCursor(QCursor(QIcon::fromTheme(m_bEditModeDraw ? "editModeDraw" : "editModeOn").pixmap(22), 5, 18)); } else { m_pEditView->viewport()->unsetCursor(); m_pEditEvent->viewport()->unsetCursor(); } } // MIDI event tool tip helper. QString qtractorMidiEditor::eventToolTip ( qtractorMidiEvent *pEvent, long iTimeDelta, int iNoteDelta, int iValueDelta ) const { long d0 = 0; if (m_resizeMode == ResizeNoteRight) { d0 = iTimeDelta; iTimeDelta = 0; } else if (m_resizeMode == ResizeNoteLeft) d0 = -iTimeDelta; unsigned long t0 = m_pTimeScale->tickFromFrame(m_iOffset) + pEvent->time(); t0 = (long(t0) + iTimeDelta < 0 ? 0 : t0 + iTimeDelta); QString sToolTip = tr("Time:\t%1\nType:\t") .arg(m_pTimeScale->textFromTick(t0)); switch (pEvent->type()) { // case qtractorMidiEvent::NOTEOFF: // sToolTip += tr("Note Off (%1)").arg(int(pEvent->note())); // break; case qtractorMidiEvent::NOTEON: d0 = (long(pEvent->duration()) + d0 < 0 ? 0 : pEvent->duration() + d0); sToolTip += tr("Note On (%1) %2\nVelocity:\t%3\nDuration: %4") .arg(int(pEvent->note() + iNoteDelta)) .arg(noteName(pEvent->note() + iNoteDelta)) .arg(safeValue(pEvent->velocity() + iValueDelta)) .arg(m_pTimeScale->textFromTick(t0, true, d0)); break; case qtractorMidiEvent::KEYPRESS: sToolTip += tr("Key Press (%1) %2\nValue:\t%3") .arg(int(pEvent->note() + iNoteDelta)) .arg(noteName(pEvent->note() + iNoteDelta)) .arg(safeValue(pEvent->velocity() + iValueDelta)); break; case qtractorMidiEvent::CONTROLLER: sToolTip += tr("Controller (%1)\nName:\t%2\nValue:\t%3") .arg(int(pEvent->controller())) .arg(controllerName(pEvent->controller())) .arg(safeValue(pEvent->value() + iValueDelta)); break; case qtractorMidiEvent::REGPARAM: sToolTip += tr("RPN (%1)\nName:\t%2\nValue:\t%3") .arg(int(pEvent->param())) .arg(rpnNames().value(pEvent->param())) .arg(safeValue14(pEvent->value() + iValueDelta)); break; case qtractorMidiEvent::NONREGPARAM: sToolTip += tr("NRPN (%1)\nName:\t%2\nValue:\t%3") .arg(int(pEvent->param())) .arg(nrpnNames().value(pEvent->param())) .arg(safeValue14(pEvent->value() + iValueDelta)); break; case qtractorMidiEvent::CONTROL14: sToolTip += tr("Control 14 (%1)\nName:\t%2\nValue:\t%3") .arg(int(pEvent->controller())) .arg(control14Name(pEvent->controller())) .arg(safeValue14(pEvent->value() + iValueDelta)); break; case qtractorMidiEvent::PGMCHANGE: sToolTip += tr("Pgm Change (%1)") .arg(safeValue(pEvent->param() + iValueDelta)); break; case qtractorMidiEvent::CHANPRESS: sToolTip += tr("Chan Press (%1)") .arg(safeValue(pEvent->value() + iValueDelta)); break; case qtractorMidiEvent::PITCHBEND: sToolTip += tr("Pitch Bend (%1)") .arg(safePitchBend(pEvent->pitchBend() + iValueDelta)); break; case qtractorMidiEvent::SYSEX: { unsigned char *data = pEvent->sysex(); unsigned short len = pEvent->sysex_len(); sToolTip += tr("SysEx (%1 bytes)\nData: ").arg(int(len)); sToolTip += '{'; sToolTip += ' '; for (unsigned short i = 0; i < len; ++i) #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) sToolTip += QString().sprintf("%02x ", data[i]); #else sToolTip += QString::asprintf("%02x ", data[i]); #endif sToolTip += '}'; break; } // case qtractorMidiEvent::META: // sToolTip += tr("Meta"); // break; default: sToolTip += tr("Unknown (%1)").arg(int(pEvent->type())); break; } // That's it return sToolTip; } // Keyboard event handler (common). bool qtractorMidiEditor::keyPress ( qtractorScrollView *pScrollView, int iKey, const Qt::KeyboardModifiers& modifiers ) { switch (iKey) { case Qt::Key_Insert: // Aha, joking :) case Qt::Key_Return: if (m_dragState == DragStep) { executeDragMove(pScrollView, m_posDrag); } else { const QPoint& pos = pScrollView->viewportToContents( pScrollView->viewport()->mapFromGlobal(QCursor::pos())); if (m_dragState == DragMove) executeDragMove(pScrollView, pos); else if (m_dragState == DragPaste) executeDragPaste(pScrollView, pos); } resetDragState(pScrollView); break; case Qt::Key_Escape: m_dragState = DragStep; // HACK: Force selection clearance! m_select.clear(); resetDragState(pScrollView); break; case Qt::Key_Home: if (modifiers & Qt::ControlModifier) { pScrollView->setContentsPos(0, 0); } else { pScrollView->setContentsPos(0, pScrollView->contentsY()); } break; case Qt::Key_End: if (modifiers & Qt::ControlModifier) { pScrollView->setContentsPos( pScrollView->contentsWidth() - pScrollView->width(), pScrollView->contentsHeight() - pScrollView->height()); } else { pScrollView->setContentsPos( pScrollView->contentsWidth() - pScrollView->width(), pScrollView->contentsY()); } break; case Qt::Key_Left: if (modifiers & Qt::ControlModifier) { pScrollView->setContentsPos( pScrollView->contentsX() - pScrollView->width(), pScrollView->contentsY()); } else if (!keyStep(pScrollView, iKey, modifiers)) { pScrollView->setContentsPos( pScrollView->contentsX() - 16, pScrollView->contentsY()); } break; case Qt::Key_Right: if (modifiers & Qt::ControlModifier) { pScrollView->setContentsPos( pScrollView->contentsX() + pScrollView->width(), pScrollView->contentsY()); } else if (!keyStep(pScrollView, iKey, modifiers)) { pScrollView->setContentsPos( pScrollView->contentsX() + 16, pScrollView->contentsY()); } break; case Qt::Key_Up: if (modifiers & Qt::ControlModifier) { pScrollView->setContentsPos( pScrollView->contentsX(), pScrollView->contentsY() - pScrollView->height()); } else if (!keyStep(pScrollView, iKey, modifiers)) { pScrollView->setContentsPos( pScrollView->contentsX(), pScrollView->contentsY() - 16); } break; case Qt::Key_Down: if (modifiers & Qt::ControlModifier) { pScrollView->setContentsPos( pScrollView->contentsX(), pScrollView->contentsY() + pScrollView->height()); } else if (!keyStep(pScrollView, iKey, modifiers)) { pScrollView->setContentsPos( pScrollView->contentsX(), pScrollView->contentsY() + 16); } break; case Qt::Key_PageUp: if (modifiers & Qt::ControlModifier) { pScrollView->setContentsPos( pScrollView->contentsX(), 16); } else { pScrollView->setContentsPos( pScrollView->contentsX(), pScrollView->contentsY() - pScrollView->height()); } break; case Qt::Key_PageDown: if (modifiers & Qt::ControlModifier) { pScrollView->setContentsPos( pScrollView->contentsX(), pScrollView->contentsHeight() - pScrollView->height()); } else { pScrollView->setContentsPos( pScrollView->contentsX(), pScrollView->contentsY() + pScrollView->height()); } break; default: // Not handled here. return false; } // Done. return true; } // Keyboard step handler. bool qtractorMidiEditor::keyStep ( qtractorScrollView *pScrollView, int iKey, const Qt::KeyboardModifiers& modifiers ) { // Only applicable if something is selected... if (m_select.items().isEmpty()) return false; const bool bEditView = (static_cast (m_pEditView) == pScrollView); // Set initial bound conditions... if (m_dragState == DragNone) { m_dragState = m_dragCursor = DragStep; m_rectDrag = (bEditView ? m_select.rectView() : m_select.rectEvent()); m_posDrag = m_rectDrag.topLeft(); m_posStep = QPoint(0, 0); m_pDragStep = pScrollView; if (bEditView && m_bDrumMode) { const int h1 = m_pEditList->itemHeight(); const int h2 = (h1 >> 1); m_posDrag += QPoint(+h1, +h2); } setEditCursor(Qt::SizeAllCursor); } // Now to say the truth... if (m_dragState != DragMove && m_dragState != DragStep && m_dragState != DragPaste) return false; // Make sure we've a anchor... if (m_pEventDrag == nullptr) m_pEventDrag = m_select.anchorEvent(); // Determine vertical step... if (iKey == Qt::Key_Up || iKey == Qt::Key_Down) { const int iVerticalStep = (bEditView ? m_pEditList->itemHeight() : 1); const int y0 = m_posDrag.y(); int y1 = y0 + m_posStep.y(); if (iKey == Qt::Key_Up) y1 -= iVerticalStep; else y1 += iVerticalStep; m_posStep.setY((y1 < 0 ? 0 : y1) - y0); } else // Determine horizontal step... if (iKey == Qt::Key_Left || iKey == Qt::Key_Right) { const int x0 = m_posDrag.x() + m_pTimeScale->pixelFromFrame(m_iOffset); int iHorizontalStep = 0; int x1 = x0 + m_posStep.x(); qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekPixel(x1); if (modifiers & Qt::ShiftModifier) { iHorizontalStep = pNode->pixelsPerBeat() * pNode->beatsPerBar; } else { unsigned short iSnapPerBeat = m_pTimeScale->snapPerBeat(); if (iSnapPerBeat > 0) iHorizontalStep = pNode->pixelsPerBeat() / iSnapPerBeat; } if (iHorizontalStep < 4) iHorizontalStep = 4; if (iKey == Qt::Key_Left) x1 -= iHorizontalStep; else x1 += iHorizontalStep; m_posStep.setX(pixelSnap(x1 < 0 ? 0 : x1) - x0); } // Early sanity check... const QRect& rect = (bEditView ? m_select.rectView() : m_select.rectEvent()); QPoint pos = m_posDrag; if (m_dragState == DragMove || m_dragState == DragPaste) { pos = pScrollView->viewportToContents( pScrollView->viewport()->mapFromGlobal(QCursor::pos())); } int x2 = - pos.x(); int y2 = - pos.y(); if (m_dragState == DragMove || m_dragState == DragPaste) { x2 += (m_posDrag.x() - rect.x()); y2 += (m_posDrag.y() - rect.y()); } if (m_posStep.x() < x2) { m_posStep.setX (x2); } else { x2 += pScrollView->contentsWidth() - rect.width(); if (m_posStep.x() > x2) m_posStep.setX (x2); } if (bEditView) { if (m_posStep.y() < y2) { m_posStep.setY (y2); } else { y2 += pScrollView->contentsHeight() - rect.height(); if (m_posStep.y() > y2) m_posStep.setY (y2); } } // Do our deeds... updateDragMove(pScrollView, pos + m_posStep); return true; } // Focus lost event. void qtractorMidiEditor::focusOut ( qtractorScrollView *pScrollView ) { if (m_dragState == DragStep && m_pDragStep == pScrollView) resetDragState(pScrollView); } // Show selection tooltip... void qtractorMidiEditor::showToolTip ( qtractorScrollView *pScrollView, const QRect& rect ) const { if (pScrollView == nullptr) return; if (!m_bToolTips) return; if (m_pTimeScale == nullptr) return; const unsigned long iFrameStart = frameSnap( m_iOffset + m_pTimeScale->frameFromPixel(qMax(0, rect.left()))); const unsigned long iFrameEnd = frameSnap( m_iOffset + m_pTimeScale->frameFromPixel(qMax(0, rect.right()))); QToolTip::showText( QCursor::pos(), tr("Start:\t%1\nEnd:\t%2\nLength:\t%3") .arg(m_pTimeScale->textFromFrame(iFrameStart)) .arg(m_pTimeScale->textFromFrame(iFrameEnd)) .arg(m_pTimeScale->textFromFrame(iFrameStart, true, iFrameEnd - iFrameStart)), pScrollView->viewport()); } // Temporary sync-view/follow-playhead hold state. void qtractorMidiEditor::setSyncViewHoldOn ( bool bOn ) { m_iSyncViewHold = (m_bSyncViewHold && bOn ? QTRACTOR_SYNC_VIEW_HOLD : 0); } void qtractorMidiEditor::setSyncViewHold ( bool bSyncViewHold ) { m_bSyncViewHold = bSyncViewHold; setSyncViewHoldOn(bSyncViewHold); } bool qtractorMidiEditor::isSyncViewHold (void) const { return (m_bSyncViewHold && m_iSyncViewHold > 0); } // Return either snapped pixel, or the passed one if [Alt] key is pressed. unsigned int qtractorMidiEditor::pixelSnap ( unsigned int x ) const { if (QApplication::keyboardModifiers() & Qt::AltModifier) return x; else return (m_pTimeScale ? m_pTimeScale->pixelSnap(x) : x); } // Return either snapped frame, or the passed one if [Alt] key is pressed. unsigned long qtractorMidiEditor::frameSnap ( unsigned long iFrame ) const { if (QApplication::keyboardModifiers() & Qt::AltModifier) return iFrame; else return (m_pTimeScale ? m_pTimeScale->frameSnap(iFrame) : iFrame); } // end of qtractorMidiEditor.cpp qtractor-1.5.11/src/PaxHeaders/qtractorAudioIOMatrixForm.h0000644000000000000000000000013215124701674020545 xustar0030 mtime=1767080892.779263512 30 atime=1767080892.779263512 30 ctime=1767080892.779263512 qtractor-1.5.11/src/qtractorAudioIOMatrixForm.h0000644000175000001440000000521315124701674020536 0ustar00rncbcusers// qtractorAudioIOMatrixForm.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorAudioIOMatrixForm_h #define __qtractorAudioIOMatrixForm_h #include #include // forward decls. class QButtonGroup; //------------------------------------------------------------------------- // qtractorAudioIOMatrixForm namespace Ui { class qtractorAudioIOMatrixForm; } class qtractorAudioIOMatrixForm: public QDialog { Q_OBJECT public: qtractorAudioIOMatrixForm(QWidget *parent = nullptr); virtual ~qtractorAudioIOMatrixForm(); void setChannels(int nins, int nouts); int inputChannels() const; int outputChannels() const; void setMatrix(const QList& matrix); const QList& matrix() const; void refresh(); class TableWidget; class TableCell; class RadioButton; const QList& groups() const; protected slots: void inputChannelsChanged(int index); void outputChannelsChanged(int index); void accept(); void reject(); private: Ui::qtractorAudioIOMatrixForm *p_ui; Ui::qtractorAudioIOMatrixForm& m_ui; QList m_groups; QList m_matrix; }; //------------------------------------------------------------------------- // qtractorAudioIOMatrixForm::TableWidget class qtractorAudioIOMatrixForm::TableWidget : public QTableWidget { public: TableWidget(QWidget *parent = nullptr); void setForm(qtractorAudioIOMatrixForm *form); qtractorAudioIOMatrixForm *form() const; bool isDirty() const; protected: void mousePressEvent(QMouseEvent *event) override; void keyPressEvent(QKeyEvent *event) override; void toggleCell(int row, int col); private: qtractorAudioIOMatrixForm *m_form; int m_dirty; }; #endif // __qtractorAudioIOMatrixForm_h // end of qtractorAudioIOMatrixForm.h qtractor-1.5.11/src/PaxHeaders/qtractorTrackCommand.cpp0000644000000000000000000000013215124701674020141 xustar0030 mtime=1767080892.802263416 30 atime=1767080892.802263416 30 ctime=1767080892.802263416 qtractor-1.5.11/src/qtractorTrackCommand.cpp0000644000175000001440000011463315124701674020141 0ustar00rncbcusers// qtractorTrackCommand.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorTrackCommand.h" #include "qtractorClipCommand.h" #include "qtractorMainForm.h" #include "qtractorTracks.h" #include "qtractorTrackList.h" #include "qtractorTrackView.h" #include "qtractorAudioClip.h" #include "qtractorMidiEngine.h" #include "qtractorMidiControl.h" #include "qtractorMidiManager.h" #include "qtractorMidiClip.h" #include "qtractorMixer.h" #include "qtractorMonitor.h" #include "qtractorPlugin.h" #include "qtractorCurve.h" //---------------------------------------------------------------------- // class qtractorTrackCommand - implementation // // Constructor. qtractorTrackCommand::qtractorTrackCommand ( const QString& sName, qtractorTrack *pTrack, qtractorTrack *pAfterTrack ) : qtractorCommand(sName), m_pTrack(pTrack), m_pAfterTrack(pAfterTrack) { setClearSelectReset(true); } // Destructor. qtractorTrackCommand::~qtractorTrackCommand (void) { if (isAutoDelete()) delete m_pTrack; } // Track command methods. bool qtractorTrackCommand::addTrack (void) { #ifdef CONFIG_DEBUG qDebug("qtractorTrackCommand::addTrack(%p, %p)", m_pTrack, m_pAfterTrack); #endif if (m_pTrack == nullptr) return false; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return false; qtractorTrackList *pTrackList = pTracks->trackList(); if (pTrackList == nullptr) return false; // Guess which item we're adding after... #if 0 if (m_pAfterTrack == nullptr) m_pAfterTrack = m_pTrack->prev(); #else if (m_pAfterTrack == nullptr) m_pAfterTrack = pSession->tracks().last(); #endif int iTrack = pSession->tracks().find(m_pAfterTrack) + 1; // Link the track into session... pSession->insertTrack(m_pTrack, m_pAfterTrack); // And the new track list view item too... iTrack = pTrackList->insertTrack(iTrack, m_pTrack); // Update track-list items... m_pTrack->updateTrack(); // (Re)open all clips... qtractorClip *pClip = m_pTrack->clips().first(); for ( ; pClip; pClip = pClip->next()) pClip->open(); // Mixer turn... qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) pMixer->updateTracks(true); // Let the change get visible. pTrackList->setCurrentTrackRow(iTrack); // ATTN: MIDI controller map feedback. qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) pMidiControl->sendAllControllers(iTrack); // Avoid disposal of the track reference. setAutoDelete(false); return true; } bool qtractorTrackCommand::removeTrack (void) { #ifdef CONFIG_DEBUG qDebug("qtractorTrackCommand::removeTrack(%p, %p)", m_pTrack, m_pAfterTrack); #endif if (m_pTrack == nullptr) return false; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return false; qtractorTrackList *pTrackList = pTracks->trackList(); if (pTrackList == nullptr) return false; // Save which item we're adding after... if (m_pAfterTrack == nullptr) m_pAfterTrack = m_pTrack->prev(); #if 0 if (m_pAfterTrack == nullptr) m_pAfterTrack = pSession->tracks().last(); #endif // Get the list view item reference of the intended track... int iTrack = pSession->tracks().find(m_pTrack); if (iTrack < 0) return false; // Close all clips... qtractorClip *pClip = m_pTrack->clips().last(); for ( ; pClip; pClip = pClip->prev()) pClip->close(); // Second, remove from session... pSession->unlinkTrack(m_pTrack); // Third, remove track from list view... iTrack = pTrackList->removeTrack(iTrack); // Clear track-view clipboard whther applicable... if (qtractorTrackView::singleTrackClipboard() == nullptr) qtractorTrackView::clearClipboard(); // Mixer turn... qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) pMixer->updateTracks(true); // Let the change get visible. pTrackList->setCurrentTrackRow(iTrack); // ATTN: MIDI controller map feedback. qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) pMidiControl->sendAllControllers(iTrack); // Make ths track reference disposable. setAutoDelete(true); return true; } //---------------------------------------------------------------------- // class qtractorAddTrackCommand - implementation // // Constructor. qtractorAddTrackCommand::qtractorAddTrackCommand ( qtractorTrack *pTrack, qtractorTrack *pAfterTrack ) : qtractorTrackCommand(QObject::tr("add track"), pTrack, pAfterTrack) { } // Track insertion command methods. bool qtractorAddTrackCommand::redo (void) { return addTrack(); } bool qtractorAddTrackCommand::undo (void) { return removeTrack(); } //---------------------------------------------------------------------- // class qtractorRemoveTrackCommand - implementation // // Constructor. qtractorRemoveTrackCommand::qtractorRemoveTrackCommand ( qtractorTrack *pTrack ) : qtractorTrackCommand(QObject::tr("remove track"), pTrack, pTrack->prev()) { } // Track-removal command methods. bool qtractorRemoveTrackCommand::redo (void) { return removeTrack(); } bool qtractorRemoveTrackCommand::undo (void) { return addTrack(); } //---------------------------------------------------------------------- // class qtractorCopyTrackCommand - implementation // // Constructor. qtractorCopyTrackCommand::qtractorCopyTrackCommand ( qtractorTrack *pTrack, qtractorTrack *pAfterTrack ) : qtractorTrackCommand(QObject::tr("duplicate track"), pTrack, pAfterTrack), m_iCopyCount(0) { } // Track insertion command methods. bool qtractorCopyTrackCommand::redo (void) { const bool bResult = addTrack(); if (++m_iCopyCount > 1) return bResult; // One-time copy... qtractorTrack *pTrack = afterTrack(); qtractorTrack *pNewTrack = track(); // Reset state properties... pNewTrack->resetProperties(); // Copy all former clips depending of track type... const qtractorTrack::TrackType trackType = pTrack->trackType(); for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { switch (trackType) { case qtractorTrack::Audio: { qtractorAudioClip *pAudioClip = static_cast (pClip); if (pAudioClip) { qtractorAudioClip *pNewAudioClip = new qtractorAudioClip(*pAudioClip); pNewAudioClip->setClipStart(pAudioClip->clipStart()); pNewAudioClip->setClipOffset(pAudioClip->clipOffset()); pNewAudioClip->setClipLength(pAudioClip->clipLength()); pNewAudioClip->setFadeInLength(pAudioClip->fadeInLength()); pNewAudioClip->setFadeOutLength(pAudioClip->fadeOutLength()); pNewAudioClip->setPitchShift(pAudioClip->pitchShift()); pNewAudioClip->setTimeStretch(pAudioClip->timeStretch()); pNewTrack->addClipEx(pNewAudioClip); } break; } case qtractorTrack::Midi: { qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) { qtractorMidiClip *pNewMidiClip = new qtractorMidiClip(*pMidiClip); pNewMidiClip->setClipStart(pMidiClip->clipStart()); pNewMidiClip->setClipOffset(pMidiClip->clipOffset()); pNewMidiClip->setClipLength(pMidiClip->clipLength()); pNewMidiClip->setFadeInLength(pMidiClip->fadeInLength()); pNewMidiClip->setFadeOutLength(pMidiClip->fadeOutLength()); pNewTrack->addClipEx(pNewMidiClip); } break; } default: break; } } // Let same old statistics prevail... pNewTrack->setMidiNoteMax(pTrack->midiNoteMax()); pNewTrack->setMidiNoteMin(pTrack->midiNoteMin()); // About to copy all automation/curves... qtractorCurveList *pCurveList = pTrack->curveList(); qtractorCurveList *pNewCurveList = pNewTrack->curveList(); // About to find and set current automation/curve... qtractorCurve *pCurve, *pCurrentCurve = pTrack->currentCurve(); qtractorCurve *pNewCurve, *pNewCurrentCurve = nullptr; // Copy all former plugins and respective automation/curves... qtractorPluginList *pPluginList = pTrack->pluginList(); qtractorPluginList *pNewPluginList = pNewTrack->pluginList(); qtractorMidiManager *pMidiManager = nullptr; qtractorMidiManager *pNewMidiManager = nullptr; if (pPluginList && pNewPluginList) { pMidiManager = pPluginList->midiManager(); pNewMidiManager = pNewPluginList->midiManager(); for (qtractorPlugin *pPlugin = pPluginList->first(); pPlugin; pPlugin = pPlugin->next()) { // Copy new plugin... qtractorPlugin *pNewPlugin = pNewPluginList->copyPlugin(pPlugin); if (pNewPlugin == nullptr) continue; pNewPluginList->insertPlugin(pNewPlugin, nullptr); // Activation automation/curves... if (pCurveList == nullptr || pNewCurveList == nullptr) continue; pCurve = pPlugin->activateSubject()->curve(); if (pCurve && pCurve->list() == pCurveList) { pNewCurve = cloneCurve(pNewCurveList, pNewPlugin->activateSubject(), pCurve); if (pNewCurrentCurve == nullptr && pCurrentCurve == pCurve) pNewCurrentCurve = pNewCurve; } // Copy plugin parameters automation/curves... const qtractorPlugin::Params& params = pPlugin->params(); qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator& param_end = params.constEnd(); for ( ; param != param_end; ++param) { qtractorPlugin::Param *pParam = param.value(); pCurve = pParam->subject()->curve(); if (pCurve && pCurve->list() == pCurveList) { qtractorPlugin::Param *pNewParam = pNewPlugin->findParam(pParam->index()); if (pNewParam == nullptr) continue; pNewCurve = cloneCurve(pNewCurveList, pNewParam->subject(), pCurve); if (pNewCurrentCurve == nullptr && pCurrentCurve == pCurve) pNewCurrentCurve = pNewCurve; } } } // And other MIDI specific plugins-list properties as well if (pMidiManager && pNewMidiManager) { // The basic ones... pNewPluginList->setMidiBank( pPluginList->midiBank()); pNewPluginList->setMidiProg( pPluginList->midiProg()); pNewPluginList->setAudioOutputBusName( pPluginList->audioOutputBusName()); pNewPluginList->setAudioOutputAutoConnect( pPluginList->isAudioOutputAutoConnect()); pNewPluginList->setAudioOutputBus( pPluginList->isAudioOutputBus()); // The effective ones... pNewMidiManager->setAudioOutputBusName( pMidiManager->audioOutputBusName()); pNewMidiManager->setAudioOutputAutoConnect( pMidiManager->isAudioOutputAutoConnect()); pNewMidiManager->setAudioOutputBus( pMidiManager->isAudioOutputBus()); pNewMidiManager->setAudioOutputMonitor( pMidiManager->isAudioOutputMonitor()); } } // Copy all former plugins and respective automation... if (pCurveList && pNewCurveList) { pCurve = pTrack->monitorSubject()->curve(); if (pCurve) { pNewCurve = cloneCurve(pNewCurveList, pNewTrack->monitorSubject(), pCurve); if (pNewCurrentCurve == nullptr && pCurrentCurve == pCurve) pNewCurrentCurve = pNewCurve; } pCurve = pTrack->monitor()->panningSubject()->curve(); if (pCurve) { pNewCurve = cloneCurve(pNewCurveList, pNewTrack->monitor()->panningSubject(), pCurve); if (pNewCurrentCurve == nullptr && pCurrentCurve == pCurve) pNewCurrentCurve = pNewCurve; } pCurve = pTrack->monitor()->gainSubject()->curve(); if (pCurve) { pNewCurve = cloneCurve(pNewCurveList, pNewTrack->monitor()->gainSubject(), pCurve); if (pNewCurrentCurve == nullptr && pCurrentCurve == pCurve) pNewCurrentCurve = pNewCurve; } pCurve = pTrack->recordSubject()->curve(); if (pCurve) { pNewCurve = cloneCurve(pNewCurveList, pNewTrack->recordSubject(), pCurve); if (pNewCurrentCurve == nullptr && pCurrentCurve == pCurve) pNewCurrentCurve = pNewCurve; } pCurve = pTrack->muteSubject()->curve(); if (pCurve) { pNewCurve = cloneCurve(pNewCurveList, pNewTrack->muteSubject(), pCurve); if (pNewCurrentCurve == nullptr && pCurrentCurve == pCurve) pNewCurrentCurve = pNewCurve; } pCurve = pTrack->soloSubject()->curve(); if (pCurve) { pNewCurve = cloneCurve(pNewCurveList, pNewTrack->soloSubject(), pCurve); if (pNewCurrentCurve == nullptr && pCurrentCurve == pCurve) pNewCurrentCurve = pNewCurve; } } // Set final new track properties... if (pNewCurrentCurve) pNewTrack->setCurrentCurve(pNewCurrentCurve); // Update monitor volume/panning for new MIDI track... if (trackType == qtractorTrack::Midi) { qtractorMidiBus *pMidiBus = static_cast (pNewTrack->outputBus()); if (pMidiBus) { pMidiBus->setVolume(pNewTrack, pNewTrack->gain()); pMidiBus->setPanning(pNewTrack, pNewTrack->panning()); } } // Refresh to most recent things... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { // Meters on tracks list... qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks && pNewMidiManager) pTracks->updateMidiTrackItem(pNewMidiManager); // Meters on mixer strips... qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer && pNewMidiManager) pMixer->updateMidiManagerStrip(pNewMidiManager); if (pMixer) pMixer->updateTrackStrip(pNewTrack); } return bResult; } bool qtractorCopyTrackCommand::undo (void) { return removeTrack(); } // Clone an existing automation/curve. qtractorCurve *qtractorCopyTrackCommand::cloneCurve ( qtractorCurveList *pNewCurveList, qtractorSubject *pNewSubject, qtractorCurve *pCurve ) const { qtractorCurve *pNewCurve = new qtractorCurve(pNewCurveList, pNewSubject, pCurve->mode(), pCurve->minFrameDist()); pNewCurve->setDefaultValue(pCurve->defaultValue()); pNewCurve->setLength(pCurve->length()); pNewCurve->setCapture(pCurve->isCapture()); pNewCurve->setProcess(pCurve->isProcess()); pNewCurve->setLocked(pCurve->isLocked()); pNewCurve->setLogarithmic(pCurve->isLogarithmic()); pNewCurve->setColor(pCurve->color()); pNewCurve->copyNodes(pCurve); return pNewCurve; } //---------------------------------------------------------------------- // class qtractorMoveTrackCommand - implementation // // Constructor. qtractorMoveTrackCommand::qtractorMoveTrackCommand ( qtractorTrack *pTrack, qtractorTrack *pNextTrack ) : qtractorTrackCommand(QObject::tr("move track"), pTrack) { m_pNextTrack = pNextTrack; } // Track-move command methods. bool qtractorMoveTrackCommand::redo (void) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return false; int iTrack = pSession->tracks().find(pTrack); if (iTrack < 0) return false; // Save the next track alright... qtractorTrack *pNextTrack = pTrack->next(); // Remove and insert back again... qtractorTrackList *pTrackList = pTracks->trackList(); pTrackList->removeTrack(iTrack); // Get actual index of new position... int iNextTrack = pTrackList->trackRow(m_pNextTrack); // Make it all set back. pSession->moveTrack(pTrack, m_pNextTrack); // Just insert under the track list position... // We'll renumber all items now... iNextTrack = pTrackList->insertTrack(iNextTrack, pTrack); // Swap it nice, finally. m_pNextTrack = pNextTrack; // Mixer turn... qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) pMixer->updateTracks(true); // Make it new current track (updates mixer too)... pTrackList->setCurrentTrackRow(iNextTrack); // ATTN: MIDI controller map feedback. qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) { if (iTrack > iNextTrack) iTrack = iNextTrack; pMidiControl->sendAllControllers(iTrack); } return true; } bool qtractorMoveTrackCommand::undo (void) { // As we swap the prev/track this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorResizeTrackCommand - implementation // // Constructor. qtractorResizeTrackCommand::qtractorResizeTrackCommand ( qtractorTrack *pTrack, int iZoomHeight ) : qtractorTrackCommand(QObject::tr("resize track"), pTrack) { m_iZoomHeight = iZoomHeight; } // Track-resize command methods. bool qtractorResizeTrackCommand::redo (void) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; // Save the previous item height alright... const int iZoomHeight = pTrack->zoomHeight(); // Just set new one... pTrack->setZoomHeight(m_iZoomHeight); // Swap it nice, finally. m_iZoomHeight = iZoomHeight; // Update track list item... qtractorTrackList *pTrackList = pMainForm->tracks()->trackList(); pTrackList->updateTrack(pTrack); return true; } bool qtractorResizeTrackCommand::undo (void) { // As we swap the prev/track this is non-identpotent. return redo(); } //---------------------------------------------------------------------- // class qtractorImportTrackCommand - implementation // // Constructor. qtractorImportTrackCommand::qtractorImportTrackCommand ( qtractorTrack *pAfterTrack ) : qtractorCommand(QObject::tr("import track")), m_pAfterTrack(pAfterTrack) { // Session properties backup preparation. m_iSaveCount = 0; m_pSaveCommand = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { m_sessionProps = pSession->properties(); m_pSaveCommand = new qtractorPropertyCommand (name(), pSession->properties(), m_sessionProps); } setClearSelectReset(true); } // Destructor. qtractorImportTrackCommand::~qtractorImportTrackCommand (void) { if (m_pSaveCommand) delete m_pSaveCommand; qDeleteAll(m_trackCommands); m_trackCommands.clear(); } // Track-import list methods. void qtractorImportTrackCommand::addTrack ( qtractorTrack *pTrack ) { m_trackCommands.append( new qtractorAddTrackCommand(pTrack, m_pAfterTrack)); m_pAfterTrack = pTrack; } // Track-import command methods. bool qtractorImportTrackCommand::redo (void) { bool bResult = true; if (m_pSaveCommand && m_iSaveCount > 0) { if (!m_pSaveCommand->redo()) bResult = false; } ++m_iSaveCount; QListIterator iter(m_trackCommands); while (iter.hasNext()) { qtractorAddTrackCommand *pTrackCommand = iter.next(); if (!pTrackCommand->redo()) bResult = false; } return bResult; } bool qtractorImportTrackCommand::undo (void) { bool bResult = true; QListIterator iter(m_trackCommands); iter.toBack(); while (iter.hasPrevious()) { qtractorAddTrackCommand *pTrackCommand = iter.previous(); if (!pTrackCommand->undo()) bResult = false; } if (m_pSaveCommand && !m_pSaveCommand->undo()) bResult = false; return bResult; } //---------------------------------------------------------------------- // class qtractorEditTrackCommand - implementation // // Constructor. qtractorEditTrackCommand::qtractorEditTrackCommand ( qtractorTrack *pTrack, const qtractorTrack::Properties& props ) : qtractorPropertyCommand ( QObject::tr("track properties"), pTrack->properties(), props) { m_pTrack = pTrack; // Check whether we'll need to re-open the track... const qtractorTrack::Properties& old_props = pTrack->properties(); m_bReopen = ( old_props.inputBusName != props.inputBusName || old_props.outputBusName != props.outputBusName); m_bLatency = ( ( old_props.pluginListLatency && !props.pluginListLatency) || (!old_props.pluginListLatency && props.pluginListLatency)); } // Overridden track-edit command methods. bool qtractorEditTrackCommand::redo (void) { if (m_pTrack == nullptr) return false; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; // Howdy, maybe we're already have a name on recording... const bool bRecord = m_pTrack->isRecord(); if (bRecord) pSession->trackRecord(m_pTrack, false, 0, 0); // Release track-name from uniqueness... pSession->releaseTrackName(m_pTrack); // Make the track property change... bool bResult = qtractorPropertyCommand::redo(); // Reopen to assign a probable new bus, latency... if (bResult) { if (m_bReopen) { pSession->lock(); bResult = m_pTrack->open(); pSession->unlock(); } else if (m_bLatency && m_pTrack->pluginList()) { m_pTrack->pluginList()->setLatency( m_pTrack->isPluginListLatency()); } } // Re-acquire track-name for uniqueness... pSession->acquireTrackName(m_pTrack); if (!bResult) { pMainForm->appendMessagesError( QObject::tr("Track assignment failed:\n\n" "Track: \"%1\" Input: \"%2\" Output: \"%3\"") .arg(m_pTrack->shortTrackName()) .arg(m_pTrack->inputBusName()) .arg(m_pTrack->outputBusName())); } else // Reassign recording... if (bRecord) { unsigned long iClipStart = pSession->playHead(); if (pSession->isPunching()) { const unsigned long iPunchIn = pSession->punchIn(); if (iClipStart < iPunchIn) iClipStart = iPunchIn; } const unsigned long iFrameTime = pSession->frameTimeEx(); pSession->trackRecord(m_pTrack, true, iClipStart, iFrameTime); } // Update track-list items... m_pTrack->updateTrack(); // Special MIDI track case: update and trap dirty clips... if (m_pTrack->trackType() == qtractorTrack::Midi) m_pTrack->updateMidiClips(); // Mixer turn... qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) pMixer->updateTracks(true); // Finally update any outstanding clip editors... m_pTrack->updateClipEditors(); return bResult; } bool qtractorEditTrackCommand::undo (void) { return redo(); } //---------------------------------------------------------------------- // class qtractorTrackControlCommand - implementation. // // Constructor. qtractorTrackControlCommand::qtractorTrackControlCommand ( const QString& sName, qtractorTrack *pTrack, bool bMidiControl ) : qtractorTrackCommand(sName, pTrack), m_bMidiControl(bMidiControl), m_iMidiControlFeedback(0) { } // Primitive control predicate. bool qtractorTrackControlCommand::midiControlFeedback (void) { return (m_bMidiControl ? (++m_iMidiControlFeedback > 1) : true); } //---------------------------------------------------------------------- // class qtractorTrackStateCommand - implementation. // // Constructor. qtractorTrackStateCommand::qtractorTrackStateCommand ( qtractorTrack *pTrack, qtractorTrack::ToolType toolType, bool bOn, bool bMidiControl ) : qtractorTrackControlCommand(QString(), pTrack, bMidiControl) { m_toolType = toolType; m_bOn = bOn; m_pClipCommand = nullptr; m_iRecordCount = 0; switch (m_toolType) { case qtractorTrack::Record: qtractorTrackCommand::setName(QObject::tr("track record")); break; case qtractorTrack::Mute: qtractorTrackCommand::setName(QObject::tr("track mute")); break; case qtractorTrack::Solo: qtractorTrackCommand::setName(QObject::tr("track solo")); break; } // Toggle/update all othera? qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) { const Qt::KeyboardModifiers& modifiers = QApplication::keyboardModifiers(); const bool bAllTracks = (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)); if (modifiers & Qt::ControlModifier) bOn = !bOn; const QList& tracks = pTracks->trackList()->selectedTracks(track(), bAllTracks); QListIterator iter(tracks); while (iter.hasNext()) m_tracks.append(new TrackItem(iter.next(), bOn)); } } setRefresh(m_toolType != qtractorTrack::Record); } // Destructor. qtractorTrackStateCommand::~qtractorTrackStateCommand (void) { if (m_pClipCommand) delete m_pClipCommand; qDeleteAll(m_tracks); } // Track-button command method. bool qtractorTrackStateCommand::redo (void) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; bool bOn = false; qtractorMmcEvent::SubCommand scmd = qtractorMmcEvent::TRACK_NONE; qtractorMidiControl::Command ccmd = qtractorMidiControl::Command(0); switch (m_toolType) { case qtractorTrack::Record: scmd = qtractorMmcEvent::TRACK_RECORD; ccmd = qtractorMidiControl::TRACK_RECORD; // Special stuffing if currently recording in first place... bOn = pTrack->isRecord(); if (bOn && !m_bOn && m_pClipCommand == nullptr && m_iRecordCount == 0) { m_pClipCommand = new qtractorClipCommand(QString()); // Do all the record stuffing here... const unsigned long iFrameTime = pSession->frameTimeEx(); if (m_pClipCommand->addClipRecord(pTrack, iFrameTime)) { // Yes, we've recorded something... setRefresh(true); } else { // nothing was actually recorded... delete m_pClipCommand; m_pClipCommand = nullptr; } } // Was it before (skip undos)? if (m_pClipCommand && (m_iRecordCount % 2) == 0) m_pClipCommand->redo(); ++m_iRecordCount; // Carry on... pTrack->setRecord(m_bOn); break; case qtractorTrack::Mute: scmd = qtractorMmcEvent::TRACK_MUTE; ccmd = qtractorMidiControl::TRACK_MUTE; bOn = pTrack->isMute(); pTrack->setMute(m_bOn); break; case qtractorTrack::Solo: scmd = qtractorMmcEvent::TRACK_SOLO; ccmd = qtractorMidiControl::TRACK_SOLO; bOn = pTrack->isSolo(); pTrack->setSolo(m_bOn); break; default: // Whaa? return false; } // Send MMC MASKED_WRITE command... qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); int iTrack = pSession->tracks().find(pTrack); if (pMidiEngine) pMidiEngine->sendMmcMaskedWrite(scmd, iTrack, m_bOn); // Send MIDI controller command... if (midiControlFeedback()) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) pMidiControl->processTrackCommand(ccmd, iTrack, m_bOn); } // Update track list item... qtractorTrackList *pTrackList = pMainForm->tracks()->trackList(); pTrackList->updateTrack(pTrack); // Reset for undo. m_bOn = bOn; // Toggle/update all other? if (!m_tracks.isEmpty()) { // Exclusive mode. qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); QListIterator iter(m_tracks); while (iter.hasNext()) { TrackItem *pTrackItem = iter.next(); pTrack = pTrackItem->track; bOn = false; switch (m_toolType) { case qtractorTrack::Record: bOn = pTrack->isRecord(); pTrack->setRecord(pTrackItem->on); break; case qtractorTrack::Mute: bOn = pTrack->isMute(); pTrack->setMute(pTrackItem->on); break; case qtractorTrack::Solo: bOn = pTrack->isSolo(); pTrack->setSolo(pTrackItem->on); break; } // Send MMC MASKED_WRITE command... iTrack = pTrackList->trackRow(pTrack); if (pMidiEngine) pMidiEngine->sendMmcMaskedWrite(scmd, iTrack, pTrackItem->on); // Send MIDI controller command... if (pMidiControl) pMidiControl->processTrackCommand(ccmd, iTrack, pTrackItem->on); // Update track list item... pTrackList->updateTrack(pTrack); // Swap for undo... pTrackItem->on = bOn; } // Done with exclusive mode. } return true; } bool qtractorTrackStateCommand::undo (void) { if (m_pClipCommand) m_pClipCommand->undo(); return redo(); } //---------------------------------------------------------------------- // class qtractorTrackMonitorCommand - implementation. // // Constructor. qtractorTrackMonitorCommand::qtractorTrackMonitorCommand ( qtractorTrack *pTrack, bool bMonitor, bool bMidiControl ) : qtractorTrackControlCommand( QObject::tr("track monitor"), pTrack, bMidiControl) { m_bMonitor = bMonitor; // Toggle/update all other? qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) { const Qt::KeyboardModifiers& modifiers = QApplication::keyboardModifiers(); const bool bAllTracks = (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)); if (modifiers & Qt::ControlModifier) bMonitor = !bMonitor; const QList& tracks = pTracks->trackList()->selectedTracks(track(), bAllTracks); QListIterator iter(tracks); while (iter.hasNext()) m_tracks.append(new TrackItem(iter.next(), bMonitor)); } } setRefresh(false); } // Destructor. qtractorTrackMonitorCommand::~qtractorTrackMonitorCommand (void) { qDeleteAll(m_tracks); } // Track-monitor command method. bool qtractorTrackMonitorCommand::redo (void) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; // Save undo value... bool bMonitor = pTrack->isMonitor(); // Set track monitoring... pTrack->setMonitor(m_bMonitor); // Send MMC MASKED_WRITE command... qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); const int iTrack = pSession->tracks().find(pTrack); if (pMidiEngine) { pMidiEngine->sendMmcMaskedWrite( qtractorMmcEvent::TRACK_MONITOR, iTrack, m_bMonitor); } // Send MIDI controller command(s)... if (midiControlFeedback()) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) { pMidiControl->processTrackCommand( qtractorMidiControl::TRACK_MONITOR, iTrack, m_bMonitor); } } // Set undo value... m_bMonitor = bMonitor; // Toggle/update all other? if (!m_tracks.isEmpty()) { // Exclusive mode. qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); QListIterator iter(m_tracks); while (iter.hasNext()) { TrackItem *pTrackItem = iter.next(); pTrack = pTrackItem->track; bMonitor = pTrack->isMonitor(); pTrack->setMonitor(pTrackItem->on); // Send MIDI controller command... if (pMidiControl) { const int iTrack = pSession->tracks().find(pTrack); pMidiControl->processTrackCommand( qtractorMidiControl::TRACK_MONITOR, iTrack, pTrackItem->on); } // Swap for undo... pTrackItem->on = bMonitor; } // Done with exclusive mode. } return true; } //---------------------------------------------------------------------- // class qtractorTrackGainCommand - implementation. // // Constructor. qtractorTrackGainCommand::qtractorTrackGainCommand ( qtractorTrack *pTrack, float fGain, bool bMidiControl ) : qtractorTrackControlCommand( QObject::tr("track gain"), pTrack, bMidiControl) { m_fGain = fGain; m_fPrevGain = pTrack->prevGain(); setRefresh(false); // Try replacing an previously equivalent command... static qtractorTrackGainCommand *s_pPrevGainCommand = nullptr; if (s_pPrevGainCommand) { qtractorSession *pSession = qtractorSession::getInstance(); qtractorCommand *pLastCommand = (pSession->commands())->lastCommand(); qtractorCommand *pPrevCommand = static_cast (s_pPrevGainCommand); if (pPrevCommand == pLastCommand && s_pPrevGainCommand->track() == pTrack) { qtractorTrackGainCommand *pLastGainCommand = static_cast (pLastCommand); if (pLastGainCommand) { // Equivalence means same (sign) direction too... const float fPrevGain = pLastGainCommand->prevGain(); const float fLastGain = pLastGainCommand->gain(); const int iPrevSign = (fPrevGain > fLastGain ? +1 : -1); const int iCurrSign = (fPrevGain < m_fGain ? +1 : -1); if (iPrevSign == iCurrSign || m_fGain == m_fPrevGain) { m_fPrevGain = fLastGain; (pSession->commands())->removeLastCommand(); } } } } s_pPrevGainCommand = this; } // Track-gain command method. bool qtractorTrackGainCommand::redo (void) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; // Set undo value... const float fGain = m_fPrevGain; // Set track gain (respective monitor gets set too...) pTrack->setGain(m_fGain); #if 0 // MIDI tracks are special... if (pTrack->trackType() == qtractorTrack::Midi) { // Gotta make sure we've a proper MIDI bus... qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus) pMidiBus->setVolume(pTrack, m_fGain); } #endif // Send MIDI controller command(s)... if (midiControlFeedback()) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) { const int iTrack = pSession->tracks().find(pTrack); pMidiControl->processTrackCommand( qtractorMidiControl::TRACK_GAIN, iTrack, m_fGain, pTrack->trackType() == qtractorTrack::Audio); } } // Set undo value... m_fPrevGain = m_fGain; m_fGain = fGain; return true; } //---------------------------------------------------------------------- // class qtractorTrackPanningCommand - implementation. // // Constructor. qtractorTrackPanningCommand::qtractorTrackPanningCommand ( qtractorTrack *pTrack, float fPanning, bool bMidiControl ) : qtractorTrackControlCommand( QObject::tr("track pan"), pTrack, bMidiControl) { m_fPanning = fPanning; m_fPrevPanning = pTrack->prevPanning(); setRefresh(false); // Try replacing an previously equivalent command... static qtractorTrackPanningCommand *s_pPrevPanningCommand = nullptr; if (s_pPrevPanningCommand) { qtractorSession *pSession = qtractorSession::getInstance(); qtractorCommand *pLastCommand = (pSession->commands())->lastCommand(); qtractorCommand *pPrevCommand = static_cast (s_pPrevPanningCommand); if (pPrevCommand == pLastCommand && s_pPrevPanningCommand->track() == pTrack) { qtractorTrackPanningCommand *pLastPanningCommand = static_cast (pLastCommand); if (pLastPanningCommand) { // Equivalence means same (sign) direction too... const float fPrevPanning = pLastPanningCommand->prevPanning(); const float fLastPanning = pLastPanningCommand->panning(); const int iPrevSign = (fPrevPanning > fLastPanning ? +1 : -1); const int iCurrSign = (fPrevPanning < m_fPanning ? +1 : -1); if (iPrevSign == iCurrSign || m_fPanning == m_fPrevPanning) { m_fPrevPanning = fLastPanning; (pSession->commands())->removeLastCommand(); } } } } s_pPrevPanningCommand = this; } // Track-panning command method. bool qtractorTrackPanningCommand::redo (void) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; // Set undo value... const float fPanning = m_fPrevPanning; // Set track panning (respective monitor gets set too...) pTrack->setPanning(m_fPanning); // MIDI tracks are special... if (pTrack->trackType() == qtractorTrack::Midi) { // Gotta make sure we've a proper MIDI bus... qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus) pMidiBus->setPanning(pTrack, m_fPanning); } // Send MIDI controller command(s)... if (midiControlFeedback()) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) { const int iTrack = pSession->tracks().find(pTrack); pMidiControl->processTrackCommand( qtractorMidiControl::TRACK_PANNING, iTrack, m_fPanning); } } // Set undo value... m_fPrevPanning = m_fPanning; m_fPanning = fPanning; return true; } //---------------------------------------------------------------------- // class qtractorTrackstrumentCommand - implementation. // // Constructor. qtractorTrackInstrumentCommand::qtractorTrackInstrumentCommand ( qtractorTrack *pTrack, const QString& sInstrumentName, int iBank, int iProg ) : qtractorTrackCommand(QObject::tr("track instrument"), pTrack), m_sInstrumentName(sInstrumentName), m_iBank(iBank), m_iProg(iProg) { setRefresh(false); } // Track-instrument command method. bool qtractorTrackInstrumentCommand::redo (void) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; if (pTrack->trackType() != qtractorTrack::Midi) return false; // Gotta make sure we've a proper MIDI bus... qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; // Set undo values... const unsigned short iChannel = pTrack->midiChannel(); const qtractorMidiBus::Patch& patch = pMidiBus->patch(iChannel); QString sInstrumentName = patch.instrumentName; if (sInstrumentName.isEmpty()) sInstrumentName = pMidiBus->instrumentName(); int iBankSelMethod = pTrack->midiBankSelMethod(); if (iBankSelMethod < 0) iBankSelMethod = patch.bankSelMethod; const int iBank = pTrack->midiBank(); const int iProg = pTrack->midiProg(); // Set instrument patch... pMidiBus->setPatch(iChannel, m_sInstrumentName, iBankSelMethod, m_iBank, m_iProg, pTrack); // Set track instrument... pTrack->setMidiBankSelMethod(iBankSelMethod); pTrack->setMidiBank(m_iBank); pTrack->setMidiProg(m_iProg); pTrack->updateMidiTrack(); pTrack->updateMidiClips(); // Reset undo values... m_sInstrumentName = sInstrumentName; m_iBank = iBank; m_iProg = iProg; // Refresh to most recent things... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) pMixer->updateTrackStrip(pTrack); } return true; } // end of qtractorTrackCommand.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiControlPlugin.cpp0000644000000000000000000000013215124701674021200 xustar0030 mtime=1767080892.790263466 30 atime=1767080892.790263466 30 ctime=1767080892.790263466 qtractor-1.5.11/src/qtractorMidiControlPlugin.cpp0000644000175000001440000004104515124701674021174 0ustar00rncbcusers// qtractorMidiControlPlugin.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorMidiControlPlugin.h" #include "qtractorSession.h" #include "qtractorMidiEngine.h" #include "qtractorCurve.h" // Config keys. static const char *ControlTypeKey = "ControlType"; static const char *ControlParamKey = "ControlParam"; static const char *ControlChannelKey = "ControlChannel"; static const char *ControlLogarithmicKey = "ControlLogarithmic"; static const char *ControlInvertKey = "ControlInvert"; static const char *ControlBipolarKey = "ControlBipolar"; static const char *ControlAutoConnectKey = "ControlAutoConnect"; static const char *ControlSendKey = "ControlSend"; //---------------------------------------------------------------------------- // qtractorMidiControlPluginType -- Insert pseudo-plugin type impl. // // Factory method (static) qtractorPlugin *qtractorMidiControlPluginType::createPlugin ( qtractorPluginList *pList ) { qtractorPlugin *pPlugin = nullptr; qtractorMidiControlPluginType *pMidiControlType = new qtractorMidiControlPluginType(); if (pMidiControlType->open()) pPlugin = new qtractorMidiControlPlugin(pList, pMidiControlType); return pPlugin; } // Derived methods. bool qtractorMidiControlPluginType::open (void) { // Sanity check... const unsigned short iChannels = index(); if (iChannels > 0) return false; #ifdef CONFIG_DEBUG qDebug("qtractorMidiControlPluginType[%p]::open() channels=%u", this, iChannels); #endif // Pseudo-plugin type names. m_sName = QObject::tr("Control (MIDI)"); m_sLabel = "MidiControl"; // m_sLabel.remove(' '); // Pseudo-plugin unique identifier. m_iUniqueID = qHash(m_sLabel);//^ qHash(iChannels); // Pseudo-plugin port counts... m_iControlIns = 1; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; m_iMidiIns = 0; m_iMidiOuts = 0; // Cache flags. m_bRealtime = true; m_bConfigure = true; // Done. return true; } void qtractorMidiControlPluginType::close (void) { } // Instance cached-deferred accessors. const QString& qtractorMidiControlPluginType::aboutText (void) { if (m_sAboutText.isEmpty()) { m_sAboutText += QObject::tr("MIDI Controller Send pseudo-plugin"); m_sAboutText += '\n'; m_sAboutText += QTRACTOR_WEBSITE; m_sAboutText += '\n'; m_sAboutText += QTRACTOR_COPYRIGHT; } return m_sAboutText; } //---------------------------------------------------------------------------- // qtractorMidiControlPlugin -- MIDI Controller pseudo-plugin instance. // // Constructors. qtractorMidiControlPlugin::qtractorMidiControlPlugin ( qtractorPluginList *pList, qtractorMidiControlPluginType *pMidiControlType ) : qtractorPlugin(pList, pMidiControlType), m_pMidiBus(nullptr), m_controlType(qtractorMidiEvent::CONTROLLER), m_iControlParam(0), m_iControlChannel(0), m_bControlLogarithmic(false), m_bControlInvert(false), m_bControlBipolar(false), m_bControlAutoConnect(false) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiControlPlugin[%p] channels=%u", this, pMidiControlType->channels()); #endif // Create and attach the custom parameters... m_pControlValueParam = new Param(this, 0); m_pControlValueParam->setName(QObject::tr("Value")); m_pControlValueParam->setMinValue(0.0f); m_pControlValueParam->setMaxValue(1.0f); m_pControlValueParam->setDefaultValue(0.0f); m_pControlValueParam->setValue(0.0f, false); addParam(m_pControlValueParam); // Setup plugin instance... //setChannels(channels()); } // Destructor. qtractorMidiControlPlugin::~qtractorMidiControlPlugin (void) { // Cleanup plugin instance... setChannels(0); } // Channel/instance number accessors. void qtractorMidiControlPlugin::setChannels ( unsigned short iChannels ) { // Check our type... qtractorPluginType *pType = type(); if (pType == nullptr) return; // We'll need this globals... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; // Estimate the (new) number of instances... const unsigned short iInstances = pType->instances(iChannels, list()->isMidi()); // Now see if instance and channel count changed anyhow... if (iInstances == instances() && iChannels == channels()) return; // Gotta go for a while... const bool bActivated = isActivated(); setChannelsActivated(iChannels, false); // Cleanup bus... if (m_pMidiBus) { pMidiEngine->removeBusEx(m_pMidiBus); m_pMidiBus->close(); delete m_pMidiBus; m_pMidiBus = nullptr; } // Set new instance number... setInstances(iInstances); if (iInstances < 1) { // setChannelsActivated(iChannels, bActivated); return; } #ifdef CONFIG_DEBUG qDebug("qtractorMidiControlPlugin[%p]::setChannels(%u) instances=%u", this, iChannels, iInstances); #endif // MIDI bus name -- it must be unique... int iBusName = 1; const QString sBusNamePrefix("Control_%1"); QString sBusName = label(); // HACK: take previously saved label. if (sBusName.isEmpty() || sBusName == pType->label()) sBusName = sBusNamePrefix.arg(iBusName); while (pMidiEngine->findBus(sBusName) || pMidiEngine->findBusEx(sBusName)) sBusName = sBusNamePrefix.arg(++iBusName); setLabel(sBusName); // HACK: remember this label. // Create the private audio bus... m_pMidiBus = new qtractorMidiBus(pMidiEngine, sBusName, qtractorBus::BusMode(qtractorBus::Output | qtractorBus::Ex), false); // Add this one to the engine's exo-bus list, // for conection persistence purposes... pMidiEngine->addBusEx(m_pMidiBus); // (Re)issue all configuration as needed... realizeConfigs(); realizeValues(); // But won't need it anymore. releaseConfigs(); releaseValues(); // Open-up private bus... m_pMidiBus->open(); // (Re)activate instance if necessary... setChannelsActivated(iChannels, bActivated); } // Do the actual activation. void qtractorMidiControlPlugin::activate (void) { } // Do the actual deactivation. void qtractorMidiControlPlugin::deactivate (void) { } // The main plugin processing procedure. void qtractorMidiControlPlugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { qtractorPlugin::process(ppIBuffer, ppOBuffer, nframes); } // Pseudo-plugin configuration handlers. void qtractorMidiControlPlugin::configure ( const QString& sKey, const QString& sValue ) { if (m_pMidiBus == nullptr) return; if (sKey == ControlTypeKey) { setControlType(qtractorMidiControl::typeFromText(sValue)); return; } if (sKey == ControlParamKey) { setControlParam(sValue.toUShort()); return; } if (sKey == ControlChannelKey) { setControlChannel(sValue.toUShort()); return; } if (sKey == ControlLogarithmicKey) { setControlLogarithmic(sValue.toInt() > 0); return; } if (sKey == ControlInvertKey) { setControlInvert(sValue.toInt() > 0); return; } if (sKey == ControlBipolarKey) { setControlBipolar(sValue.toInt() > 0); return; } if (sKey == ControlAutoConnectKey) { setControlAutoConnect(sValue.toInt() > 0); return; } qtractorBus::ConnectItem *pItem = new qtractorBus::ConnectItem; pItem->index = sValue.section('|', 0, 0).toUShort(); const QString& sClient = sValue.section('|', 1, 1); const QString& sClientName = sClient.section(':', 1); if (sClientName.isEmpty()) { pItem->clientName = sClient; } else { pItem->client = sClient.section(':', 0, 0).toInt(); pItem->clientName = sClientName; } const QString& sPort = sValue.section('|', 2, 2); const QString& sPortName = sPort.section(':', 1); if (sPortName.isEmpty()) { pItem->portName = sPort; } else { pItem->port = sPort.section(':', 0, 0).toInt(); pItem->portName = sPortName; } const QString& sKeyPrefix = sKey.section('_', 0, 0); if (sKeyPrefix == ControlSendKey) m_pMidiBus->outputs().append(pItem); else delete pItem; } // Pseudo-plugin configuration/state snapshot. void qtractorMidiControlPlugin::freezeConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiControlPlugin[%p]::freezeConfigs()", this); #endif clearConfigs(); setConfig(ControlTypeKey, qtractorMidiControl::textFromType(m_controlType)); setConfig(ControlParamKey, QString::number(int(m_iControlParam))); setConfig(ControlChannelKey, QString::number(int(m_iControlChannel))); setConfig(ControlLogarithmicKey, QString::number(int(m_bControlLogarithmic))); setConfig(ControlInvertKey, QString::number(int(m_bControlInvert))); setConfig(ControlBipolarKey, QString::number(int(m_bControlBipolar))); setConfig(ControlAutoConnectKey, QString::number(int(m_bControlAutoConnect))); if (m_pMidiBus == nullptr) return; // Save connect items... const QString sKeyPrefix(ControlSendKey); int iKey = 0; qtractorBus::ConnectList connects; m_pMidiBus->updateConnects(qtractorBus::Output, connects); QListIterator iter(connects); while (iter.hasNext()) { qtractorBus::ConnectItem *pItem = iter.next(); QString sIndex = QString::number(pItem->index); QString sClient; if (pItem->client >= 0) sClient += QString::number(pItem->client) + ':'; sClient += pItem->clientName; QString sPort; if (pItem->port >= 0) sPort += QString::number(pItem->port) + ':'; sPort += pItem->portName; QString sKey = sKeyPrefix + '_' + QString::number(iKey++); setConfig(sKey, sIndex + '|' + sClient + '|' + sPort); } } void qtractorMidiControlPlugin::releaseConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiControlPlugin[%p]::releaseConfigs()", this); #endif qtractorPlugin::clearConfigs(); } // MIDI specific accessors. qtractorMidiBus *qtractorMidiControlPlugin::midiBus (void) const { return m_pMidiBus; } // Accessors. void qtractorMidiControlPlugin::setControlType ( qtractorMidiControl::ControlType ctype ) { m_controlType = ctype; } qtractorMidiControl::ControlType qtractorMidiControlPlugin::controlType (void) const { return m_controlType; } void qtractorMidiControlPlugin::setControlParam ( unsigned short iParam ) { m_iControlParam = iParam; } unsigned short qtractorMidiControlPlugin::controlParam (void) const { return m_iControlParam; } void qtractorMidiControlPlugin::setControlChannel ( unsigned short iChannel ) { m_iControlChannel = iChannel; } unsigned short qtractorMidiControlPlugin::controlChannel (void) const { return m_iControlChannel; } void qtractorMidiControlPlugin::setControlLogarithmic ( bool bLogarithmic ) { m_bControlLogarithmic = bLogarithmic; } bool qtractorMidiControlPlugin::isControlLogarithmic (void) const { return m_bControlLogarithmic; } void qtractorMidiControlPlugin::setControlInvert ( bool bInvert ) { m_bControlInvert = bInvert; } bool qtractorMidiControlPlugin::isControlInvert (void) const { return m_bControlInvert; } void qtractorMidiControlPlugin::setControlBipolar ( bool bBipolar ) { m_bControlBipolar = bBipolar; updateControlBipolar(); } bool qtractorMidiControlPlugin::isControlBipolar (void) const { return m_bControlBipolar; } void qtractorMidiControlPlugin::updateControlBipolar (void) { const float fOldMinValue = m_pControlValueParam->minValue(); const float fNewMinValue = (m_bControlBipolar ? -1.0f : 0.0f); if (qAbs(fOldMinValue - fNewMinValue) > 0.0f) { const float fValue = m_pControlValueParam->value(); m_pControlValueParam->setMinValue(fNewMinValue); m_pControlValueParam->setValue(fValue, true); qtractorCurve *pCurve = m_pControlValueParam->subject()->curve(); if (pCurve) { qtractorCurve::Node *pNode = pCurve->nodes().first(); while (pNode) { const float fValue = pNode->value; if (fNewMinValue < 0.0f) pNode->value = 2.0f * (fValue - 0.5f); else pNode->value = 0.5f * (fValue + 1.0f); pNode = pNode->next(); } pCurve->update(); } } } void qtractorMidiControlPlugin::setControlAutoConnect ( bool bAutoConnect ) { m_bControlAutoConnect = bAutoConnect; updateControlAutoConnect(); } bool qtractorMidiControlPlugin::isControlAutoConnect (void) const { return m_bControlAutoConnect; } void qtractorMidiControlPlugin::updateControlAutoConnect (void) { if (m_pMidiBus == nullptr) return; // We'll need this globals... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; qtractorMidiBus *pIControlBus = nullptr; if (pMidiEngine->isControlBus()) pIControlBus = pMidiEngine->controlBus_in(); if (pIControlBus == nullptr) { for (qtractorBus *pBus = pMidiEngine->buses().first(); pBus; pBus = pBus->next()) { if (pBus->busMode() & qtractorBus::Input) { pIControlBus = static_cast (pBus); break; } } } if (pIControlBus == nullptr) return; snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; snd_seq_port_subscribe_t *pAlsaSubs; snd_seq_addr_t seq_addr; snd_seq_port_subscribe_alloca(&pAlsaSubs); seq_addr.client = pMidiEngine->alsaClient(); seq_addr.port = m_pMidiBus->alsaPort(); snd_seq_port_subscribe_set_sender(pAlsaSubs, &seq_addr); seq_addr.client = pMidiEngine->alsaClient(); seq_addr.port = pIControlBus->alsaPort(); snd_seq_port_subscribe_set_dest(pAlsaSubs, &seq_addr); if (m_bControlAutoConnect) snd_seq_subscribe_port(pAlsaSeq, pAlsaSubs); else snd_seq_unsubscribe_port(pAlsaSeq, pAlsaSubs); } // Override title/name caption. QString qtractorMidiControlPlugin::title (void) const { QString sTitle; if (m_pMidiBus && qtractorPlugin::alias().isEmpty()) { sTitle = QObject::tr("%1 (MIDI)").arg(m_pMidiBus->busName()); sTitle.replace('_', ' '); } else { sTitle = qtractorPlugin::title(); } return sTitle; } //---------------------------------------------------------------------------- // qtractorMidiControlPlugin::Param -- MIDI Controller plugin control input port. // // Logarithmic range hint predicate method. bool qtractorMidiControlPlugin::Param::isLogarithmic (void) const { qtractorMidiControlPlugin *pMidiControlPlugin = static_cast (plugin()); if (pMidiControlPlugin) return pMidiControlPlugin->isControlLogarithmic(); else return false; } // Parameter update method (virtual). void qtractorMidiControlPlugin::updateParam ( qtractorPlugin::Param *pParam, float fValue, bool bUpdate ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiControlPlugin::Param::updateParam(%p, %g, %d)", pParam, fValue, int(bUpdate)); #endif qtractorPlugin::updateParam(pParam, fValue, bUpdate); qtractorMidiControlPlugin::Param *pControlValueParam = static_cast (pParam); if (pControlValueParam == m_pControlValueParam) { if (m_pMidiBus && isActivated()) { const qtractorMidiControl::ControlType ctype = controlType(); const unsigned short iChannel = controlChannel(); const unsigned short iParam = controlParam(); const bool bLogarithmic = isControlLogarithmic(); const bool bInvert = isControlInvert(); const bool bBipolar = isControlBipolar(); unsigned short iScale = 0x7f; if (ctype == qtractorMidiEvent::PITCHBEND || ctype == qtractorMidiEvent::CONTROL14 || ctype == qtractorMidiEvent::REGPARAM || ctype == qtractorMidiEvent::NONREGPARAM) iScale = 0x3fff; float fScale = (bBipolar ? 0.5f * (fValue + 1.0f) : fValue); if (bLogarithmic) { if (bBipolar) { if (fScale > 0.5f) { fScale = 2.0f * (fScale - 0.5f); fScale = 0.5f + 0.5f * ::cbrtf(fScale); } else { fScale = 2.0f * (0.5f - fScale); fScale = 0.5f - 0.5f * ::cbrtf(fScale); } } else { fScale = ::cbrtf(fScale); } } unsigned short iValue = ::rintf(float(iScale) * fScale); if (iValue > iScale) iValue = iScale; if (bInvert) iValue = (iScale - iValue); m_pMidiBus->sendEvent(ctype, iChannel, iParam, iValue); } } } // end of qtractorMidiControlPlugin.cpp qtractor-1.5.11/src/PaxHeaders/qtractorRubberBand.cpp0000644000000000000000000000013215124701674017604 xustar0030 mtime=1767080892.799263428 30 atime=1767080892.799263428 30 ctime=1767080892.799263428 qtractor-1.5.11/src/qtractorRubberBand.cpp0000644000175000001440000000447115124701674017602 0ustar00rncbcusers// qtractorRubberBand.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorRubberBand.h" #include //---------------------------------------------------------------------------- // qtractorRubberBand -- Custom rubber-band widget. // Constructor. qtractorRubberBand::qtractorRubberBand ( Shape shape, QWidget *widget, int thick ) : QRubberBand(shape, widget) { m_pStyle = new qtractorRubberBand::Style(thick); setStyle(m_pStyle); } // Destructor. qtractorRubberBand::~qtractorRubberBand (void) { delete m_pStyle; } // Rubberband thickness accessor. void qtractorRubberBand::setThickness ( int thick ) { m_pStyle->thickness = thick; } int qtractorRubberBand::thickness (void) const { return m_pStyle->thickness; } // Custom virtual override. int qtractorRubberBand::Style::styleHint ( StyleHint sh, const QStyleOption *opt, const QWidget *widget, QStyleHintReturn *hint ) const { const int ret = QCommonStyle::styleHint(sh, opt, widget, hint); if (sh == QStyle::SH_RubberBand_Mask) { QStyleHintReturnMask *mask = qstyleoption_cast (hint); QRect rect(mask->region.boundingRect()); const QRegion regn(rect); rect.setX(rect.x() + thickness); rect.setY(rect.y() + thickness); rect.setWidth(rect.width() - thickness); rect.setHeight(rect.height() - thickness); mask->region = regn.subtracted(QRegion(rect)); } return ret; } // end of qtractorRubberBand.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiToolsForm.h0000644000000000000000000000013215124701674017772 xustar0030 mtime=1767080892.795263445 30 atime=1767080892.795263445 30 ctime=1767080892.795263445 qtractor-1.5.11/src/qtractorMidiToolsForm.h0000644000175000001440000000547415124701674017774 0ustar00rncbcusers// qtractorMidiToolsForm.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorMidiToolsForm_h #define __qtractorMidiToolsForm_h #include "ui_qtractorMidiToolsForm.h" #include // Forward declarations. class qtractorMidiClip; class qtractorMidiEditSelect; class qtractorMidiEditCommand; class qtractorTimeScale; class qtractorTimeScaleNodeCommand; //---------------------------------------------------------------------------- // qtractorMidiToolsForm -- UI wrapper form. class qtractorMidiToolsForm : public QDialog { Q_OBJECT public: // Constructor. qtractorMidiToolsForm(QWidget *pParent = nullptr); // Destructor. ~qtractorMidiToolsForm(); // Tool page accessors. void setToolIndex(int iToolIndex); int toolIndex() const; // Create edit command based on given selection. qtractorMidiEditCommand *midiEditCommand(qtractorMidiClip *pMidiClip, qtractorMidiEditSelect *pSelect, unsigned long iTimeOffset, unsigned long iTimeStart = 0, unsigned long iTimeEnd = 0); // Special tempo ramp tool helper... qtractorTimeScaleNodeCommand *timeScaleNodeCommand(); protected slots: // Preset management slots... void presetChanged(const QString& sPreset); void presetActivated(int iPreset); void presetSave(); void presetDelete(); void timeshiftSpinBoxChanged(double p); void timeshiftSliderChanged(int i); void formatChanged(int); void changed(); void accept(); void reject(); void stabilizeForm(); protected: // Preset management methods... void loadPreset(const QString& sPreset); void savePreset(const QString& sPreset); void refreshPresets(); private: // The Qt-designer UI struct... Ui::qtractorMidiToolsForm m_ui; // Instance variables... qtractorTimeScale *m_pTimeScale; int m_iDirtyCount; int m_iUpdate; class TimeshiftCurve *m_pTimeshiftCurve; QList m_timeScaleNodeCommands; }; #endif // __qtractorMidiToolsForm_h // end of qtractorMidiToolsForm.h qtractor-1.5.11/src/PaxHeaders/qtractorDssiPlugin.cpp0000644000000000000000000000013215124701674017657 xustar0030 mtime=1767080892.783263496 30 atime=1767080892.783263496 30 ctime=1767080892.783263496 qtractor-1.5.11/src/qtractorDssiPlugin.cpp0000644000175000001440000010520215124701674017647 0ustar00rncbcusers// qtractorDssiPlugin.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #ifdef CONFIG_DSSI #include "qtractorDssiPlugin.h" #include "qtractorPluginForm.h" #include "qtractorPluginCommand.h" #include "qtractorMidiManager.h" #include "qtractorSession.h" #include "qtractorAudioEngine.h" #include "qtractorMainForm.h" #include #include #ifdef CONFIG_LIBLO #include #include #include //---------------------------------------------------------------------------- // qtractorDssiPlugin::DssiEditor -- DSSI GUI Editor instance. // struct DssiEditor { // Constructor. DssiEditor(qtractorDssiPlugin *pDssiPlugin) : plugin(pDssiPlugin), target(nullptr), source(nullptr), path(nullptr), busy(0) {} // Destructor. ~DssiEditor() { if (target) lo_address_free(target); if (source) lo_address_free(source); if (path) ::free(path); } // Member variables. qtractorDssiPlugin *plugin; lo_address target; lo_address source; char *path; int busy; }; static lo_server_thread g_oscThread; static QString g_sOscPath; static QMutex g_oscMutex; static QHash g_dssiEditors; static QString osc_label ( qtractorDssiPlugin *pDssiPlugin ) { return (pDssiPlugin->type())->label() + '.' + QString::number(ulong(pDssiPlugin), 16); } static DssiEditor *osc_find_editor ( const QString& sOscLabel ) { #ifdef CONFIG_DEBUG_0 qDebug("osc_find_editor(\"%s\")", sOscLabel.toUtf8().constData()); #endif return g_dssiEditors.value(sOscLabel, nullptr); } static void osc_error ( int num, const char *msg, const char *path ) { qWarning("osc_error: server error %d in path \"%s\": %s", num, path, msg); } static int osc_send_configure ( DssiEditor *pDssiEditor, const char *key, const char *value ) { if (pDssiEditor->busy > 0) return 1; if (pDssiEditor->target == nullptr) return 1; #ifdef CONFIG_DEBUG qDebug("osc_send_configure: path \"%s\", key \"%s\", value \"%s\"", pDssiEditor->path, key, value); #endif QString sPath(pDssiEditor->path); sPath += "/configure"; lo_send(pDssiEditor->target, sPath.toUtf8().constData(), "ss", key, value); return 0; } static int osc_send_control ( DssiEditor *pDssiEditor, int param, float value ) { if (pDssiEditor->busy > 0) return 1; if (pDssiEditor->target == nullptr) return 1; #ifdef CONFIG_DEBUG qDebug("osc_send_control: path \"%s\", param %d, value %g", pDssiEditor->path, param, value); #endif QString sPath(pDssiEditor->path); sPath += "/control"; lo_send(pDssiEditor->target, sPath.toUtf8().constData(), "if", param, value); return 0; } static int osc_send_program ( DssiEditor *pDssiEditor, int bank, int prog ) { if (pDssiEditor->busy > 0) return 1; if (pDssiEditor->target == nullptr) return 1; #ifdef CONFIG_DEBUG qDebug("osc_send_program: path \"%s\", bank %d, prog %d", pDssiEditor->path, bank, prog); #endif QString sPath(pDssiEditor->path); sPath += "/program"; lo_send(pDssiEditor->target, sPath.toUtf8().constData(), "ii", bank, prog); return 0; } static int osc_send_show ( DssiEditor *pDssiEditor ) { if (pDssiEditor->busy > 0) return 1; if (pDssiEditor->target == nullptr) return 1; #ifdef CONFIG_DEBUG qDebug("osc_send_show: path \"%s\"", pDssiEditor->path); #endif QString sPath(pDssiEditor->path); sPath += "/show"; lo_send(pDssiEditor->target, sPath.toUtf8().constData(), ""); return 0; } static int osc_send_hide ( DssiEditor *pDssiEditor ) { if (pDssiEditor->busy > 0) return 1; if (pDssiEditor->target == nullptr) return 1; #ifdef CONFIG_DEBUG qDebug("osc_send_hide: path \"%s\"", pDssiEditor->path); #endif QString sPath(pDssiEditor->path); sPath += "/hide"; lo_send(pDssiEditor->target, sPath.toUtf8().constData(), ""); return 0; } static int osc_send_quit ( DssiEditor *pDssiEditor ) { if (pDssiEditor->busy > 0) return 1; if (pDssiEditor->target == nullptr) return 1; #ifdef CONFIG_DEBUG qDebug("osc_send_quit: path \"%s\"", pDssiEditor->path); #endif QString sPath(pDssiEditor->path); sPath += "/quit"; lo_send(pDssiEditor->target, sPath.toUtf8().constData(), ""); return 0; } static int osc_update ( DssiEditor *pDssiEditor, lo_arg **argv, lo_address source ) { const char *url = (const char *) &argv[0]->s; const char *host, *port; #ifdef CONFIG_DEBUG qDebug("osc_update: path \"%s\"", url); #endif qtractorDssiPlugin *pDssiPlugin = pDssiEditor->plugin; if (pDssiPlugin == nullptr) return 1; ++(pDssiEditor->busy); if (pDssiEditor->target) lo_address_free(pDssiEditor->target); host = lo_url_get_hostname(url); port = lo_url_get_port(url); pDssiEditor->target = lo_address_new(host, port); ::free((void *) host); ::free((void *) port); if (pDssiEditor->source) lo_address_free(pDssiEditor->source); host = lo_address_get_hostname(source); port = lo_address_get_port(source); pDssiEditor->source = lo_address_new(host, port); if (pDssiEditor->path) ::free(pDssiEditor->path); pDssiEditor->path = lo_url_get_path(url); --(pDssiEditor->busy); // Update plugin configuration... const qtractorPlugin::Configs& configs = pDssiPlugin->configs(); qtractorPlugin::Configs::ConstIterator iter = configs.constBegin(); const qtractorPlugin::Configs::ConstIterator& iter_end = configs.constEnd(); for (; iter != iter_end; ++iter) { osc_send_configure(pDssiEditor, iter.key().toUtf8().constData(), iter.value().toUtf8().constData()); } // Update program selection... qtractorMidiManager *pMidiManager = (pDssiPlugin->list())->midiManager(); if (pMidiManager && pMidiManager->currentBank() >= 0 && pMidiManager->currentProg() >= 0) { osc_send_program(pDssiEditor, pMidiManager->currentBank(), pMidiManager->currentProg()); } // Update control params... const qtractorPlugin::Params& params = pDssiPlugin->params(); qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator& param_end = params.constEnd(); for ( ; param != param_end; ++param) { qtractorPlugin::Param *pParam = param.value(); osc_send_control(pDssiEditor, pParam->index(), pParam->value()); } // Update all control output ports... pDssiPlugin->updateControlOuts(true); return osc_send_show(pDssiEditor); } static int osc_configure ( DssiEditor *pDssiEditor, lo_arg **argv ) { const char *key = (const char *) &argv[0]->s; const char *value = (const char *) &argv[1]->s; #ifdef CONFIG_DEBUG qDebug("osc_configure: path \"%s\", key \"%s\", value \"%s\"", pDssiEditor->path, key, value); #endif qtractorDssiPlugin *pDssiPlugin = pDssiEditor->plugin; if (pDssiPlugin == nullptr) return 1; // Save and send configuration to plugin... ++(pDssiEditor->busy); pDssiPlugin->setConfig(key, value); pDssiPlugin->configure(key, value); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->dirtyNotifySlot(); --(pDssiEditor->busy); return 0; } static int osc_control ( DssiEditor *pDssiEditor, lo_arg **argv ) { const int param = argv[0]->i; const float value = argv[1]->f; #ifdef CONFIG_DEBUG qDebug("osc_control: path \"%s\", param %d, value %g", pDssiEditor->path, param, value); #endif qtractorDssiPlugin *pDssiPlugin = pDssiEditor->plugin; if (pDssiPlugin == nullptr) return 1; // Plugin parameter lookup. ++(pDssiEditor->busy); pDssiPlugin->updateParamValue(param, value, true); --(pDssiEditor->busy); return 0; } static int osc_program ( DssiEditor *pDssiEditor, lo_arg **argv ) { const int bank = argv[0]->i; const int prog = argv[1]->i; #ifdef CONFIG_DEBUG qDebug("osc_program: path \"%s\", bank %d, prog %d", pDssiEditor->path, bank, prog); #endif qtractorDssiPlugin *pDssiPlugin = pDssiEditor->plugin; if (pDssiPlugin == nullptr) return 1; // Bank/Program selection pending... ++(pDssiEditor->busy); //pDssiPlugin->selectProgram(bank, prog); -- done via observer update: qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->execute( new qtractorPluginProgramCommand(pDssiPlugin, bank, prog)); --(pDssiEditor->busy); return 0; } static int osc_midi ( DssiEditor *pDssiEditor, lo_arg **argv ) { static snd_midi_event_t *s_pAlsaCoder = nullptr; static snd_seq_event_t s_aAlsaEvent[4]; const unsigned char *data = argv[0]->m; #ifdef CONFIG_DEBUG qDebug("osc_midi: path \"%s\", midi 0x%02x 0x%02x 0x%02x 0x%02x", pDssiEditor->path, data[0], data[1], data[2], data[3]); #endif qtractorDssiPlugin *pDssiPlugin = pDssiEditor->plugin; if (pDssiPlugin == nullptr) return 1; qtractorMidiManager *pMidiManager = (pDssiPlugin->list())->midiManager(); if (pMidiManager == nullptr) return 1; if (s_pAlsaCoder == nullptr && snd_midi_event_new(4, &s_pAlsaCoder)) return 1; snd_midi_event_reset_encode(s_pAlsaCoder); if (snd_midi_event_encode(s_pAlsaCoder, &data[1], 3, s_aAlsaEvent) < 1) return 1; // Send the event directly to snd_seq_event_t *pEvent = &s_aAlsaEvent[0]; if (snd_seq_ev_is_channel_type(pEvent)) pMidiManager->direct(pEvent); return 0; } static int osc_exiting ( DssiEditor *pDssiEditor ) { #ifdef CONFIG_DEBUG qDebug("osc_exiting: path \"%s\"", pDssiEditor->path); #endif qtractorDssiPlugin *pDssiPlugin = pDssiEditor->plugin; if (pDssiPlugin) { pDssiPlugin->clearEditor(); } pDssiEditor->plugin = nullptr; if (g_dssiEditors.remove(osc_label(pDssiPlugin)) > 0) delete pDssiEditor; return 0; } static int osc_message ( const char *path, const char * /*types*/, lo_arg **argv, int /*argc*/, lo_message data, void * /*user_data*/ ) { QMutexLocker locker(&g_oscMutex); #ifdef CONFIG_DEBUG_0 printf("osc_message: path \"%s\"", path); for (int i = 0; i < argc; ++i) { printf(", arg %d '%c' ", i, types[i]); lo_arg_pp(lo_type(types[i]), argv[i]); } printf(", data %p, user_data %p\n", data, user_data); #endif if (::strncmp(path, "/dssi", 5)) return 1; const QString sPath = path; const QString& sLabel = sPath.section('/', 2, 2); DssiEditor *pDssiEditor = osc_find_editor(sLabel); if (pDssiEditor == nullptr) return 1; if (pDssiEditor->busy > 0) return 1; lo_message message = lo_message(data); lo_address source = lo_message_get_source(message); const QString& sMethod = sPath.section('/', 3, 3); if (sMethod == "update") return osc_update(pDssiEditor, argv, source); else if (sMethod == "configure") return osc_configure(pDssiEditor, argv); else if (sMethod == "control") return osc_control(pDssiEditor, argv); else if (sMethod == "program") return osc_program(pDssiEditor, argv); else if (sMethod == "midi") return osc_midi(pDssiEditor, argv); else if (sMethod == "exiting") return osc_exiting(pDssiEditor); return 1; } static void osc_start (void) { if (g_oscThread) return; #ifdef CONFIG_DEBUG qDebug("osc_start()"); #endif // Create OSC thread... g_oscThread = lo_server_thread_new(nullptr, osc_error); g_sOscPath = lo_server_thread_get_url(g_oscThread); g_sOscPath += "dssi"; lo_server_thread_add_method(g_oscThread, nullptr, nullptr, osc_message, nullptr); lo_server_thread_start(g_oscThread); } static void osc_stop (void) { #ifdef CONFIG_DEBUG qDebug("osc_stop()"); #endif qDeleteAll(g_dssiEditors); g_dssiEditors.clear(); } static DssiEditor *osc_open_editor ( qtractorDssiPlugin *pDssiPlugin ) { QMutexLocker locker(&g_oscMutex); #ifdef CONFIG_DEBUG qDebug("osc_open_editor(\"%s\")", osc_label(pDssiPlugin).toUtf8().constData()); #endif if (g_dssiEditors.count() < 1) osc_start(); DssiEditor *pDssiEditor = new DssiEditor(pDssiPlugin); g_dssiEditors.insert(osc_label(pDssiPlugin), pDssiEditor); return pDssiEditor; } static void osc_close_editor ( DssiEditor *pDssiEditor ) { QMutexLocker locker(&g_oscMutex); #ifdef CONFIG_DEBUG qDebug("osc_close_editor(\"%s\")", osc_label(pDssiEditor->plugin).toUtf8().constData()); #endif osc_send_hide(pDssiEditor); osc_send_quit(pDssiEditor); osc_exiting(pDssiEditor); if (g_dssiEditors.count() < 1) osc_stop(); } #endif //---------------------------------------------------------------------------- // qtractorDssiPlugin::DssiMulti -- DSSI multiple instance pool entry. // static float *g_pDummyBuffer = nullptr; static unsigned int g_iDummyBufferSize = 0; class DssiMulti { public: // Constructor. DssiMulti() : m_iSize(0), m_ppPlugins(nullptr), m_iInstances(0), m_piInstances(nullptr), m_phInstances(nullptr), m_ppEvents(nullptr), m_piEvents(nullptr), m_iProcess(0), m_iActivated(0), m_iRefCount(0) {} // Destructor. ~DssiMulti() { if (m_piEvents) delete [] m_piEvents; if (m_ppEvents) delete [] m_ppEvents; if (m_phInstances) delete [] m_phInstances; if (m_piInstances) delete [] m_piInstances; if (m_ppPlugins) delete [] m_ppPlugins; } // Add plugin instances to registry pool. void addPlugin(qtractorDssiPlugin *pDssiPlugin) { const unsigned long iInstances = pDssiPlugin->instances(); const unsigned long iNewInstances = m_iInstances + iInstances; if (iNewInstances >= m_iSize) { m_iSize += iNewInstances; qtractorDssiPlugin **ppNewPlugins = new qtractorDssiPlugin * [m_iSize]; unsigned long *piNewInstances = new unsigned long [m_iSize]; qtractorDssiPlugin **ppOldPlugins = m_ppPlugins; unsigned long *piOldInstances = m_piInstances; if (ppOldPlugins && piOldInstances) { m_ppPlugins = nullptr; m_piInstances = nullptr; for (unsigned long i = 0; i < m_iInstances; ++i) { ppNewPlugins[i] = ppOldPlugins[i]; piNewInstances[i] = piOldInstances[i]; } delete [] piOldInstances; delete [] ppOldPlugins; } m_ppPlugins = ppNewPlugins; m_piInstances = piNewInstances; if (m_phInstances) delete [] m_phInstances; if (m_ppEvents) delete [] m_ppEvents; if (m_piEvents) delete [] m_piEvents; m_phInstances = new LADSPA_Handle [m_iSize]; m_ppEvents = new snd_seq_event_t * [m_iSize]; m_piEvents = new unsigned long [m_iSize]; } for (unsigned long i = 0; i < iInstances; ++i) { const unsigned long iInstance = m_iInstances + i; m_ppPlugins[iInstance] = pDssiPlugin; m_piInstances[iInstance] = i; } m_iInstances = iNewInstances; unsigned int iBufferSize = 0x400; // FIXME: Sane default. qtractorAudioEngine *pAudioEngine = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pAudioEngine = pSession->audioEngine(); if (pAudioEngine) iBufferSize = pAudioEngine->bufferSizeEx(); if (g_iDummyBufferSize < iBufferSize) { g_iDummyBufferSize = iBufferSize; if (g_pDummyBuffer) delete [] g_pDummyBuffer; g_pDummyBuffer = new float [g_iDummyBufferSize]; } reset(pDssiPlugin); } // Remove plugin instances from registry pool. void removePlugin(qtractorDssiPlugin *pDssiPlugin) { for (unsigned long i = 0; i < m_iInstances; ++i) { unsigned long j = i; while (j < m_iInstances && m_ppPlugins[j] == pDssiPlugin) ++j; if (j > i) { const unsigned long k = (j - i); for (; j < m_iInstances; ++j, ++i) { m_piInstances[i] = m_piInstances[j]; m_ppPlugins[i] = m_ppPlugins[j]; } m_iInstances -= k; break; } } } // Process registry pool. void process(const DSSI_Descriptor *pDssiDescriptor, unsigned int nframes) { // Count in only the active instances... if (++m_iProcess < m_iActivated) return; unsigned long iInstances = 0; for (unsigned long i = 0; i < m_iInstances; ++i) { qtractorDssiPlugin *pDssiPlugin = m_ppPlugins[i]; if (!pDssiPlugin->isActivated()) continue; // Set proper instance handle... m_phInstances[iInstances] = pDssiPlugin->ladspa_handle(m_piInstances[i]); // Set MIDI event lists... qtractorMidiManager *pMidiManager = pDssiPlugin->list()->midiManager(); if (pMidiManager) { m_ppEvents[iInstances] = pMidiManager->dssi_events(); m_piEvents[iInstances] = pMidiManager->dssi_count(); } else { m_ppEvents[iInstances] = nullptr; m_piEvents[iInstances] = 0; } // Count active instances... ++iInstances; } (*pDssiDescriptor->run_multiple_synths)(iInstances, m_phInstances, nframes, m_ppEvents, m_piEvents); m_iProcess = 0; } // Activation count methods. void activate(qtractorDssiPlugin *pDssiPlugin) { m_iActivated += pDssiPlugin->instances(); } void deactivate(qtractorDssiPlugin *pDssiPlugin) { m_iActivated -= pDssiPlugin->instances(); } // Reference count methods. void addRef() { ++m_iRefCount; } bool removeRef() { return (--m_iRefCount == 0); } // Reset connections. void reset(qtractorDssiPlugin *pDssiPlugin) { // One must connect the ports of all inactive // instances somewhere, otherwise it will crash... const LADSPA_Descriptor *pLadspaDescriptor = pDssiPlugin->ladspa_descriptor(); const unsigned short iAudioIns = pDssiPlugin->audioIns(); const unsigned short iAudioOuts = pDssiPlugin->audioOuts(); const unsigned short iInstances = pDssiPlugin->instances(); for (unsigned short i = 0; i < iInstances; ++i) { LADSPA_Handle handle = pDssiPlugin->ladspa_handle(i); for (unsigned short j = 0; j < iAudioIns; ++j) { (*pLadspaDescriptor->connect_port)(handle, pDssiPlugin->audioIn(j), g_pDummyBuffer); } for (unsigned short j = 0; j < iAudioOuts; ++j) { (*pLadspaDescriptor->connect_port)(handle, pDssiPlugin->audioOut(j), g_pDummyBuffer); } } } private: // Member variables. unsigned long m_iSize; qtractorDssiPlugin **m_ppPlugins; unsigned long m_iInstances; unsigned long *m_piInstances; LADSPA_Handle *m_phInstances; snd_seq_event_t **m_ppEvents; unsigned long *m_piEvents; unsigned long m_iProcess; unsigned long m_iActivated; unsigned int m_iRefCount; }; // DSSI multiple instance pool // for each DSSI plugin implementing run_multiple_synths // one must keep a global registry of all instances here. static QHash g_dssiHash; // DSSI multiple instance key helper. static QString dssi_multi_key ( qtractorDssiPluginType *pDssiType ) { return pDssiType->filename() + '_' + pDssiType->label(); } // DSSI multiple instance entry constructor. static DssiMulti *dssi_multi_create ( qtractorDssiPluginType *pDssiType ) { const DSSI_Descriptor *pDssiDescriptor = pDssiType->dssi_descriptor(); if (pDssiDescriptor == nullptr) return nullptr; if (pDssiDescriptor->run_multiple_synths == nullptr) return nullptr; DssiMulti *pDssiMulti = nullptr; const QString& sKey = dssi_multi_key(pDssiType); QHash::const_iterator iter = g_dssiHash.constFind(sKey); if (iter == g_dssiHash.constEnd()) { pDssiMulti = new DssiMulti(); g_dssiHash.insert(sKey, pDssiMulti); } else { pDssiMulti = iter.value(); } pDssiMulti->addRef(); return pDssiMulti; } // DSSI multiple instance entry destructor. void dssi_multi_destroy ( qtractorDssiPluginType *pDssiType ) { // Remove hash table entry... const QString& sKey = dssi_multi_key(pDssiType); QHash::Iterator iter = g_dssiHash.find(sKey); if (iter == g_dssiHash.end()) return; DssiMulti *pDssiMulti = iter.value(); if (pDssiMulti->removeRef()) { delete pDssiMulti; g_dssiHash.erase(iter); } // On last entry deallocate dummy buffer as well... if (g_dssiHash.isEmpty() && g_pDummyBuffer) { delete [] g_pDummyBuffer; g_pDummyBuffer = nullptr; g_iDummyBufferSize = 0; } } //---------------------------------------------------------------------------- // qtractorDssiPluginType -- DSSI plugin type instance. // // Derived methods. bool qtractorDssiPluginType::open (void) { // Do we have a descriptor already? if (m_pDssiDescriptor == nullptr) m_pDssiDescriptor = dssi_descriptor(file(), index()); if (m_pDssiDescriptor == nullptr) return false; // We're also a LADSPA one... m_pLadspaDescriptor = m_pDssiDescriptor->LADSPA_Plugin; // Let's get the it's own LADSPA stuff... if (!qtractorLadspaPluginType::open()) { m_pLadspaDescriptor = nullptr; m_pDssiDescriptor = nullptr; return false; } #ifdef CONFIG_DEBUG qDebug("qtractorDssiPluginType[%p]::open() filename=\"%s\" index=%lu", this, filename().toUtf8().constData(), index()); #endif // Things we have now for granted... m_bConfigure = (m_pDssiDescriptor->configure != nullptr); m_iMidiIns = 1; #ifdef CONFIG_LIBLO // Check for GUI editor executable... const QFileInfo fi(filename()); QFileInfo gi(fi.dir(), fi.baseName()); if (gi.isDir()) { QDir dir(gi.absoluteFilePath()); const QString sMask("%1_*"); QStringList names; names.append(sMask.arg(fi.baseName())); names.append(sMask.arg(m_pLadspaDescriptor->Label)); dir.setNameFilters(names); const QStringList& guis = dir.entryList(QDir::Files | QDir::Executable); if (!guis.isEmpty()) { gi.setFile(dir, guis.first()); // FIXME: Only the first? m_sDssiEditor = gi.absoluteFilePath(); m_bEditor = gi.isExecutable(); } } #endif return true; } void qtractorDssiPluginType::close (void) { m_pDssiDescriptor = nullptr; qtractorLadspaPluginType::close(); } // Factory method (static) qtractorDssiPluginType *qtractorDssiPluginType::createType ( qtractorPluginFile *pFile, unsigned long iIndex ) { // Sanity check... if (pFile == nullptr) return nullptr; // Retrieve DSSI descriptor if any... const DSSI_Descriptor *pDssiDescriptor = dssi_descriptor(pFile, iIndex); if (pDssiDescriptor == nullptr) return nullptr; // Yep, most probably its a valid plugin descriptor... return new qtractorDssiPluginType(pFile, iIndex, pDssiDescriptor); } // Descriptor method (static) const DSSI_Descriptor *qtractorDssiPluginType::dssi_descriptor ( qtractorPluginFile *pFile, unsigned long iIndex ) { // Retrieve the DSSI descriptor function, if any... DSSI_Descriptor_Function pfnDssiDescriptor = (DSSI_Descriptor_Function) pFile->resolve("dssi_descriptor"); if (pfnDssiDescriptor == nullptr) return nullptr; // Retrieve DSSI descriptor if any... return (*pfnDssiDescriptor)(iIndex); } //---------------------------------------------------------------------------- // qtractorDssiPlugin -- DSSI plugin instance. // // Constructors. qtractorDssiPlugin::qtractorDssiPlugin ( qtractorPluginList *pList, qtractorDssiPluginType *pDssiType ) : qtractorLadspaPlugin(pList, pDssiType), m_pDssiMulti(nullptr), m_pDssiEditor(nullptr), m_bEditorVisible(false), m_pfControlOutsLast(nullptr) { // Check whether we're go into a multiple instance pool. m_pDssiMulti = dssi_multi_create(pDssiType); // For tracking changes on output control ports. const unsigned long iControlOuts = pDssiType->controlOuts(); if (iControlOuts > 0) { m_pfControlOutsLast = new float [iControlOuts]; for (unsigned long j = 0; j < iControlOuts; ++j) m_pfControlOutsLast[j] = 0.0f; } // Extended first instantiantion. resetChannels(); } // Destructor. qtractorDssiPlugin::~qtractorDssiPlugin (void) { // Cleanup all plugin instances... cleanup(); // setChannels(0); // Remove reference from multiple instance pool. if (m_pDssiMulti) dssi_multi_destroy(static_cast (type())); // Remove last output control port values seen... if (m_pfControlOutsLast) delete [] m_pfControlOutsLast; } // Channel/instance number accessors. void qtractorDssiPlugin::setChannels ( unsigned short iChannels ) { // (Re)set according to existing instances... if (m_pDssiMulti) m_pDssiMulti->removePlugin(this); // Setup new instances... qtractorLadspaPlugin::setChannels(iChannels); // Epilogue... resetChannels(); } // Post-(re)initializer. void qtractorDssiPlugin::resetChannels (void) { // (Re)initialize controller port map, anyway. ::memset(m_apControllerMap, 0, 128 * sizeof(qtractorPlugin::Param *)); // Check how many instances are about there... const unsigned short iInstances = instances(); if (iInstances < 1) return; #ifdef CONFIG_DEBUG qDebug("qtractorDssiPlugin[%p]::resetChannels() instances=%u", this, iInstances); #endif // (Re)set according to existing instances... if (m_pDssiMulti) m_pDssiMulti->addPlugin(this); // Map all existing input control ports... const DSSI_Descriptor *pDssiDescriptor = dssi_descriptor(); if (pDssiDescriptor && pDssiDescriptor->get_midi_controller_for_port) { // Only the first one instance should matter... LADSPA_Handle handle = m_phInstances[0]; const qtractorPlugin::Params& params = qtractorPlugin::params(); qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator& param_end = params.constEnd(); for ( ; param != param_end; ++param) { qtractorPlugin::Param *pParam = param.value(); const int iController = (*pDssiDescriptor->get_midi_controller_for_port)( handle, pParam->index()); if (iController > 0 && DSSI_IS_CC(iController)) m_apControllerMap[DSSI_CC_NUMBER(iController)] = pParam; } } } // Do the actual activation. void qtractorDssiPlugin::activate (void) { // Activate as usual... if (m_pDssiMulti) m_pDssiMulti->activate(this); qtractorLadspaPlugin::activate(); } // Do the actual deactivation. void qtractorDssiPlugin::deactivate (void) { // Deactivate as usual... qtractorLadspaPlugin::deactivate(); if (m_pDssiMulti) m_pDssiMulti->deactivate(this); } // The main plugin processing procedure. void qtractorDssiPlugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { // Get MIDI manager access... qtractorMidiManager *pMidiManager = list()->midiManager(); if (pMidiManager == nullptr) { qtractorLadspaPlugin::process(ppIBuffer, ppOBuffer, nframes); return; } if (m_phInstances == nullptr) return; const LADSPA_Descriptor *pLadspaDescriptor = ladspa_descriptor(); if (pLadspaDescriptor == nullptr) return; const DSSI_Descriptor *pDssiDescriptor = dssi_descriptor(); if (pDssiDescriptor == nullptr) return; // We'll cross channels over instances... const unsigned short iInstances = instances(); const unsigned short iChannels = channels(); const unsigned short iAudioIns = audioIns(); const unsigned short iAudioOuts = audioOuts(); unsigned short iIChannel = 0; unsigned short iOChannel = 0; unsigned short i, j; // For each plugin instance... for (i = 0; i < iInstances; ++i) { LADSPA_Handle handle = m_phInstances[i]; // For each instance audio input port... for (j = 0; j < iAudioIns && iIChannel < iChannels; ++j) { (*pLadspaDescriptor->connect_port)(handle, m_piAudioIns[j], ppIBuffer[iIChannel++]); } // For each instance audio output port... for (j = 0; j < iAudioOuts && iOChannel < iChannels; ++j) { (*pLadspaDescriptor->connect_port)(handle, m_piAudioOuts[j], ppOBuffer[iOChannel++]); } // Care of multiple instances here... if (m_pDssiMulti) m_pDssiMulti->process(pDssiDescriptor, nframes); // Make it run... else if (pDssiDescriptor->run_synth) { (*pDssiDescriptor->run_synth)(handle, nframes, pMidiManager->dssi_events(), pMidiManager->dssi_count()); } else (*pLadspaDescriptor->run)(handle, nframes); // Wrap dangling output channels?... for (j = iOChannel; j < iChannels; ++j) ::memset(ppOBuffer[j], 0, nframes * sizeof(float)); } } // Parameter update method. void qtractorDssiPlugin::updateParam ( qtractorPlugin::Param *pParam, float fValue, bool bUpdate ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorDssiPlugin[%p]::updateParam(%lu, %g, %d)", this, pParam->index(), fValue, int(bUpdate)); #endif #ifdef CONFIG_LIBLO // And update the editor too... if (bUpdate && m_pDssiEditor) osc_send_control(m_pDssiEditor, pParam->index(), fValue); #endif } // GUI Editor stuff. void qtractorDssiPlugin::openEditor ( QWidget */*pParent*/ ) { qtractorDssiPluginType *pDssiType = static_cast (type()); if (pDssiType == nullptr) return; if (!pDssiType->isEditor()) return; #ifdef CONFIG_LIBLO // Are we already there? if (m_pDssiEditor) { osc_send_show(m_pDssiEditor); m_bEditorVisible = true; // Bail out. return; } // Tell the world we'll (maybe) take some time... qtractorPluginList::WaitCursor waiting; // Open up a new one... m_pDssiEditor = osc_open_editor(this); const QString& sDssiEditor = pDssiType->dssi_editor(); QStringList args; args.append(g_sOscPath + '/' + osc_label(this)); args.append(QFileInfo((pDssiType->file())->filename()).fileName()); args.append(pDssiType->label()); args.append(pDssiType->name()); #ifdef CONFIG_DEBUG qDebug("qtractorDssiPlugin::openEditor() " "path=\"%s\" url=\"%s\" filename=\"%s\" label=\"%s\" name=\"%s\"", sDssiEditor.toUtf8().constData(), args[0].toUtf8().constData(), args[1].toUtf8().constData(), args[2].toUtf8().constData(), args[3].toUtf8().constData()); #endif const bool bStartDetached = QProcess::startDetached(sDssiEditor, args); if (!bStartDetached) { closeEditor(); return; } #endif m_bEditorVisible = true; toggleFormEditor(true); } void qtractorDssiPlugin::closeEditor (void) { #ifdef CONFIG_LIBLO if (m_pDssiEditor) osc_close_editor(m_pDssiEditor); #else clearEditor(); #endif } // GUI editor visibility state. void qtractorDssiPlugin::setEditorVisible ( bool bVisible ) { #ifdef CONFIG_LIBLO // Check if still here... if (m_pDssiEditor == nullptr) { if (bVisible) openEditor(nullptr); return; } // Do our deeds... if (bVisible) osc_send_show(m_pDssiEditor); else osc_send_hide(m_pDssiEditor); #endif m_bEditorVisible = bVisible; toggleFormEditor(bVisible); } bool qtractorDssiPlugin::isEditorVisible (void) const { return m_bEditorVisible; } // Bank/program selector. void qtractorDssiPlugin::selectProgram ( int iBank, int iProg ) { if (iBank < 0 || iProg < 0) return; // HACK: We don't change program-preset when // we're supposed to be multi-timbral... if (list()->isMidiBus()) return; if (m_phInstances == nullptr) return; const DSSI_Descriptor *pDssiDescriptor = dssi_descriptor(); if (pDssiDescriptor == nullptr) return; if (pDssiDescriptor->select_program == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorDssiPlugin[%p]::selectProgram(%d, %d)", this, iBank, iProg); #endif // For each plugin instance... const unsigned short iInstances = instances(); for (unsigned short i = 0; i < iInstances; ++i) (*pDssiDescriptor->select_program)(m_phInstances[i], iBank, iProg); #ifdef CONFIG_LIBLO // And update the editor too... if (m_pDssiEditor) osc_send_program(m_pDssiEditor, iBank, iProg); #endif // Reset parameters default value... const qtractorPlugin::Params& params = qtractorPlugin::params(); qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator& param_end = params.constEnd(); for ( ; param != param_end; ++param) { qtractorPlugin::Param *pParam = param.value(); pParam->setDefaultValue(pParam->value()); } } // Provisional program/patch accessor. bool qtractorDssiPlugin::getProgram ( int iIndex, Program& program ) const { if (m_phInstances == nullptr) return false; const DSSI_Descriptor *pDssiDescriptor = dssi_descriptor(); if (pDssiDescriptor == nullptr) return false; if (pDssiDescriptor->get_program == nullptr) return false; // Only first one instance should matter... const DSSI_Program_Descriptor *pDssiProgram = (*pDssiDescriptor->get_program)(m_phInstances[0], iIndex); if (pDssiProgram == nullptr) return false; // Map this to that... program.bank = pDssiProgram->Bank; program.prog = pDssiProgram->Program; program.name = pDssiProgram->Name; return true; } // MIDI continuous controller handler. void qtractorDssiPlugin::setController ( int iController, int iValue ) { qtractorPlugin::Param *pParam = m_apControllerMap[DSSI_CC_NUMBER(iController)]; if (pParam == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorDssiPlugin[%p]::setController(%d, %d) index=%d", this, iController, iValue, int(pParam->index())); #endif const float fValue = float(iValue) / 127.0f; pParam->setValue(fValue, true); } // Configuration (CLOB) stuff. void qtractorDssiPlugin::configure ( const QString& sKey, const QString& sValue ) { if (m_phInstances == nullptr) return; const DSSI_Descriptor *pDssiDescriptor = dssi_descriptor(); if (pDssiDescriptor == nullptr) return; if (pDssiDescriptor->configure == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorDssiPlugin[%p]::configure(\"%s\", \"%s\")", this, sKey.toUtf8().constData(), sValue.toUtf8().constData()); #endif // For each plugin instance... const unsigned short iInstances = instances(); for (unsigned short i = 0; i < iInstances; ++i) { (*pDssiDescriptor->configure)(m_phInstances[i], sKey.toUtf8().constData(), sValue.toUtf8().constData()); } #ifdef CONFIG_LIBLO if (m_pDssiEditor) { osc_send_configure(m_pDssiEditor, sKey.toUtf8().constData(), sValue.toUtf8().constData()); } #endif } // Specific accessor. const DSSI_Descriptor *qtractorDssiPlugin::dssi_descriptor (void) const { qtractorDssiPluginType *pDssiType = static_cast (type()); return (pDssiType ? pDssiType->dssi_descriptor() : nullptr); } // Update all control output ports... void qtractorDssiPlugin::updateControlOuts ( bool bForce ) { #ifdef CONFIG_LIBLO if (m_pDssiEditor && m_piControlOuts && m_pfControlOuts) { const unsigned long iControlOuts = type()->controlOuts(); for (unsigned long j = 0; j < iControlOuts; ++j) { // if (qAbs(m_pfControlOuts[j] - m_pfControlOutsLast[j]) > 1e-6f) { if (m_pfControlOutsLast[j] != m_pfControlOuts[j] || bForce) { osc_send_control(m_pDssiEditor, m_piControlOuts[j], m_pfControlOuts[j]); m_pfControlOutsLast[j] = m_pfControlOuts[j]; } } } #endif } // Reset(null) internal editor reference. void qtractorDssiPlugin::clearEditor (void) { m_pDssiEditor = nullptr; m_bEditorVisible = false; toggleFormEditor(false); } // Idle editor update (static) void qtractorDssiPlugin::idleEditorAll (void) { #ifdef CONFIG_LIBLO QHash::ConstIterator iter = g_dssiEditors.constBegin(); const QHash::ConstIterator& iter_end = g_dssiEditors.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorDssiPlugin *pDssiPlugin = iter.value()->plugin; if (pDssiPlugin) pDssiPlugin->updateControlOuts(); } #endif } #endif // CONFIG_DSSI // end of qtractorDssiPlugin.cpp qtractor-1.5.11/src/PaxHeaders/qtractorFileList.h0000644000000000000000000000013215124701674016756 xustar0030 mtime=1767080892.785263487 30 atime=1767080892.784263491 30 ctime=1767080892.785263487 qtractor-1.5.11/src/qtractorFileList.h0000644000175000001440000000773615124701674016763 0ustar00rncbcusers// qtractorFileList.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorFileList_h #define __qtractorFileList_h #include #include #include // Forward declarations. class qtractorClip; //---------------------------------------------------------------------- // class qtractorFileList -- file path registry. // class qtractorFileList { public: // Constructor. qtractorFileList() {} // Destructor. ~qtractorFileList() { clear(); } // File types. enum Type { Audio = 0, Midi = 1 }; // File hash key. // class Key { public: // Constructors. Key(Type iType, const QString& sPath) : m_iType(iType), m_info(sPath) {} Key(const Key& key) : m_iType(key.type()), m_info(key.info()) {} // Key accessors. Type type() const { return m_iType; } const QFileInfo& info() const { return m_info; } // Key helpers. QString path() const { return m_info.absoluteFilePath(); } // Match descriminator. bool operator== (const Key& key) const { return m_iType == key.type() && m_info == key.info(); } private: // Interesting variables. Type m_iType; QFileInfo m_info; }; // File hash item. // class Item { public: // Constructor. Item(const Key& key, bool bAutoRemove = false) : m_key(key), m_bAutoRemove(bAutoRemove), m_iClipRefCount(0), m_iRefCount(0) {} // Key accessors. Type type() const { return m_key.type(); } const QFileInfo& info() const { return m_key.info(); } // Key helpers. QString path() const { return m_key.path(); } // Auto-destruction flag accessor. void setAutoRemove(bool bAutoRemove) { m_bAutoRemove = bAutoRemove; } bool isAutoRemove() const { return m_bAutoRemove; } // Clip ref-counting methods. void addClipRef() { ++m_iClipRefCount; } void removeClipRef() { --m_iClipRefCount; } unsigned int clipRefCount() const { return m_iClipRefCount; } // Ref-counting methods. unsigned int refCount() const { return m_iRefCount; } void addRef() { ++m_iRefCount; } void removeRef() { --m_iRefCount; } private: // Most interesting variables. Key m_key; bool m_bAutoRemove; unsigned int m_iClipRefCount; unsigned int m_iRefCount; }; typedef QHash Hash; // File/path registry management. void addFileItem(Type iType, const QString& sPath, bool bAutoRemove = false); void removeFileItem(Type iType, const QString& sPath); // Clip/path registry management. void addClipItem(Type iType, const QString& sPath, bool bAutoRemove = false); void addClipItem(Type iType, qtractorClip *pClip, bool bAutoRemove = false); void addClipItemEx(Type iType, qtractorClip *pClip, bool bAutoRemove = false); void removeClipItem(Type iType, qtractorClip *pClip); Item *findItem(Type iType, const QString& sPath) const; // Cleanup (dtors). void cleanup(bool bForce); void clear(); protected: // File hash table management. Item *addItem(Type iType, const QString& sPath, bool bAutoRemove); void removeItem(Item *pItem); private: // File hash table. Hash m_items; }; #endif // __qtractorFileList_h // end of qtractorFileList.h qtractor-1.5.11/src/PaxHeaders/qtractorVst3Plugin.h0000644000000000000000000000013215124701674017261 xustar0030 mtime=1767080892.805263403 30 atime=1767080892.805263403 30 ctime=1767080892.805263403 qtractor-1.5.11/src/qtractorVst3Plugin.h0000644000175000001440000001375015124701674017257 0ustar00rncbcusers// qtractorVst3Plugin.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorVst3Plugin_h #define __qtractorVst3Plugin_h #include "qtractorPlugin.h" #include #include #include // Forward decls. class qtractorAudioEngine; class QWidget; //---------------------------------------------------------------------- // class qtractorVst3PluginType -- VST3 plugin meta-interface decl. // class qtractorVst3PluginType : public qtractorPluginType { public: // Constructor. qtractorVst3PluginType(qtractorPluginFile *pFile, unsigned long iIndex); // Destructor. ~qtractorVst3PluginType(); // Factory method (static) static qtractorVst3PluginType *createType ( qtractorPluginFile *pFile, unsigned long iIndex); // Executive methods. bool open(); void close(); // It can be only one... unsigned short instances ( unsigned short iChannels, bool /*bMidi*/) const { return (iChannels > 0 ? 1 : 0); } // Instance cached-deferred accessors. const QString& aboutText(); // Forward decls. class Impl; Impl *impl() const { return m_pImpl; } private: // Instance variables. Impl *m_pImpl; }; //---------------------------------------------------------------------- // class qtractorVst3Plugin -- VST3 plugin instance interface decl. // class qtractorVst3Plugin : public qtractorPlugin { public: // Constructor. qtractorVst3Plugin(qtractorPluginList *pList, qtractorVst3PluginType *pType); // Destructor. ~qtractorVst3Plugin(); // Forward decl. class Param; // Channel/instance number accessors. void setChannels(unsigned short iChannels); // Do the actual (de)activation. void activate(); void deactivate(); // Parameter update methods. void updateParam(qtractorPlugin::Param *pParam, float fValue, bool bUpdate); // Parameters update method. void updateParamValues(bool bUpdate); // Parameters enablement method. void resetParamValues(bool bEnabled); // Parameter finder (by id). qtractorPlugin::Param *findParamId(int id) const; // Configuration state stuff. void configure(const QString& sKey, const QString& sValue); // Plugin configuration/state snapshot. void freezeConfigs(); void releaseConfigs(); // Open/close editor widget. void openEditor(QWidget *pParent = nullptr); void closeEditor(); // GUI editor visibility state. void setEditorVisible(bool bVisible); bool isEditorVisible() const; void setEditorTitle(const QString& sTitle); // Our own editor widget accessor. QWidget *editorWidget() const; // Processor stuff... // void process_midi_in(unsigned char *data, unsigned int size, unsigned long offset, unsigned short port); void process(float **ppIBuffer, float **ppOBuffer, unsigned int nframes); // Plugin current latency (in frames); unsigned long latency() const; // Provisional program/patch accessor. bool getProgram(int iIndex, Program& program) const; // Specific MIDI instrument selector. void selectProgram(int iBank, int iProg); // Plugin preset i/o (configuration from/to state files). bool loadPresetFile(const QString& sFilename); bool savePresetFile(const QString& sFilename); // Common host-time keeper (static) static void updateTime(qtractorAudioEngine *pAudioEngine); // Host cleanup (static). static void clearAll(); // Forward decls. class Impl; Impl *impl() const { return m_pImpl; } protected: // Forward decls. class Handler; class RunLoop; class EditorFrame; class EditorWidget; class ParamQueue; class ParamChanges; class ParamTransfer; class EventList; class Stream; // Plugin instance (de)initializer. void initialize(); void deinitialize(); // Internal accessors. EditorFrame *editorFrame() const; // Make up some others dirty... void updateDirtyCount(); private: // Instance variables. Impl *m_pImpl; EditorFrame *m_pEditorFrame; EditorWidget *m_pEditorWidget; // Audio I/O buffer pointers. float **m_ppIBuffer; float **m_ppOBuffer; // Dummy I/O buffers. float *m_pfIDummy; float *m_pfODummy; // MIDI Event decoder. snd_midi_event_t *m_pMidiParser; // Identififier-parameter map. QHash m_paramIds; }; //---------------------------------------------------------------------------- // qtractorVst3Plugin::Param -- VST3 plugin parameter interface decl. // class qtractorVst3Plugin::Param : public qtractorPlugin::Param { public: // Constructor. Param(qtractorVst3Plugin *pPlugin, unsigned long iIndex); // Destructor. ~Param(); // Parameter range hints predicate methods. bool isBoundedBelow() const; bool isBoundedAbove() const; bool isDefaultValue() const; bool isLogarithmic() const; bool isSampleRate() const; bool isInteger() const; bool isToggled() const; bool isDisplay() const; // Current display value. QString display() const; // Forward decls. class Impl; Impl *impl() const { return m_pImpl; } // Parameter enablement methods. void setValueEnabled(bool bEnabled) { m_bValueEnabled = bEnabled; } bool isValueEnabled() const { return m_bValueEnabled; } private: // Instance variables. Impl *m_pImpl; bool m_bValueEnabled; }; #endif // __qtractorVst3Plugin_h // end of qtractorVst3Plugin.h qtractor-1.5.11/src/PaxHeaders/qtractorPluginForm.h0000644000000000000000000000013215124701674017325 xustar0030 mtime=1767080892.798263432 30 atime=1767080892.798263432 30 ctime=1767080892.798263432 qtractor-1.5.11/src/qtractorPluginForm.h0000644000175000001440000001241215124701674017315 0ustar00rncbcusers// qtractorPluginForm.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorPluginForm_h #define __qtractorPluginForm_h #include "ui_qtractorPluginForm.h" #include "qtractorPlugin.h" #include // Forward declarations... class qtractorPluginParamWidget; class qtractorPluginPropertyWidget; class qtractorMidiControlObserver; class qtractorMidiControlPluginWidget; class qtractorObserverCheckBox; class qtractorObserverSlider; class qtractorObserverSpinBox; class qtractorPluginParamDisplay; class qtractorSpinBox; class QCheckBox; class QTextEdit; class QComboBox; class QPushButton; class QToolButton; //---------------------------------------------------------------------------- // qtractorPluginForm -- UI wrapper form. class qtractorPluginForm : public QWidget { Q_OBJECT public: // Constructor. qtractorPluginForm(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()); // Destructor. ~qtractorPluginForm(); void setPlugin(qtractorPlugin *pPlugin); qtractorPlugin *plugin() const; void setPreset(const QString& sPreset); QString preset() const; void updateActivated(); void updateDirtyCount(); void updateMidiControlAutoConnect(); void updateAuxSendBusName(); void toggleEditor(bool bOn); void refresh(); void clear(); protected slots: void changePresetSlot(const QString& sPreset); void loadPresetSlot(int iPreset); void openPresetSlot(); void savePresetSlot(); void deletePresetSlot(); void aliasSlot(); void editSlot(bool bOn); void activateSlot(bool bOn); void currentChangedSlot(int iTab); void sendsSlot(); void returnsSlot(); void autoConnectSlot(bool bOn); void midiControlActionSlot(); void midiControlMenuSlot(const QPoint& pos); void changeAuxSendBusNameSlot(int iAuxSendBusName); void clickAuxSendBusNameSlot(); void clickAuxSendIOMatrixSlot(); void updateDirectAccessParamSlot(); void changeDirectAccessParamSlot(); void updateParamRangeSlot(); protected: void stabilize(); // Show insert pseudo-plugin audio bus connections. void insertPluginBus(int iBusMode); // MIDI controller/observer attachement (context menu) void addMidiControlAction( QWidget *pWidget, qtractorMidiControlObserver *pObserver); // Keyboard event handler. void keyPressEvent(QKeyEvent *); // Form show/hide events (restore/save position). void showEvent(QShowEvent *); void hideEvent(QHideEvent *); // Update the about text label (with some varying meta-data)... void updateLatencyTextLabel(); private: // The Qt-designer UI struct... Ui::qtractorPluginForm m_ui; // Instance variables... qtractorPlugin *m_pPlugin; QList m_paramWidgets; qtractorMidiControlPluginWidget *m_pMidiControlPluginWidget; QMenu *m_pDirectAccessParamMenu; int m_iDirtyCount; int m_iUpdate; // Common (de)activated icon stuff. static QIcon *g_pIcons[2]; static int g_iIconsRefCount; }; //---------------------------------------------------------------------------- // qtractorPluginParamWidget -- Plugin parameter/property common widget. // class qtractorPluginParamWidget : public QWidget { Q_OBJECT public: // Constructor. qtractorPluginParamWidget(qtractorPlugin::Param *pParam, QWidget *pParent = nullptr); // Destructor. ~qtractorPluginParamWidget(); // Refreshner-loader method. void refresh(); // Special range updater. void updateParamRange(); protected slots: // Parameter value change slot. void updateValue(float fValue); // Property value change slot. void propertyChanged(); // Property file selector. void toolButtonClicked(); // Automation curve selector. void curveButtonClicked(); protected: // Param/Property discriminator.. qtractorPlugin::Property *property () const; // Parameter automation curve status update/refresh. void updateCurveButton(); // Text edit (string) event filter. bool eventFilter(QObject *pObject, QEvent *pEvent); private: // Local forward declarations. class SliderInterface; // Instance variables. qtractorPlugin::Param *m_pParam; // Some possible managed widgets. qtractorObserverCheckBox *m_pCheckBox; qtractorObserverSlider *m_pSlider; qtractorObserverSpinBox *m_pSpinBox; qtractorPluginParamDisplay *m_pDisplay; QPushButton *m_pCurveButton; // Non-automatable widgets. QTextEdit *m_pTextEdit; QComboBox *m_pComboBox; QToolButton *m_pToolButton; }; #endif // __qtractorPluginForm_h // end of qtractorPluginForm.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiConnect.cpp0000644000000000000000000000012715124701674017776 xustar0029 mtime=1767080892.78926347 29 atime=1767080892.78926347 29 ctime=1767080892.78926347 qtractor-1.5.11/src/qtractorMidiConnect.cpp0000644000175000001440000003376715124701674020002 0ustar00rncbcusers// qtractorMidiConnect.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorMidiConnect.h" #include "qtractorMidiEngine.h" #include "qtractorSession.h" #include //---------------------------------------------------------------------- // qtractorMidiPortItem -- Alsa port list item. // // Constructor. qtractorMidiPortItem::qtractorMidiPortItem ( qtractorMidiClientItem *pClientItem ) : qtractorPortListItem(pClientItem) { if (pClientItem->isReadable()) { QTreeWidgetItem::setIcon(0, qtractorMidiConnect::icon(qtractorMidiConnect::PortOut)); } else { QTreeWidgetItem::setIcon(0, qtractorMidiConnect::icon(qtractorMidiConnect::PortIn)); } } // Default destructor. qtractorMidiPortItem::~qtractorMidiPortItem (void) { } // Alsa handles accessors. int qtractorMidiPortItem::alsaClient (void) const { qtractorMidiClientItem *pClientItem = static_cast (clientItem()); return (pClientItem ? pClientItem->alsaClient() : 0); } int qtractorMidiPortItem::alsaPort (void) const { return portName().section(':', 0, 0).toInt(); } //---------------------------------------------------------------------- // qtractorMidiClientItem -- Alsa client list item. // // Constructor. qtractorMidiClientItem::qtractorMidiClientItem ( qtractorMidiClientListView *pClientListView ) : qtractorClientListItem(pClientListView) { if (pClientListView->isReadable()) { QTreeWidgetItem::setIcon(0, qtractorMidiConnect::icon(qtractorMidiConnect::ClientOut)); } else { QTreeWidgetItem::setIcon(0, qtractorMidiConnect::icon(qtractorMidiConnect::ClientIn)); } } // Default destructor. qtractorMidiClientItem::~qtractorMidiClientItem (void) { } // Jack client accessor. int qtractorMidiClientItem::alsaClient (void) const { return clientName().section(':', 0, 0).toInt(); } // Derived port finder. qtractorMidiPortItem *qtractorMidiClientItem::findPortItem ( int iAlsaPort ) { const int iChildCount = QTreeWidgetItem::childCount(); for (int iChild = 0; iChild < iChildCount; ++iChild) { QTreeWidgetItem *pItem = QTreeWidgetItem::child(iChild); if (pItem->type() != qtractorConnect::PortItem) continue; qtractorMidiPortItem *pPortItem = static_cast (pItem); if (pPortItem && pPortItem->alsaPort() == iAlsaPort) return pPortItem; } return nullptr; } //---------------------------------------------------------------------- // qtractorMidiClientListView -- Alsa client list view. // // Constructor. qtractorMidiClientListView::qtractorMidiClientListView( QWidget *pParent ) : qtractorClientListView(pParent) { } // Default destructor. qtractorMidiClientListView::~qtractorMidiClientListView (void) { } // Alsa sequencer accessor. snd_seq_t *qtractorMidiClientListView::alsaSeq (void) const { qtractorMidiConnect *pMidiConnect = static_cast (binding()); return (pMidiConnect ? pMidiConnect->alsaSeq() : nullptr); } // Client finder by id. qtractorMidiClientItem *qtractorMidiClientListView::findClientItem ( int iAlsaClient ) { const int iItemCount = QTreeWidget::topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = QTreeWidget::topLevelItem(iItem); if (pItem->type() != qtractorConnect::ClientItem) continue; qtractorMidiClientItem *pClientItem = static_cast (pItem); if (pClientItem && pClientItem->alsaClient() == iAlsaClient) return pClientItem; } return nullptr; } // Client port finder by id. qtractorMidiPortItem *qtractorMidiClientListView::findClientPortItem ( int iAlsaClient, int iAlsaPort ) { qtractorMidiClientItem *pClientItem = findClientItem(iAlsaClient); if (pClientItem == nullptr) return nullptr; return pClientItem->findPortItem(iAlsaPort); } // Client:port refreshner. int qtractorMidiClientListView::updateClientPorts (void) { snd_seq_t *pAlsaSeq = alsaSeq(); if (pAlsaSeq == nullptr) return 0; int iDirtyCount = 0; markClientPorts(0); unsigned int uiAlsaFlags; if (isReadable()) uiAlsaFlags = SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; else uiAlsaFlags = SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; snd_seq_client_info_t *pClientInfo; snd_seq_port_info_t *pPortInfo; snd_seq_client_info_alloca(&pClientInfo); snd_seq_port_info_alloca(&pPortInfo); snd_seq_client_info_set_client(pClientInfo, -1); while (snd_seq_query_next_client(pAlsaSeq, pClientInfo) >= 0) { const int iAlsaClient = snd_seq_client_info_get_client(pClientInfo); if (iAlsaClient > 0) { qtractorMidiClientItem *pClientItem = findClientItem(iAlsaClient); snd_seq_port_info_set_client(pPortInfo, iAlsaClient); snd_seq_port_info_set_port(pPortInfo, -1); while (snd_seq_query_next_port(pAlsaSeq, pPortInfo) >= 0) { const unsigned int uiPortCapability = snd_seq_port_info_get_capability(pPortInfo); if (((uiPortCapability & uiAlsaFlags) == uiAlsaFlags) && ((uiPortCapability & SND_SEQ_PORT_CAP_NO_EXPORT) == 0)) { QString sClientName = QString::number(iAlsaClient); sClientName += ':'; sClientName += QString::fromUtf8( snd_seq_client_info_get_name(pClientInfo)); if (isClientName(sClientName)) { const int iAlsaPort = snd_seq_port_info_get_port(pPortInfo); QString sPortName = QString::number(iAlsaPort); sPortName += ':'; sPortName += QString::fromUtf8( snd_seq_port_info_get_name(pPortInfo)); if (isPortName(sPortName)) { qtractorMidiPortItem *pPortItem = nullptr; if (pClientItem == nullptr) { pClientItem = new qtractorMidiClientItem(this); pClientItem->setClientName(sClientName); ++iDirtyCount; } else { pPortItem = pClientItem->findPortItem(iAlsaPort); if (sClientName != pClientItem->clientName()) { pClientItem->setClientName(sClientName); ++iDirtyCount; } } if (pClientItem) { if (pPortItem == nullptr) { pPortItem = new qtractorMidiPortItem(pClientItem); pPortItem->setPortName(sPortName); ++iDirtyCount; } else if (sPortName != pPortItem->portName()) { pPortItem->setPortName(sPortName); ++iDirtyCount; } } if (pPortItem) pPortItem->markClientPort(1); } } } } } } cleanClientPorts(0); return iDirtyCount; } //---------------------------------------------------------------------------- // qtractorMidiConnect -- Alsa connections model integrated object. // // Constructor. qtractorMidiConnect::qtractorMidiConnect ( qtractorMidiClientListView *pOListView, qtractorMidiClientListView *pIListView, qtractorConnectorView *pConnectorView ) : qtractorConnect(pOListView, pIListView, pConnectorView) { createIcons(); } // Default destructor. qtractorMidiConnect::~qtractorMidiConnect (void) { deleteIcons(); } // Local icon-set janitor methods. QIcon *qtractorMidiConnect::g_apIcons[qtractorMidiConnect::IconCount]; int qtractorMidiConnect::g_iIconsRefCount = 0; void qtractorMidiConnect::createIcons (void) { if (++g_iIconsRefCount == 1) { g_apIcons[ClientIn] = new QIcon(QIcon::fromTheme("itemMidiClientIn")); g_apIcons[ClientOut] = new QIcon(QIcon::fromTheme("itemMidiClientOut")); g_apIcons[PortIn] = new QIcon(QIcon::fromTheme("itemMidiPortIn")); g_apIcons[PortOut] = new QIcon(QIcon::fromTheme("itemMidiPortOut")); } } void qtractorMidiConnect::deleteIcons (void) { if (--g_iIconsRefCount == 0) { for (int i = 0; i < IconCount; ++i) { if (g_apIcons[i]) delete g_apIcons[i]; g_apIcons[i] = nullptr; } } } // Common icon accessor (static). const QIcon& qtractorMidiConnect::icon ( int iIcon ) { return *g_apIcons[iIcon]; } // ALSA sequencer accessor. snd_seq_t *qtractorMidiConnect::alsaSeq (void) const { snd_seq_t *pAlsaSeq = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession && pSession->midiEngine()) pAlsaSeq = (pSession->midiEngine())->alsaSeq(); return pAlsaSeq; } // Connection primitive. bool qtractorMidiConnect::connectPorts ( qtractorPortListItem *pOPort, qtractorPortListItem *pIPort ) { qtractorMidiPortItem *pOMidiPort = static_cast (pOPort); qtractorMidiPortItem *pIMidiPort = static_cast (pIPort); if (pOMidiPort == nullptr || pIMidiPort == nullptr) return false; snd_seq_t *pAlsaSeq = alsaSeq(); if (pAlsaSeq == nullptr) return false; disconnectPortsUpdate(pOPort, pIPort); snd_seq_port_subscribe_t *pAlsaSubs; snd_seq_addr_t seq_addr; snd_seq_port_subscribe_alloca(&pAlsaSubs); seq_addr.client = pOMidiPort->alsaClient(); seq_addr.port = pOMidiPort->alsaPort(); snd_seq_port_subscribe_set_sender(pAlsaSubs, &seq_addr); seq_addr.client = pIMidiPort->alsaClient(); seq_addr.port = pIMidiPort->alsaPort(); snd_seq_port_subscribe_set_dest(pAlsaSubs, &seq_addr); return (snd_seq_subscribe_port(pAlsaSeq, pAlsaSubs) >= 0); } // Disconnection primitive. bool qtractorMidiConnect::disconnectPorts ( qtractorPortListItem *pOPort, qtractorPortListItem *pIPort ) { qtractorMidiPortItem *pOMidiPort = static_cast (pOPort); qtractorMidiPortItem *pIMidiPort = static_cast (pIPort); if (pOMidiPort == nullptr || pIMidiPort == nullptr) return false; snd_seq_t *pAlsaSeq = alsaSeq(); if (pAlsaSeq == nullptr) return false; disconnectPortsUpdate(pOPort, pIPort); snd_seq_port_subscribe_t *pAlsaSubs; snd_seq_addr_t seq_addr; snd_seq_port_subscribe_alloca(&pAlsaSubs); seq_addr.client = pOMidiPort->alsaClient(); seq_addr.port = pOMidiPort->alsaPort(); snd_seq_port_subscribe_set_sender(pAlsaSubs, &seq_addr); seq_addr.client = pIMidiPort->alsaClient(); seq_addr.port = pIMidiPort->alsaPort(); snd_seq_port_subscribe_set_dest(pAlsaSubs, &seq_addr); return (snd_seq_unsubscribe_port(pAlsaSeq, pAlsaSubs) >= 0); } // Update (clear) MIDI-buses connect lists (non-virtual). void qtractorMidiConnect::disconnectPortsUpdate ( qtractorPortListItem *pOPort, qtractorPortListItem *pIPort ) { qtractorMidiEngine *pMidiEngine = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; QString sPortName; qtractorBus::BusMode busMode = qtractorBus::None; if (pOPort->clientName().section(':', 1, 1) == pMidiEngine->clientName()) { busMode = qtractorBus::Output; sPortName = pOPort->portName().section(':', 1, 1); } else if (pIPort->clientName().section(':', 1, 1) == pMidiEngine->clientName()) { busMode = qtractorBus::Input; sPortName = pIPort->portName().section(':', 1, 1); } if (busMode == qtractorBus::None) return; for (qtractorBus *pBus = pMidiEngine->buses().first(); pBus; pBus = pBus->next()) { if ((pBus->busMode() & busMode) == 0) continue; if (sPortName == pBus->busName()) { if (busMode & qtractorBus::Input) { pBus->inputs().clear(); } else { pBus->outputs().clear(); // Remember to resend all session/tracks control stuff... pMidiEngine->resetAllControllers(false); // Deferred++ } break; } } } // Update port connection references. void qtractorMidiConnect::updateConnections (void) { snd_seq_t *pAlsaSeq = alsaSeq(); if (pAlsaSeq == nullptr) return; snd_seq_query_subscribe_t *pAlsaSubs; snd_seq_addr_t seq_addr; snd_seq_query_subscribe_alloca(&pAlsaSubs); // Proper type casts. qtractorMidiClientListView *pOListView = static_cast (OListView()); qtractorMidiClientListView *pIListView = static_cast (IListView()); // For each client item... const int iItemCount = pOListView->topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = pOListView->topLevelItem(iItem); if (pItem->type() != qtractorConnect::ClientItem) continue; qtractorMidiClientItem *pOClient = static_cast (pItem); if (pOClient == nullptr) continue; // For each port item const int iChildCount = pOClient->childCount(); for (int iChild = 0; iChild < iChildCount; ++iChild) { QTreeWidgetItem *pChild = pOClient->child(iChild); if (pChild->type() != qtractorConnect::PortItem) continue; qtractorMidiPortItem *pOPort = static_cast (pChild); if (pOPort == nullptr) continue; // Are there already any connections? if (pOPort->connects().count() > 0) continue; // Get port connections... snd_seq_query_subscribe_set_type(pAlsaSubs, SND_SEQ_QUERY_SUBS_READ); snd_seq_query_subscribe_set_index(pAlsaSubs, 0); seq_addr.client = pOPort->alsaClient(); seq_addr.port = pOPort->alsaPort(); snd_seq_query_subscribe_set_root(pAlsaSubs, &seq_addr); while (snd_seq_query_port_subscribers(pAlsaSeq, pAlsaSubs) >= 0) { seq_addr = *snd_seq_query_subscribe_get_addr(pAlsaSubs); qtractorMidiPortItem *pIPort = pIListView->findClientPortItem( seq_addr.client, seq_addr.port); if (pIPort) { pOPort->addConnect(pIPort); pIPort->addConnect(pOPort); } snd_seq_query_subscribe_set_index(pAlsaSubs, snd_seq_query_subscribe_get_index(pAlsaSubs) + 1); } } } } // end of qtractorMidiConnect.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiCursor.h0000644000000000000000000000013215124701674017323 xustar0030 mtime=1767080892.790263466 30 atime=1767080892.790263466 30 ctime=1767080892.790263466 qtractor-1.5.11/src/qtractorMidiCursor.h0000644000175000001440000000344615124701674017322 0ustar00rncbcusers// qtractorMidiCursor.h // /**************************************************************************** Copyright (C) 2005-2019, 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 __qtractorMidiCursor_h #define __qtractorMidiCursor_h // Forward declarations. class qtractorMidiEvent; class qtractorMidiSequence; //------------------------------------------------------------------------- // qtractorMidiCursor -- MIDI event cursor capsule. class qtractorMidiCursor { public: // Constructor. qtractorMidiCursor(); // Intra-sequence tick/time positioning seek. qtractorMidiEvent *seek( qtractorMidiSequence *pSeq, unsigned long iTime); // Intra-sequence tick/time positioning reset (seek forward). qtractorMidiEvent *reset( qtractorMidiSequence *pSeq, unsigned long iTime = 0); // Complete/clear positioning reset. void clear(); private: // Current event cursor variables. qtractorMidiEvent *m_pEvent; unsigned long m_iTime; }; #endif // __qtractorMidiCursor_h // end of qtractorMidiCursor.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiSequence.cpp0000644000000000000000000000013215124701674020151 xustar0030 mtime=1767080892.795263445 30 atime=1767080892.794263449 30 ctime=1767080892.795263445 qtractor-1.5.11/src/qtractorMidiSequence.cpp0000644000175000001440000001565215124701674020152 0ustar00rncbcusers// qtractorMidiSequence.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorMidiSequence.h" //---------------------------------------------------------------------- // class qtractorMidiSequence -- The generic MIDI event sequence buffer. // // Constructor. qtractorMidiSequence::qtractorMidiSequence ( const QString& sName, unsigned short iChannel, unsigned short iTicksPerBeat ) { m_sName = sName; m_iChannel = iChannel; m_iTicksPerBeat = iTicksPerBeat; m_iTimeOffset = 0; m_iTimeLength = 0; m_events.setAutoDelete(true); m_noteMax = 0; m_noteMin = 0; clear(); } // Destructor. qtractorMidiSequence::~qtractorMidiSequence (void) { clear(); } // Sequencer reset method. void qtractorMidiSequence::clear (void) { m_iBankSelMethod = -1; m_iBank = -1; m_iProg = -1; // m_noteMax = 0; // m_noteMin = 0; m_duration = 0; m_events.clear(); m_notes.clear(); } // NOTEON/OFF: Find previous note event and compute duration... void qtractorMidiSequence::addNoteEvent ( qtractorMidiEvent *pEvent ) { NoteOns::Iterator iter = m_notes.find(pEvent->note()); if (iter == m_notes.end()) return; qtractorMidiEvent *pNoteEvent = iter.value(); if (pNoteEvent == nullptr) // Double-check, not really necessary... return; const unsigned long t1 = pNoteEvent->time(); // Last NOTEON... const unsigned long t2 = pEvent->time(); // This NOTEON/OFF. if (t2 > t1) { pNoteEvent->setDuration(t2 - t1); if (m_duration < t2) m_duration = t2; } else { pNoteEvent->setDuration(m_duration - t1); } m_notes.erase(iter); } // Add event to a channel sequence, in time sort order. void qtractorMidiSequence::addEvent ( qtractorMidiEvent *pEvent ) { // Adjust to sequence offset... pEvent->adjustTime(m_iTimeOffset); // NOTE: Find previous note event and compute duration... if (pEvent->type() == qtractorMidiEvent::NOTEOFF) { // NOTEOFF: Won't own this any longer... addNoteEvent(pEvent); delete pEvent; return; } else if (pEvent->type() == qtractorMidiEvent::NOTEON) { // NOTEON: Just add to lingering notes... addNoteEvent(pEvent); m_notes.insert(pEvent->note(), pEvent); } else if (pEvent->type() == qtractorMidiEvent::SYSEX) { // SYSEX: add enough slack... const unsigned long t1 = pEvent->time() + (m_iTicksPerBeat >> 3); if (m_duration < t1) m_duration = t1; } // Add it... insertEvent(pEvent); } // Insert event in correct time sort order. void qtractorMidiSequence::insertEvent ( qtractorMidiEvent *pEvent ) { // Find the proper position in time sequence... qtractorMidiEvent *pEventAfter = m_events.last(); while (pEventAfter && pEventAfter->time() > pEvent->time()) pEventAfter = pEventAfter->prev(); // Insert it... if (pEventAfter) m_events.insertAfter(pEvent, pEventAfter); else m_events.prepend(pEvent); unsigned long iTime = pEvent->time(); // NOTEON: Keep note stats and make it pending on a NOTEOFF... if (pEvent->type() == qtractorMidiEvent::NOTEON) { const unsigned char note = pEvent->note(); if (m_noteMin > note || m_noteMin == 0) m_noteMin = note; if (m_noteMax < note || m_noteMax == 0) m_noteMax = note; iTime += pEvent->duration(); } if (m_duration < iTime) m_duration = iTime; } // Unlink event from a channel sequence. void qtractorMidiSequence::unlinkEvent ( qtractorMidiEvent *pEvent ) { m_events.unlink(pEvent); } // Remove event from a channel sequence. void qtractorMidiSequence::removeEvent ( qtractorMidiEvent *pEvent ) { m_events.remove(pEvent); } // Sequence closure method. void qtractorMidiSequence::close (void) { // Commit sequence length... if (m_duration < m_iTimeLength) m_duration = m_iTimeLength; else if (m_iTimeLength == 0) m_iTimeLength = m_duration; // Finish all pending notes... NoteOns::ConstIterator iter = m_notes.constBegin(); const NoteOns::ConstIterator& iter_end = m_notes.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = iter.value(); pEvent->setDuration(m_duration - pEvent->time()); } // Reset all pending notes. m_notes.clear(); } // Replace events from another sequence in given range. void qtractorMidiSequence::replaceEvents ( qtractorMidiSequence *pSeq, unsigned long iTimeOffset, unsigned long iTimeLength ) { // Sanitize range as default... if (iTimeOffset < 1 && iTimeLength < 1) { iTimeOffset = pSeq->timeOffset(); iTimeLength = pSeq->timeLength(); } // Reset bank/prog settings anyway... setBankSelMethod(pSeq->bankSelMethod()); setBank(pSeq->bank()); setProg(pSeq->prog()); // Set the given replacement range... const unsigned short iTicksPerBeat = pSeq->ticksPerBeat(); const unsigned long iTimeStart = timeq(iTimeOffset, iTicksPerBeat); const unsigned long iTimeEnd = timeq(iTimeOffset + iTimeLength, iTicksPerBeat); // Remove existing events in the given range... qtractorMidiEvent *pEvent = m_events.first(); while (pEvent) { qtractorMidiEvent *pNextEvent = pEvent->next(); if (pEvent->time() >= iTimeStart && pEvent->time() < iTimeEnd) removeEvent(pEvent); pEvent = pNextEvent; } // Insert new (cloned and adjusted) ones... for (pEvent = pSeq->events().first(); pEvent; pEvent = pEvent->next()) { qtractorMidiEvent *pNewEvent = new qtractorMidiEvent(*pEvent); pNewEvent->setTime(timeq(iTimeOffset + pEvent->time(), iTicksPerBeat)); if (pEvent->type() == qtractorMidiEvent::NOTEON) pNewEvent->setDuration(timeq(pEvent->duration(), iTicksPerBeat)); insertEvent(pNewEvent); } // Done. } // Copy all events from another sequence (raw-copy). void qtractorMidiSequence::copyEvents ( qtractorMidiSequence *pSeq ) { // Remove existing events. m_events.clear(); const unsigned short iTicksPerBeat = pSeq->ticksPerBeat(); // Clone new ones... qtractorMidiEvent *pEvent = pSeq->events().first(); for (; pEvent; pEvent = pEvent->next()) { qtractorMidiEvent *pNewEvent = new qtractorMidiEvent(*pEvent); pNewEvent->setTime(timeq(pEvent->time(), iTicksPerBeat)); if (pEvent->type() == qtractorMidiEvent::NOTEON) pNewEvent->setDuration(timeq(pEvent->duration(), iTicksPerBeat)); m_events.append(pNewEvent); } // Done. } // end of qtractorMidiSequence.cpp qtractor-1.5.11/src/PaxHeaders/qtractorPluginFactory.h0000644000000000000000000000013215124701674020031 xustar0030 mtime=1767080892.798263432 30 atime=1767080892.798263432 30 ctime=1767080892.798263432 qtractor-1.5.11/src/qtractorPluginFactory.h0000644000175000001440000001433615124701674020030 0ustar00rncbcusers// qtractorPluginFactory.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorPluginFactory_h #define __qtractorPluginFactory_h #include "qtractorPlugin.h" #include #include //---------------------------------------------------------------------------- // qtractorPluginFactory -- Plugin path helper. // class qtractorPluginFactory : public QObject { Q_OBJECT public: // Constructor. qtractorPluginFactory(QObject *pParent = nullptr); // Destructor. ~qtractorPluginFactory(); // Plugin files/paths resgistry. typedef QHash Paths; // Plugin type hint accessors. void setTypeHint(qtractorPluginType::Hint typeHint) { m_typeHint = typeHint; } qtractorPluginType::Hint typeHint() const { return m_typeHint; } // Plugin rescan force flag accessors. void setRescan(bool bRescan) { m_bRescan = bRescan; } bool isRescan() const { return m_bRescan; } // Executive methods. void scan(); // Plugin types registry. typedef QList Types; const Types& types() const { return m_types; } // Type register method. void addType(qtractorPluginType *pType) { m_types.append(pType); } // Type list reset methods. void clear(); void clearAll(qtractorPluginType::Hint typeHint); // Global plugin-paths executive methods. QStringList pluginPaths(qtractorPluginType::Hint typeHint); void updatePluginPaths(qtractorPluginType::Hint typeHint); // Plugin factory method. static qtractorPlugin *createPlugin( qtractorPluginList *pList, const QString& sFilename, unsigned long iIndex, qtractorPluginType::Hint typeHint); // Blacklist accessors. void setBlacklist(const QStringList& blacklist); const QStringList& blacklist() const; // Singleton instance accessor. static qtractorPluginFactory *getInstance(); signals: // Scan progress feedback. void scanned(int iPercent); protected: // Recursive plugin path/file inventory method (return partial file count). int addFiles(qtractorPluginType::Hint typeHint, const QStringList& paths); int addFiles(qtractorPluginType::Hint typeHint, const QString& sPath); // Plugin type listing methods. bool addTypes(qtractorPluginType::Hint typeHint, const QString& sFilename); bool addTypes(qtractorPluginType::Hint typeHint, qtractorPluginFile *pFile, unsigned long iIndex); // Absolute cache file path registration. void addCacheFilePath(qtractorPluginType::Hint typeHint); // Blacklist file paths. QString blacklistTempFilePath() const; QString blacklistDataFilePath() const; // Simple blacklist file I/O methods. bool readBlacklist(QFile& file); bool writeBlacklist(QFile& file, const QStringList& blacklist) const; // Generic plugin-scan factory method. int startScan(qtractorPluginType::Hint typeHint); // Plugin scan reset method. void reset(); private: // Instance variables. qtractorPluginType::Hint m_typeHint; // Thether the next scan is deeper. bool m_bRescan; // Internal plugin-paths. Paths m_paths; // Internal plugin-paths. Paths m_files; // Internal plugin types list. Types m_types; // Plugin blacklist. QStringList m_blacklist; // Scan (out-of-process) clients. class Scanner; typedef QHash Scanners; Scanners m_scanners; typedef QHash CacheFilePaths; // List of active cache scan results. CacheFilePaths m_cacheFilePaths; // Pseudo-singleton instance. static qtractorPluginFactory *g_pPluginFactory; }; //---------------------------------------------------------------------------- // qtractorPluginFactory::Scanner -- Plugin scan proxy (out-of-process client). // class qtractorPluginFactory::Scanner : public QProcess { Q_OBJECT public: // ctor. Scanner(qtractorPluginType::Hint typeHint, QObject *pParent = nullptr); // Open/close method. bool open(const QString& sCacheFilePath, int iDummyPluginHash = 0); void close(); // Service methods. bool addTypes(qtractorPluginType::Hint typeHint, const QString& sFilename); // Cached files list accessor. QStringList files() const; // Cache hash result. int dummyPluginHash() const; protected slots: // Service slots. void stdout_slot(); void stderr_slot(); void exit_slot(int exitCode, QProcess::ExitStatus exitStatus); protected: // Scan start method. bool start(); // Service methods (internal) bool addTypes(const QStringList& list, bool bDummyPluginType = false); private: // Instance scanner name. qtractorPluginType::Hint m_typeHint; // Instance state. volatile int m_iExitStatus; // Cache file object. QFile m_file; // Cache hash list. QHash m_list; // Cache hash result. int m_iDummyPluginHash; }; //---------------------------------------------------------------------------- // qtractorDummyPluginType -- Dummy plugin type instance. // class qtractorDummyPluginType : public qtractorPluginType { public: // Constructor. qtractorDummyPluginType( const QString& sText, unsigned long iIndex, Hint typeHint); // Must be overridden methods. bool open(); void close(); // Dummy plugin filename (virtual override). QString filename() const { return m_sFilename; } // Factory method (static) static qtractorDummyPluginType *createType(const QString& sText); private: // Instance variables. QString m_sFilename; }; #endif // __qtractorPluginFactory_h // end of qtractorPluginFactory.h qtractor-1.5.11/src/PaxHeaders/qtractorEngine.cpp0000644000000000000000000000013215124701674017003 xustar0030 mtime=1767080892.784263491 30 atime=1767080892.784263491 30 ctime=1767080892.784263491 qtractor-1.5.11/src/qtractorEngine.cpp0000644000175000001440000006462215124701674017005 0ustar00rncbcusers// qtractorEngine.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorEngine.h" #include "qtractorSession.h" #include "qtractorSessionCursor.h" #include "qtractorMidiControlObserver.h" #include "qtractorEngineCommand.h" #include "qtractorMonitor.h" #include "qtractorDocument.h" #include "qtractorCurveFile.h" #include "qtractorFileList.h" #include #include //---------------------------------------------------------------------- // class qtractorEngine -- Abstract device engine instance (singleton). // // Constructor. qtractorEngine::qtractorEngine ( qtractorSession *pSession, qtractorTrack::TrackType syncType ) { m_pSession = pSession; m_pSessionCursor = m_pSession->createSessionCursor(0, syncType); m_bActivated = false; m_bPlaying = false; m_buses.setAutoDelete(true); m_busesEx.setAutoDelete(false); m_bBuses2 = false; } // Destructor. qtractorEngine::~qtractorEngine (void) { clear(); if (m_pSessionCursor) delete m_pSessionCursor; } // Buses list clear. void qtractorEngine::clear (void) { m_buses.clear(); m_busesEx.clear(); m_bBuses2 = false; m_buses2.clear(); } // Session accessor. qtractorSession *qtractorEngine::session (void) const { return m_pSession; } // Session cursor accessor. qtractorSessionCursor *qtractorEngine::sessionCursor (void) const { return m_pSessionCursor; } // Engine type accessor. qtractorTrack::TrackType qtractorEngine::syncType (void) const { return m_pSessionCursor->syncType(); } // Client name accessor. const QString& qtractorEngine::clientName (void) const { return m_pSession->clientName(); } // Activation status accessor. bool qtractorEngine::isActivated(void) const { return m_bActivated; } // Buses list management methods. const qtractorList& qtractorEngine::buses (void) const { return m_buses; } // Add a bus to a device engine. void qtractorEngine::addBus ( qtractorBus *pBus, qtractorBus *pAfterBus ) { m_buses.insertAfter(pBus, pAfterBus); const int iAfterBus = (pAfterBus ? m_buses2.indexOf(pAfterBus) : -1); if (iAfterBus >= 0) m_buses2.insert(iAfterBus + 1, pBus); else m_buses2.append(pBus); } // Remove a bus from a device. void qtractorEngine::removeBus ( qtractorBus *pBus ) { m_buses2.removeAll(pBus); m_buses.remove(pBus); } // Move bus on a device engine. void qtractorEngine::moveBus ( qtractorBus *pBus, qtractorBus *pAfterBus ) { m_buses.unlink(pBus); if (pAfterBus) m_buses.insertAfter(pBus, pAfterBus); else m_buses.append(pBus); m_bBuses2 = true; } // Find a device bus by name qtractorBus *qtractorEngine::findBus ( const QString& sBusName ) const { for (qtractorBus *pBus = m_buses.first(); pBus; pBus = pBus->next()) { if (pBus->busName() == sBusName) return pBus; } return nullptr; } // Find an input bus by name qtractorBus *qtractorEngine::findInputBus ( const QString& sInputBusName ) const { for (qtractorBus *pBus = m_buses.first(); pBus; pBus = pBus->next()) { if ((pBus->busMode() & qtractorBus::Input) && pBus->busName() == sInputBusName) return pBus; } return nullptr; } // Find an output bus by name qtractorBus *qtractorEngine::findOutputBus ( const QString& sOutputBusName ) const { for (qtractorBus *pBus = m_buses.first(); pBus; pBus = pBus->next()) { if ((pBus->busMode() & qtractorBus::Output) && pBus->busName() == sOutputBusName) return pBus; } return nullptr; } // Exo-buses list management methods. const qtractorList& qtractorEngine::busesEx (void) const { return m_busesEx; } // Add an exo-bus to a device engine. void qtractorEngine::addBusEx ( qtractorBus *pBus ) { m_busesEx.append(pBus); } // Remove an exo-bus from a device. void qtractorEngine::removeBusEx ( qtractorBus *pBus ) { for (qtractorBus *pBusEx = m_busesEx.first(); pBusEx; pBusEx = pBusEx->next()) { if (pBusEx == pBus) { m_busesEx.remove(pBus); break; } } } // Find a exo-device bus by name qtractorBus *qtractorEngine::findBusEx ( const QString& sBusName ) const { for (qtractorBus *pBusEx = m_busesEx.first(); pBusEx; pBusEx = pBusEx->next()) { if (pBusEx->busName() == sBusName) return pBusEx; } return nullptr; } // Front-end/UI buses accessors. // const QList& qtractorEngine::buses2 (void) const { return m_buses2; } void qtractorEngine::moveBus2 ( qtractorBus *pBus, int iDelta ) { const int iBus2 = m_buses2.indexOf(pBus) + iDelta; if (iBus2 >= 0 && iBus2 < m_buses2.count()) { m_buses2.removeAll(pBus); m_buses2.insert(iBus2, pBus); m_bBuses2 = true; } } void qtractorEngine::setBuses2List ( const QStringList& list ) { m_bBuses2 = !list.isEmpty(); if (m_bBuses2) { qtractorBus *pAfterBus = nullptr; QStringListIterator iter(list); while (iter.hasNext()) { const QString& sBusName = iter.next(); qtractorBus *pBus = findBus(sBusName); if (pBus) { moveBus(pBus, pAfterBus); pAfterBus = pBus; } } } else { m_buses2.clear(); qtractorBus *pBus = m_buses.first(); while (pBus) { m_buses2.append(pBus); pBus = pBus->next(); } } } QStringList qtractorEngine::buses2List (void) const { QStringList list; if (m_bBuses2) { QListIterator iter(m_buses2); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus) list.append(pBus->busName()); } } return list; } QStringList qtractorEngine::loadBuses2List ( qtractorDocument *pDocument, QDomElement *pElement, const QString& sTagName ) { QStringList list; for (QDomNode nChild = pElement->firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert audio-bus item to element... QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; if (eChild.tagName() == sTagName) { const QString& sBusName = eChild.text(); if (!sBusName.isEmpty()) list.append(eChild.text()); } } return list; } bool qtractorEngine::saveBuses2List ( qtractorDocument *pDocument, QDomElement *pElement, const QString& sTagName, const QStringList& list ) const { for (qtractorBus *pBus = m_buses.first(); pBus; pBus = pBus->next()) { const QString& sBusName = pBus->busName(); if (!sBusName.isEmpty()) pDocument->saveTextElement(sTagName, sBusName, pElement); } return true; } // Device engine activation method. bool qtractorEngine::open (void) { // Update the session cursor tracks... m_pSessionCursor->resetClips(); m_pSessionCursor->reset(); // Open all buses (allocated and register ports...) qtractorBus *pBus = m_buses.first(); while (pBus) { if (!pBus->open()) return false; pBus = pBus->next(); } // Now's the right time to activate... if (!activate()) return false; // We're now ready and running... m_bActivated = true; return true; } // Device engine deactivation method. void qtractorEngine::close (void) { // Save current activation state... const bool bActivated = m_bActivated; // We're stopping now... m_bActivated = false; if (bActivated) { // Deactivate the derived engine first. deactivate(); // Close all dependent buses... for (qtractorBus *pBus = m_buses.first(); pBus; pBus = pBus->next()) { pBus->close(); } } // Clean-up everything, finally clean(); } // Engine state methods. void qtractorEngine::setPlaying ( bool bPlaying ) { if (bPlaying && !m_bPlaying) { m_pSessionCursor->reset(); m_bPlaying = start(); } else if (!bPlaying && m_bPlaying) { m_bPlaying = false; stop(); } } bool qtractorEngine::isPlaying(void) const { return m_bPlaying; } // Retrieve/restore all connections, on all buses. // return the total number of effective (re)connection attempts... int qtractorEngine::updateConnects (void) { // It must be activated, sure... if (!isActivated()) return 0; // On all dependable buses... int iUpdate = 0; iUpdate += updateConnects(m_buses.first()); iUpdate += updateConnects(m_busesEx.first()); // Done. return iUpdate; } int qtractorEngine::updateConnects ( qtractorBus *pBus ) { int iUpdate = 0; for (; pBus; pBus = pBus->next()) { // Input connections... if (pBus->busMode() & qtractorBus::Input) { iUpdate += pBus->updateConnects( qtractorBus::Input, pBus->inputs(), true); } // Output connections... if (pBus->busMode() & qtractorBus::Output) { iUpdate += pBus->updateConnects( qtractorBus::Output, pBus->outputs(), true); } } return iUpdate; } // Clear/reset all pending connections. void qtractorEngine::clearConnects (void) { qtractorBus *pBus; pBus = m_buses.first(); for (; pBus; pBus = pBus->next()) { pBus->outputs().clear(); pBus->inputs().clear(); } pBus = m_busesEx.first(); for (; pBus; pBus = pBus->next()) { pBus->outputs().clear(); pBus->inputs().clear(); } } //---------------------------------------------------------------------- // class qtractorBus -- Managed ALSA sequencer port set // // Constructor. qtractorBus::qtractorBus ( qtractorEngine *pEngine, const QString& sBusName, BusMode busMode, bool bMonitor ) { m_pEngine = pEngine; m_sBusName = sBusName; m_busMode = busMode; if (m_busMode & Ex) { m_pMonitorSubject = nullptr; } else { m_pMonitorSubject = new qtractorSubject(); m_pMonitorSubject->setToggled(true); } if (m_busMode & Ex) { m_pMonitorObserver = nullptr; } else { m_pMonitorObserver = new qtractorMidiControlObserver(m_pMonitorSubject); m_pMonitorObserver->setValue(bMonitor ? 1.0f : 0.0f); } } // Destructor. qtractorBus::~qtractorBus (void) { if (m_pMonitorObserver) delete m_pMonitorObserver; if (m_pMonitorSubject) delete m_pMonitorSubject; qDeleteAll(m_controllers_out); m_controllers_out.clear(); qDeleteAll(m_controllers_in); m_controllers_in.clear(); } // Engine accessor. qtractorEngine *qtractorBus::engine (void) const { return m_pEngine; } // Bus type accessor. qtractorTrack::TrackType qtractorBus::busType (void) const { return (m_pEngine ? m_pEngine->syncType() : qtractorTrack::None); } // Bus name accessors. void qtractorBus::setBusName ( const QString& sBusName ) { m_sBusName = sBusName; updateBusName(); } const QString& qtractorBus::busName (void) const { return m_sBusName; } // Bus name change event. [virtual] void qtractorBus::updateBusName (void) { if (m_pMonitorSubject) m_pMonitorSubject->setName(QObject::tr("%1 Monitor").arg(m_sBusName)); } // Bus mode property accessor. void qtractorBus::setBusMode ( qtractorBus::BusMode busMode ) { m_busMode = busMode; updateBusMode(); } qtractorBus::BusMode qtractorBus::busMode (void) const { return m_busMode; } // Pass-thru mode accessor. void qtractorBus::setMonitor ( bool bMonitor ) { if (m_pMonitorSubject) m_pMonitorSubject->setValue(bMonitor ? 1.0f : 0.0f); } bool qtractorBus::isMonitor (void) const { return (m_pMonitorSubject ? (m_pMonitorSubject->value() > 0.0f) : false); } // Bus state (monitor) button setup. qtractorSubject *qtractorBus::monitorSubject (void) const { return m_pMonitorSubject; } qtractorMidiControlObserver *qtractorBus::monitorObserver (void) const { return m_pMonitorObserver; } // Bus state (monitor) notifier (proto-slot). void qtractorBus::monitorChangeNotify ( bool bOn ) { #ifdef CONFIG_DEBUG qDebug("qtractorBus[%p]::monitorChangeNotify(%d)", this, int(bOn)); #endif // Put it in the form of an undoable command... qtractorSession *pSession = m_pEngine->session(); if (pSession) pSession->execute( new qtractorBusMonitorCommand(this, bOn)); } // Load bus (monitor, gain, pan) controllers (MIDI). void qtractorBus::loadControllers ( QDomElement *pElement, BusMode busMode ) { if (busMode & Input) qtractorMidiControl::loadControllers(pElement, m_controllers_in); else qtractorMidiControl::loadControllers(pElement, m_controllers_out); } // Save bus (monitor, gain, pan) controllers (MIDI). void qtractorBus::saveControllers ( qtractorDocument *pDocument, QDomElement *pElement, BusMode busMode ) const { if (m_pMonitorObserver == nullptr) return; qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; qtractorMonitor *pMonitor = nullptr; if (busMode & Input) pMonitor = monitor_in(); else pMonitor = monitor_out(); if (pMonitor == nullptr) return; qtractorMidiControl::Controllers controllers; if (busMode & Input) // It suffices for Duplex... if (pMidiControl->isMidiObserverMapped(m_pMonitorObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = m_pMonitorObserver->subject()->name(); pController->index = 0; // 0=MonitorObserver pController->ctype = m_pMonitorObserver->type(); pController->channel = m_pMonitorObserver->channel(); pController->param = m_pMonitorObserver->param(); pController->logarithmic = m_pMonitorObserver->isLogarithmic(); pController->feedback = m_pMonitorObserver->isFeedback(); pController->invert = m_pMonitorObserver->isInvert(); pController->hook = m_pMonitorObserver->isHook(); pController->latch = m_pMonitorObserver->isLatch(); controllers.append(pController); } qtractorMidiControlObserver *pPanObserver = pMonitor->panningObserver(); if (pMidiControl->isMidiObserverMapped(pPanObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = pPanObserver->subject()->name(); pController->index = 1; // 1=PanObserver pController->ctype = pPanObserver->type(); pController->channel = pPanObserver->channel(); pController->param = pPanObserver->param(); pController->logarithmic = pPanObserver->isLogarithmic(); pController->feedback = pPanObserver->isFeedback(); pController->invert = pPanObserver->isInvert(); pController->hook = pPanObserver->isHook(); pController->latch = pPanObserver->isLatch(); controllers.append(pController); } qtractorMidiControlObserver *pGainObserver = pMonitor->gainObserver(); if (pMidiControl->isMidiObserverMapped(pGainObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = pGainObserver->subject()->name(); pController->index = 2; // 2=GainObserver pController->ctype = pGainObserver->type(); pController->channel = pGainObserver->channel(); pController->param = pGainObserver->param(); pController->logarithmic = pGainObserver->isLogarithmic(); pController->feedback = pGainObserver->isFeedback(); pController->invert = pGainObserver->isInvert(); pController->hook = pGainObserver->isHook(); pController->latch = pGainObserver->isLatch(); controllers.append(pController); } qtractorMidiControl::saveControllers(pDocument, pElement, controllers); qDeleteAll(controllers); controllers.clear(); } // Map bus (monitor, gain, pan) controllers (MIDI). void qtractorBus::mapControllers ( BusMode busMode ) { if (m_pMonitorObserver == nullptr) return; qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; qtractorMonitor *pMonitor = nullptr; if (busMode & Input) pMonitor = monitor_in(); else pMonitor = monitor_out(); if (pMonitor == nullptr) return; qtractorMidiControl::Controllers& controllers = (busMode & Input ? m_controllers_in : m_controllers_out); QListIterator iter(controllers); while (iter.hasNext()) { qtractorMidiControl::Controller *pController = iter.next(); qtractorMidiControlObserver *pObserver = nullptr; switch (pController->index) { case 0: // 0=MonitorObserver pObserver = monitorObserver(); break; case 1: // 1=PanObserver pObserver = pMonitor->panningObserver(); break; case 2: // 2=GainObserver pObserver = pMonitor->gainObserver(); break; } if (pObserver) { pObserver->setType(pController->ctype); pObserver->setChannel(pController->channel); pObserver->setParam(pController->param); pObserver->setLogarithmic(pController->logarithmic); pObserver->setFeedback(pController->feedback); pObserver->setInvert(pController->invert); pObserver->setHook(pController->hook); pObserver->setLatch(pController->latch); pMidiControl->mapMidiObserver(pObserver); } } qDeleteAll(controllers); controllers.clear(); } // Load bus automation curves (monitor, gain, pan). void qtractorBus::loadCurveFile ( QDomElement *pElement, BusMode /*busMode*/, qtractorCurveFile *pCurveFile ) { if (pCurveFile) pCurveFile->load(pElement); } // Save bus automation curves (monitor, gain, pan). void qtractorBus::saveCurveFile ( qtractorDocument *pDocument, QDomElement *pElement, BusMode busMode, qtractorCurveFile *pCurveFile ) const { if (m_pMonitorSubject == nullptr) return; if (pCurveFile == nullptr) return; qtractorCurveList *pCurveList = pCurveFile->list(); if (pCurveList == nullptr) return; qtractorSession *pSession = m_pEngine->session(); if (pSession == nullptr) return; QString sBusName(busName()); qtractorMonitor *pMonitor = nullptr; if (busMode & Input) { pMonitor = monitor_in(); sBusName += "_in"; } else { pMonitor = monitor_out(); sBusName += "_out"; } if (pMonitor == nullptr) return; pCurveFile->clear(); pCurveFile->setBaseDir(pSession->sessionDir()); qtractorCurve *pCurve; if (busMode & Input) { // It suffices for Duplex... pCurve = monitorSubject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pCurve->subject()->name(); pCurveItem->index = 0; // 0=MonitorSubject pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->channel = 0; pCurveItem->param = 80; // 80=General Purpose Button 1 (on/off) pCurveItem->mode = pCurve->mode(); pCurveItem->process = pCurve->isProcess(); pCurveItem->capture = pCurve->isCapture(); pCurveItem->locked = pCurve->isLocked(); pCurveItem->logarithmic = pCurve->isLogarithmic(); pCurveItem->color = pCurve->color(); pCurveItem->subject = pCurve->subject(); pCurveFile->addItem(pCurveItem); }} pCurve = pMonitor->panningSubject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pCurve->subject()->name(); pCurveItem->index = 1; // 1=PanningSubject pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->channel = 0; pCurveItem->param = 10; // 10=Pan Position (coarse) pCurveItem->mode = pCurve->mode(); pCurveItem->process = pCurve->isProcess(); pCurveItem->capture = pCurve->isCapture(); pCurveItem->locked = pCurve->isLocked(); pCurveItem->logarithmic = pCurve->isLogarithmic(); pCurveItem->color = pCurve->color(); pCurveItem->subject = pCurve->subject(); pCurveFile->addItem(pCurveItem); } pCurve = pMonitor->gainSubject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pCurve->subject()->name(); pCurveItem->index = 2; // 2=GainSubject pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->channel = 0; pCurveItem->param = 7; // 7=Volume (coarse) pCurveItem->mode = pCurve->mode(); pCurveItem->process = pCurve->isProcess(); pCurveItem->capture = pCurve->isCapture(); pCurveItem->logarithmic = pCurve->isLogarithmic(); pCurveItem->locked = pCurve->isLocked(); pCurveItem->color = pCurve->color(); pCurveItem->subject = pCurve->subject(); pCurveFile->addItem(pCurveItem); } if (pCurveFile->isEmpty()) return; const QString sBaseName(sBusName + "_curve"); const bool bTemporary = pDocument->isTemporary(); const QString& sFilename = pSession->createFilePath(sBaseName, "mid", !bTemporary); pSession->files()->addFileItem(qtractorFileList::Midi, sFilename, bTemporary); pCurveFile->setFilename(sFilename); pCurveFile->save(pDocument, pElement, pSession->timeScale()); } // Apply bus automation curves (monitor, gain, pan). void qtractorBus::applyCurveFile ( BusMode busMode, qtractorCurveFile *pCurveFile ) const { if (m_pMonitorSubject == nullptr) return; if (pCurveFile == nullptr) return; if (pCurveFile->items().isEmpty()) return; qtractorCurveList *pCurveList = pCurveFile->list(); if (pCurveList == nullptr) return; qtractorSession *pSession = m_pEngine->session(); if (pSession == nullptr) return; qtractorMonitor *pMonitor = nullptr; if (busMode & Input) pMonitor = monitor_in(); else pMonitor = monitor_out(); if (pMonitor == nullptr) return; pCurveFile->setBaseDir(pSession->sessionDir()); QListIterator iter(pCurveFile->items()); while (iter.hasNext()) { qtractorCurveFile::Item *pCurveItem = iter.next(); switch (pCurveItem->index) { case 0: // 0=MonitorSubject pCurveItem->subject = monitorSubject(); break; case 1: // 1=PanSubject pCurveItem->subject = pMonitor->panningSubject(); break; case 2: // 2=GainSubject pCurveItem->subject = pMonitor->gainSubject(); break; } } pCurveFile->apply(pSession->timeScale()); } // Document element methods. bool qtractorBus::loadConnects ( ConnectList& connects, qtractorDocument * /*pDocument*/, QDomElement *pElement ) { connects.clear(); // Load map items... for (QDomNode nChild = pElement->firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert node to element... QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; // Load (other) track properties.. if (eChild.tagName() == "connect") { ConnectItem *pItem = new ConnectItem; pItem->index = eChild.attribute("index").toUShort(); for (QDomNode nConnect = eChild.firstChild(); !nConnect.isNull(); nConnect = nConnect.nextSibling()) { // Convert connect node to element... QDomElement eConnect = nConnect.toElement(); if (eConnect.isNull()) continue; // Add this one to map... if (eConnect.tagName() == "client") { const QString& sClient = eConnect.text(); const QString& sClientName = sClient.section(':', 1); if (sClientName.isEmpty()) { pItem->clientName = sClient; } else { // pItem->client = sClient.section(':', 0, 0).toInt(); pItem->clientName = sClientName; } } else if (eConnect.tagName() == "port") { const QString& sPort = eConnect.text(); const QString& sPortName = sPort.section(':', 1); if (sPortName.isEmpty()) { pItem->portName = sPort; } else { // pItem->port = sPort.section(':', 0, 0).toInt(); pItem->portName = sPortName; } } } connects.append(pItem); } } return true; } bool qtractorBus::saveConnects ( ConnectList& connects, qtractorDocument *pDocument, QDomElement *pElement ) { // Save connect items... QListIterator iter(connects); while (iter.hasNext()) { ConnectItem *pItem = iter.next(); QDomElement eItem = pDocument->document()->createElement("connect"); eItem.setAttribute("index", QString::number(pItem->index)); QString sClient; if (pItem->client >= 0) sClient += QString::number(pItem->client) + ':'; sClient += pItem->clientName; pDocument->saveTextElement("client", sClient, &eItem); QString sPort; if (pItem->port >= 0) sPort += QString::number(pItem->port) + ':'; sPort += pItem->portName; pDocument->saveTextElement("port", sPort, &eItem); pElement->appendChild(eItem); } return true; } // Bus mode textual helper methods. qtractorBus::BusMode qtractorBus::busModeFromText ( const QString& sText ) { BusMode busMode = None; if (sText == "input") busMode = Input; else if (sText == "output") busMode = Output; else if (sText == "duplex") busMode = Duplex; return busMode; } QString qtractorBus::textFromBusMode ( BusMode busMode ) { QString sText; switch (busMode) { case Input: sText = "input"; break; case Output: sText = "output"; break; case Duplex: sText = "duplex"; break; case None: default: sText = "none"; break; } return sText; } // Bus connections snapshot executive mthods. int qtractorBus::Connects::save ( qtractorBus *pBus ) { m_sBusName = pBus->busName(); m_busMode = pBus->busMode(); if (m_busMode & Input) pBus->updateConnects(Input, m_inputs); if (m_busMode & Output) pBus->updateConnects(Output, m_outputs); return m_inputs.count() + m_outputs.count(); } // Bus connections snapshot executive mthods. int qtractorBus::Connects::load( qtractorBus *pBus ) { if (pBus->busName() != m_sBusName || pBus->busMode() != m_busMode) return 0; if (m_busMode & Input) pBus->inputs().copy(m_inputs); if (m_busMode & Output) pBus->outputs().copy(m_outputs); return pBus->inputs().count() + pBus->outputs().count(); } // Bus connections snapshot cleaner. void qtractorBus::Connects::clear (void) { m_sBusName.clear(); m_busMode = None; m_inputs.clear(); m_outputs.clear(); } // Engine connections snapshot executive mthods. bool qtractorBus::Connections::load ( qtractorEngine *pEngine ) { int iUpdate = 0; QListIterator iter(m_list); while (iter.hasNext()) { Connects *pConnect = iter.next(); const QString& sBusName = pConnect->busName(); const BusMode busMode = pConnect->busMode(); qtractorBus *pBus = nullptr; if (busMode & Ex) pBus = pEngine->findBusEx(sBusName); else pBus = pEngine->findBus(sBusName); if (pBus) iUpdate += pConnect->load(pBus); } return (iUpdate > 0); } bool qtractorBus::Connections::save ( qtractorEngine *pEngine ) { int iUpdate = 0; iUpdate += save(pEngine->buses().first()); iUpdate += save(pEngine->busesEx().first()); return (iUpdate > 0); } int qtractorBus::Connections::save ( qtractorBus *pBus ) { int iUpdate = 0; for (; pBus; pBus = pBus->next()) { Connects *pConnects = new Connects(); if (pConnects->save(pBus) > 0) { m_list.append(pConnects); ++iUpdate; } else delete pConnects; } return iUpdate; } // Generic connections snapshot cleaner. void qtractorBus::Connections::clear (void) { qDeleteAll(m_list); m_list.clear(); } // end of qtractorEngine.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiEngine.cpp0000644000000000000000000000013215124701674017606 xustar0030 mtime=1767080892.793263454 30 atime=1767080892.793263454 30 ctime=1767080892.793263454 qtractor-1.5.11/src/qtractorMidiEngine.cpp0000644000175000001440000045354315124701674017614 0ustar00rncbcusers// qtractorMidiEngine.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorMidiEngine.h" #include "qtractorMidiMonitor.h" #include "qtractorMidiEvent.h" #include "qtractorSession.h" #include "qtractorSessionCursor.h" #include "qtractorDocument.h" #include "qtractorAudioEngine.h" #include "qtractorMidiClip.h" #include "qtractorMidiManager.h" #include "qtractorMidiControl.h" #include "qtractorMidiTimer.h" #include "qtractorMidiSysex.h" #include "qtractorMidiRpn.h" #include "qtractorPlugin.h" #include "qtractorInsertPlugin.h" #include "qtractorMidiEditCommand.h" #include #include #include #include #include #include #include #include #include // Specific controller definitions #define BANK_SELECT_MSB 0x00 #define BANK_SELECT_LSB 0x20 #define ALL_SOUND_OFF 0x78 #define ALL_CONTROLLERS_OFF 0x79 #define ALL_NOTES_OFF 0x7b #define CHANNEL_VOLUME 0x07 #define CHANNEL_PANNING 0x0a // Audio vs. MIDI time drift cycle #define DRIFT_CHECK 8 #define DRIFT_CHECK_MIN (DRIFT_CHECK >> 2) #define DRIFT_CHECK_MAX (DRIFT_CHECK << 1) //---------------------------------------------------------------------- // class qtractorMidiInputRpn -- MIDI RPN/NRPN input parser (singleton). // class qtractorMidiInputRpn : public qtractorMidiRpn { public: // Constructor. qtractorMidiInputRpn(); // Encoder. bool process (const snd_seq_event_t *ev); // Decoder. bool dequeue (snd_seq_event_t *ev); }; //---------------------------------------------------------------------- // class qtractorMidiInputThread -- MIDI input thread (singleton). // class qtractorMidiInputThread : public QThread { public: // Constructor. qtractorMidiInputThread(qtractorMidiEngine *pMidiEngine); // Destructor. ~qtractorMidiInputThread(); // Thread run state accessors. void setRunState(bool bRunState); bool runState() const; protected: // The main thread executive. void run(); private: // The thread launcher engine. qtractorMidiEngine *m_pMidiEngine; // Whether the thread is logically running. bool m_bRunState; }; //---------------------------------------------------------------------- // class qtractorMidiOutputThread -- MIDI output thread (singleton). // class qtractorMidiOutputThread : public QThread { public: // Constructor. qtractorMidiOutputThread(qtractorMidiEngine *pMidiEngine); // Destructor. ~qtractorMidiOutputThread(); // Thread run state accessors. void setRunState(bool bRunState); bool runState() const; // MIDI track output process resync. void trackSync(qtractorTrack *pTrack, unsigned long iFrameStart); // MIDI metronome output process resync. void metroSync(unsigned long iFrameStart); // MIDI output process cycle iteration (locked). void processSync(); // MIDI output flush/drain (locked). void flushSync(); // MIDI queue reset (locked). void resetSync(); // Wake from executive wait condition. void sync(); protected: // The main thread executive. void run(); private: // The thread launcher engine. qtractorMidiEngine *m_pMidiEngine; // Whether the thread is logically running. bool m_bRunState; // Thread synchronization objects. QMutex m_mutex; QWaitCondition m_cond; }; //---------------------------------------------------------------------- // class qtractorMidiPlayerThread -- MIDI player thread. // class qtractorMidiPlayer; class qtractorMidiPlayerThread : public QThread { public: // Constructor. qtractorMidiPlayerThread(qtractorMidiPlayer *pMidiPlayer); // Destructor. ~qtractorMidiPlayerThread(); // Thread run state accessors. void setRunState(bool bRunState); bool runState() const; protected: // The main thread executive. void run(); private: // Instance variables. qtractorMidiPlayer *m_pMidiPlayer; bool m_bRunState; }; //---------------------------------------------------------------------- // class qtractorMidiPlayer -- Simple MIDI player. // class qtractorMidiPlayer { public: // Constructor. qtractorMidiPlayer(qtractorMidiBus *pMidiBus); // Destructor. ~qtractorMidiPlayer(); // Open and start playing. bool open(const QString& sFilename, int iTrackChannel = -1); // Close and stop playing. void close(); // Process playing executive. bool process(unsigned long iFrameStart, unsigned long iFrameEnd); // Open status predicate. bool isOpen() const; // Sample-rate accessor. unsigned int sampleRate() const; // Queue time (ticks) accessor. unsigned long queueTime() const; // Queue time (frames) accessor. unsigned long queueFrame() const; protected: // Enqueue process event. void enqueue(unsigned short iMidiChannel, qtractorMidiEvent *pEvent, unsigned long iTime); private: // Instance variables. qtractorMidiBus *m_pMidiBus; qtractorMidiEngine *m_pMidiEngine; int m_iPlayerQueue; unsigned short m_iSeqs; qtractorMidiSequence **m_ppSeqs; qtractorMidiCursor **m_ppSeqCursors; qtractorTimeScale *m_pTimeScale; qtractorTimeScale::Cursor *m_pCursor; float m_fTempo; qtractorMidiPlayerThread *m_pPlayerThread; }; //---------------------------------------------------------------------- // class qtractorMidiInputRpn -- MIDI RPN/NRPN input parser. // // Constructor. qtractorMidiInputRpn::qtractorMidiInputRpn (void) : qtractorMidiRpn() { } // Encoder. bool qtractorMidiInputRpn::process ( const snd_seq_event_t *ev ) { if (ev->type != SND_SEQ_EVENT_CONTROLLER) { qtractorMidiRpn::flush(); return false; } qtractorMidiRpn::Event event; event.time = ev->time.tick; event.port = ev->dest.port; event.status = qtractorMidiRpn::CC | (ev->data.control.channel & 0x0f); event.param = ev->data.control.param; event.value = ev->data.control.value; return qtractorMidiRpn::process(event); } // Decoder. bool qtractorMidiInputRpn::dequeue ( snd_seq_event_t *ev ) { qtractorMidiRpn::Event event; if (!qtractorMidiRpn::dequeue(event)) return false; snd_seq_ev_clear(ev); snd_seq_ev_schedule_tick(ev, 0, 0, event.time); snd_seq_ev_set_dest(ev, 0, event.port); snd_seq_ev_set_fixed(ev); switch (qtractorMidiRpn::Type(event.status & 0x70)) { case qtractorMidiRpn::CC: // 0x10 ev->type = SND_SEQ_EVENT_CONTROLLER; break; case qtractorMidiRpn::RPN: // 0x20 ev->type = SND_SEQ_EVENT_REGPARAM; break; case qtractorMidiRpn::NRPN: // 0x30 ev->type = SND_SEQ_EVENT_NONREGPARAM; break; case qtractorMidiRpn::CC14: // 0x40 ev->type = SND_SEQ_EVENT_CONTROL14; break; default: return false; } ev->data.control.channel = event.status & 0x0f; ev->data.control.param = event.param; ev->data.control.value = event.value; return true; } //---------------------------------------------------------------------- // class qtractorMidiInputThread -- MIDI input thread (singleton). // // Constructor. qtractorMidiInputThread::qtractorMidiInputThread ( qtractorMidiEngine *pMidiEngine ) : QThread() { m_pMidiEngine = pMidiEngine; m_bRunState = false; } // Destructor. qtractorMidiInputThread::~qtractorMidiInputThread (void) { // Try to terminate executive thread, // but give it a bit of time to cleanup... if (isRunning()) do { setRunState(false); // terminate(); } while (!wait(100)); } // Thread run state accessors. void qtractorMidiInputThread::setRunState ( bool bRunState ) { m_bRunState = bRunState; } bool qtractorMidiInputThread::runState (void) const { return m_bRunState; } // The main thread executive. void qtractorMidiInputThread::run (void) { snd_seq_t *pAlsaSeq = m_pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiInputThread[%p]::run(%p): started...", this); #endif int nfds; struct pollfd *pfds; nfds = snd_seq_poll_descriptors_count(pAlsaSeq, POLLIN); pfds = (struct pollfd *) alloca(nfds * sizeof(struct pollfd)); snd_seq_poll_descriptors(pAlsaSeq, pfds, nfds, POLLIN); qtractorMidiInputRpn xrpn; m_bRunState = true; int iPoll = 0; while (m_bRunState && iPoll >= 0) { // Wait for events... iPoll = poll(pfds, nfds, 200); // Timeout? if (iPoll == 0) xrpn.flush(); while (iPoll > 0) { snd_seq_event_t *pEv = nullptr; snd_seq_event_input(pAlsaSeq, &pEv); // Process input event - ... // - enqueue to input track mapping; if (!xrpn.process(pEv)) m_pMidiEngine->capture(pEv); // snd_seq_free_event(pEv); iPoll = snd_seq_event_input_pending(pAlsaSeq, 0); } // Process pending events... while (xrpn.isPending()) { snd_seq_event_t ev; if (xrpn.dequeue(&ev)) m_pMidiEngine->capture(&ev); } } #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiInputThread[%p]::run(): stopped.", this); #endif } //---------------------------------------------------------------------- // class qtractorMidiOutputThread -- MIDI output thread (singleton). // // Constructor. qtractorMidiOutputThread::qtractorMidiOutputThread ( qtractorMidiEngine *pMidiEngine ) : QThread() { m_pMidiEngine = pMidiEngine; m_bRunState = false; } // Destructor. qtractorMidiOutputThread::~qtractorMidiOutputThread (void) { // Try to wake and terminate executive thread, // but give it a bit of time to cleanup... if (isRunning()) do { setRunState(false); // terminate(); sync(); } while (!wait(100)); } // Thread run state accessors. void qtractorMidiOutputThread::setRunState ( bool bRunState ) { QMutexLocker locker(&m_mutex); m_bRunState = bRunState; } bool qtractorMidiOutputThread::runState (void) const { return m_bRunState; } // The main thread executive. void qtractorMidiOutputThread::run (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiOutputThread[%p]::run(): started...", this); #endif m_bRunState = true; m_mutex.lock(); while (m_bRunState) { // Wait for sync... m_cond.wait(&m_mutex); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiOutputThread[%p]::run(): waked.", this); #endif // Only if playing, the output process cycle. if (m_pMidiEngine->isPlaying()) m_pMidiEngine->process(); } m_mutex.unlock(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiOutputThread[%p]::run(): stopped.", this); #endif } // MIDI output process cycle iteration (locked). void qtractorMidiOutputThread::processSync (void) { QMutexLocker locker(&m_mutex); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiOutputThread[%p]::processSync()", this); #endif m_pMidiEngine->process(); } // MIDI output flush/drain (locked). void qtractorMidiOutputThread::flushSync (void) { QMutexLocker locker(&m_mutex); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiOutputThread[%p]::flushSync()", this); #endif snd_seq_drain_output(m_pMidiEngine->alsaSeq()); } // MIDI queue reset time (locked). void qtractorMidiOutputThread::resetSync (void) { QMutexLocker locker(&m_mutex); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiOutputThread[%p]::resetSync()", this); #endif m_pMidiEngine->resetSync(); } // MIDI track output process resync. void qtractorMidiOutputThread::trackSync ( qtractorTrack *pTrack, unsigned long iFrameStart ) { QMutexLocker locker(&m_mutex); // Must have a valid session... qtractorSession *pSession = m_pMidiEngine->session(); if (pSession == nullptr) return; // Pick our actual MIDI sequencer cursor... qtractorSessionCursor *pMidiCursor = m_pMidiEngine->sessionCursor(); if (pMidiCursor == nullptr) return; // This is the last framestamp to be trown out... const unsigned long iFrameEnd = pMidiCursor->frame(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiOutputThread[%p]::trackSync(%p, %lu, %lu)", this, pTrack, iFrameStart, iFrameEnd); #endif // Split processing, in case we've been caught looping... if (pSession->isLooping() && iFrameStart > iFrameEnd && iFrameStart < pSession->loopEnd()) iFrameStart = pSession->loopStart(); // Locate the immediate nearest clip in track // and render them all thereafter, immediately... qtractorClip *pClip = pTrack->clips().first(); while (pClip && pClip->clipStart() < iFrameEnd) { if (iFrameStart < pClip->clipStart() + pClip->clipLength()) pClip->process(iFrameStart, iFrameEnd); pClip = pClip->next(); } // Surely must realize the output queue... snd_seq_drain_output(m_pMidiEngine->alsaSeq()); } // MIDI metronome output process resync. void qtractorMidiOutputThread::metroSync ( unsigned long iFrameStart ) { QMutexLocker locker(&m_mutex); // Pick our actual MIDI sequencer cursor... qtractorSessionCursor *pMidiCursor = m_pMidiEngine->sessionCursor(); if (pMidiCursor == nullptr) return; // This is the last framestamp to be trown out... const unsigned long iFrameEnd = pMidiCursor->frame(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiOutputThread[%p]::metroSync(%lu, %lu)", this, iFrameStart, iFrameEnd); #endif // (Re)process the metronome stuff... m_pMidiEngine->processMetro(iFrameStart, iFrameEnd); // Surely must realize the output queue... snd_seq_drain_output(m_pMidiEngine->alsaSeq()); } // Wake from executive wait condition. void qtractorMidiOutputThread::sync (void) { if (m_mutex.tryLock()) { m_cond.wakeAll(); m_mutex.unlock(); } #ifdef CONFIG_DEBUG_0 else qDebug("qtractorMidiOutputThread[%p]::sync(): tryLock() failed.", this); #endif } //---------------------------------------------------------------------- // class qtractorMidiPlayerThread -- MIDI player thread. // // Constructor. qtractorMidiPlayerThread::qtractorMidiPlayerThread ( qtractorMidiPlayer *pMidiPlayer ) : QThread(), m_pMidiPlayer(pMidiPlayer), m_bRunState(false) { } // Destructor. qtractorMidiPlayerThread::~qtractorMidiPlayerThread (void) { if (isRunning()) do { setRunState(false); // terminate(); } while (!wait(100)); } // Thread run state accessors. void qtractorMidiPlayerThread::setRunState ( bool bRunState ) { m_bRunState = bRunState; } bool qtractorMidiPlayerThread::runState (void) const { return m_bRunState; } // The main thread executive. void qtractorMidiPlayerThread::run (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiPlayerThread[%p]::run(): started...", this); #endif const unsigned int iSampleRate = m_pMidiPlayer->sampleRate(); const unsigned long iReadAhead = (iSampleRate >> 2); unsigned long iFrameStart = 0; unsigned long iFrameEnd = 0; m_bRunState = true; while (m_bRunState) { iFrameEnd += iReadAhead; if (m_pMidiPlayer->process(iFrameStart, iFrameEnd)) { const unsigned long iQueueFrame = m_pMidiPlayer->queueFrame(); const long iDelta = long(iFrameStart) - long(iQueueFrame); #ifdef CONFIG_DEBUG qDebug("qtractorMidiPlayer::process(%lu, %lu) iQueueFrame=%lu (%ld)", iFrameStart, iFrameEnd, iQueueFrame, iDelta); #endif unsigned long iSleep = iReadAhead; if (iSleep + iDelta > 0) iSleep += iDelta; msleep((1000 * iSleep) / iSampleRate); iFrameStart = iFrameEnd; } else m_bRunState = false; } #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiPlayerThread[%p]::run(): stopped.", this); #endif } //---------------------------------------------------------------------- // class qtractorMidiPlayer -- Simple MIDI player. // // Constructor. qtractorMidiPlayer::qtractorMidiPlayer ( qtractorMidiBus *pMidiBus ) : m_pMidiBus(pMidiBus), m_pMidiEngine(static_cast (pMidiBus->engine())), m_iPlayerQueue(-1), m_iSeqs(0), m_ppSeqs(nullptr), m_ppSeqCursors(nullptr), m_pTimeScale(nullptr), m_pCursor(nullptr), m_fTempo(0.0f), m_pPlayerThread(nullptr) { } // Destructor. qtractorMidiPlayer::~qtractorMidiPlayer (void) { close(); } // Open and start playing. bool qtractorMidiPlayer::open ( const QString& sFilename, int iTrackChannel ) { close(); if (m_pMidiBus == nullptr) return false; qtractorSession *pSession = m_pMidiEngine->session(); if (pSession == nullptr) return false; qtractorSessionCursor *pAudioCursor = pSession->audioEngine()->sessionCursor(); if (pAudioCursor == nullptr) return false; qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return false; snd_seq_t *pAlsaSeq = m_pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return false; m_iPlayerQueue = snd_seq_alloc_queue(pAlsaSeq); if (m_iPlayerQueue < 0) return false; qtractorMidiFile file; if (!file.open(sFilename)) { snd_seq_free_queue(pAlsaSeq, m_iPlayerQueue); m_iPlayerQueue = -1; return false; } m_pTimeScale = new qtractorTimeScale(*pTimeScale); m_pTimeScale->setTicksPerBeat(file.ticksPerBeat()); if (iTrackChannel < 0) { m_iSeqs = (file.format() == 1 ? file.tracks() : 16); iTrackChannel = 0; } else m_iSeqs = 1; m_ppSeqs = new qtractorMidiSequence * [m_iSeqs]; m_ppSeqCursors = new qtractorMidiCursor * [m_iSeqs]; for (unsigned short iSeq = 0; iSeq < m_iSeqs; ++iSeq) { m_ppSeqs[iSeq] = new qtractorMidiSequence( QString(), iSeq, m_pTimeScale->ticksPerBeat()); m_ppSeqCursors[iSeq] = new qtractorMidiCursor(); } if (file.readTracks(m_ppSeqs, m_iSeqs, iTrackChannel) && file.tempoMap()) file.tempoMap()->intoTimeScale(m_pTimeScale); file.close(); m_pCursor = new qtractorTimeScale::Cursor(m_pTimeScale); m_fTempo = m_pTimeScale->tempo(); snd_seq_queue_tempo_t *pQueueTempo; snd_seq_queue_tempo_alloca(&pQueueTempo); snd_seq_get_queue_tempo(pAlsaSeq, m_iPlayerQueue, pQueueTempo); snd_seq_queue_tempo_set_ppq(pQueueTempo, qtractorTimeScale::TICKS_PER_BEAT_HRQ); snd_seq_queue_tempo_set_tempo(pQueueTempo, (unsigned int) (60000000.0f / m_fTempo)); snd_seq_set_queue_tempo(pAlsaSeq, m_iPlayerQueue, pQueueTempo); snd_seq_start_queue(pAlsaSeq, m_iPlayerQueue, nullptr); snd_seq_drain_output(pAlsaSeq); pAudioCursor->reset(); qtractorMidiMonitor::resetTime(m_pTimeScale, 0); if (m_pMidiBus->midiMonitor_out()) m_pMidiBus->midiMonitor_out()->reset(); m_pPlayerThread = new qtractorMidiPlayerThread(this); m_pPlayerThread->start(QThread::HighPriority); return true; } // Close and stop playing. void qtractorMidiPlayer::close (void) { if (m_pPlayerThread) { if (m_pPlayerThread->isRunning()) do { m_pPlayerThread->setRunState(false); // m_pPayerThread->terminate(); } while (!m_pPlayerThread->wait(100)); delete m_pPlayerThread; m_pPlayerThread = nullptr; } if (m_ppSeqs && m_pMidiBus) { snd_seq_t *pAlsaSeq = m_pMidiEngine->alsaSeq(); if (pAlsaSeq) { m_pMidiBus->dequeueNoteOffs(queueTime()); snd_seq_drop_output(pAlsaSeq); if (m_iPlayerQueue >= 0) { snd_seq_stop_queue(pAlsaSeq, m_iPlayerQueue, nullptr); snd_seq_free_queue(pAlsaSeq, m_iPlayerQueue); m_iPlayerQueue = -1; } for (unsigned short iSeq = 0; iSeq < m_iSeqs; ++iSeq) { const unsigned short iChannel = m_ppSeqs[iSeq]->channel(); m_pMidiBus->setController(iChannel, ALL_SOUND_OFF); m_pMidiBus->setController(iChannel, ALL_NOTES_OFF); m_pMidiBus->setController(iChannel, ALL_CONTROLLERS_OFF); } snd_seq_drain_output(pAlsaSeq); } if (m_pMidiBus->pluginList_out() && (m_pMidiBus->pluginList_out())->midiManager()) (m_pMidiBus->pluginList_out())->midiManager()->reset(); } if (m_pCursor) { delete m_pCursor; m_pCursor = nullptr; } m_fTempo = 0.0f; for (unsigned short iSeq = 0; iSeq < m_iSeqs; ++iSeq) { if (m_ppSeqCursors && m_ppSeqCursors[iSeq]) delete m_ppSeqCursors[iSeq]; if (m_ppSeqs && m_ppSeqs[iSeq]) delete m_ppSeqs[iSeq]; } if (m_ppSeqCursors) { delete [] m_ppSeqCursors; m_ppSeqCursors = nullptr; } if (m_ppSeqs) { delete [] m_ppSeqs; m_ppSeqs = nullptr; } m_iSeqs = 0; if (m_pTimeScale) { delete m_pTimeScale; m_pTimeScale = nullptr; } } // Process playing executive. bool qtractorMidiPlayer::process ( unsigned long iFrameStart, unsigned long iFrameEnd ) { if (m_pMidiBus == nullptr) return false; if (m_pCursor == nullptr) return false; snd_seq_t *pAlsaSeq = m_pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return false; if (m_iPlayerQueue < 0) return false; qtractorTimeScale::Node *pNode = m_pCursor->seekFrame(iFrameEnd); if (pNode->tempo != m_fTempo) { const unsigned long iTime = (pNode->frame < iFrameStart ? pNode->tickFromFrame(iFrameStart) : pNode->tick); snd_seq_event_t ev; snd_seq_ev_clear(&ev); snd_seq_ev_schedule_tick(&ev, m_iPlayerQueue, 0, m_pTimeScale->timep(iTime)); ev.type = SND_SEQ_EVENT_TEMPO; ev.data.queue.queue = m_iPlayerQueue; ev.data.queue.param.value = (unsigned int) (60000000.0f / pNode->tempo); ev.dest.client = SND_SEQ_CLIENT_SYSTEM; ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER; snd_seq_event_output(pAlsaSeq, &ev); m_fTempo = pNode->tempo; qtractorMidiMonitor::splitTime( m_pCursor->timeScale(), pNode->frame, iTime); } const unsigned long iTimeStart = m_pTimeScale->tickFromFrame(iFrameStart); const unsigned long iTimeEnd = m_pTimeScale->tickFromFrame(iFrameEnd); unsigned int iProcess = 0; for (unsigned short iSeq = 0; iSeq < m_iSeqs; ++iSeq) { qtractorMidiSequence *pSeq = m_ppSeqs[iSeq]; qtractorMidiCursor *pSeqCursor = m_ppSeqCursors[iSeq]; qtractorMidiEvent *pEvent = pSeqCursor->seek(pSeq, iTimeStart); while (pEvent) { const unsigned long iTime = pEvent->time(); if (iTime >= iTimeEnd) break; if (iTime >= iTimeStart) enqueue(pSeq->channel(), pEvent, iTime); pEvent = pEvent->next(); } if (iTimeEnd < pSeq->duration()) ++iProcess; } snd_seq_drain_output(pAlsaSeq); return (iProcess > 0); } // Enqueue process event. void qtractorMidiPlayer::enqueue ( unsigned short iMidiChannel, qtractorMidiEvent *pEvent, unsigned long iTime ) { snd_seq_event_t ev; snd_seq_ev_clear(&ev); snd_seq_ev_set_source(&ev, m_pMidiBus->alsaPort()); snd_seq_ev_set_subs(&ev); snd_seq_ev_schedule_tick(&ev, m_iPlayerQueue, 0, m_pTimeScale->timep(iTime)); unsigned long iDuration = 0; switch (pEvent->type()) { case qtractorMidiEvent::NOTEON: ev.type = SND_SEQ_EVENT_NOTE; ev.data.note.channel = iMidiChannel; ev.data.note.note = pEvent->note(); ev.data.note.velocity = pEvent->value(); iDuration = pEvent->duration(); ev.data.note.duration = m_pTimeScale->timep(iDuration); break; case qtractorMidiEvent::KEYPRESS: ev.type = SND_SEQ_EVENT_KEYPRESS; ev.data.note.channel = iMidiChannel; ev.data.note.note = pEvent->note(); ev.data.note.velocity = pEvent->velocity(); ev.data.note.duration = 0; break; case qtractorMidiEvent::CONTROLLER: ev.type = SND_SEQ_EVENT_CONTROLLER; ev.data.control.channel = iMidiChannel; ev.data.control.param = pEvent->controller(); ev.data.control.value = pEvent->value(); break; case qtractorMidiEvent::REGPARAM: ev.type = SND_SEQ_EVENT_REGPARAM; ev.data.control.channel = iMidiChannel; ev.data.control.param = pEvent->param(); ev.data.control.value = pEvent->value(); break; case qtractorMidiEvent::NONREGPARAM: ev.type = SND_SEQ_EVENT_NONREGPARAM; ev.data.control.channel = iMidiChannel; ev.data.control.param = pEvent->param(); ev.data.control.value = pEvent->value(); break; case qtractorMidiEvent::CONTROL14: ev.type = SND_SEQ_EVENT_CONTROL14; ev.data.control.channel = iMidiChannel; ev.data.control.param = pEvent->param(); ev.data.control.value = pEvent->value(); break; case qtractorMidiEvent::PGMCHANGE: ev.type = SND_SEQ_EVENT_PGMCHANGE; ev.data.control.channel = iMidiChannel; ev.data.control.value = pEvent->param(); break; case qtractorMidiEvent::CHANPRESS: ev.type = SND_SEQ_EVENT_CHANPRESS; ev.data.control.channel = iMidiChannel; ev.data.control.value = pEvent->value(); break; case qtractorMidiEvent::PITCHBEND: ev.type = SND_SEQ_EVENT_PITCHBEND; ev.data.control.channel = iMidiChannel; ev.data.control.value = pEvent->pitchBend(); break; case qtractorMidiEvent::SYSEX: ev.type = SND_SEQ_EVENT_SYSEX; snd_seq_ev_set_sysex(&ev, pEvent->sysex_len(), pEvent->sysex()); break; default: break; } snd_seq_event_output(m_pMidiEngine->alsaSeq(), &ev); if (ev.type == SND_SEQ_EVENT_NOTE && iDuration > 0) m_pMidiBus->enqueueNoteOff(&ev, iTime, iTime + (iDuration - 1)); if (m_pMidiBus->midiMonitor_out()) m_pMidiBus->midiMonitor_out()->enqueue( pEvent->type(), pEvent->value(), iTime); if (m_pMidiBus->pluginList_out()) { qtractorMidiManager *pMidiManager = (m_pMidiBus->pluginList_out())->midiManager(); if (pMidiManager) { qtractorTimeScale::Cursor& cursor = m_pTimeScale->cursor(); qtractorTimeScale::Node *pNode = cursor.seekTick(iTime); const unsigned long t1 = pNode->frameFromTick(iTime); unsigned long t2 = t1; if (ev.type == SND_SEQ_EVENT_NOTE && iDuration > 0) { iTime += (iDuration - 1); pNode = cursor.seekTick(iTime); t2 += (pNode->frameFromTick(iTime) - t1); } pMidiManager->queued(&ev, t1, t2); } } } // Open status predicate. bool qtractorMidiPlayer::isOpen (void) const { return (m_pPlayerThread && m_pPlayerThread->isRunning()); } // Sample-rate accessor. unsigned int qtractorMidiPlayer::sampleRate (void) const { return (m_pTimeScale ? m_pTimeScale->sampleRate() : 44100); } // Queue time (ticks) accessor. unsigned long qtractorMidiPlayer::queueTime (void) const { snd_seq_t *pAlsaSeq = m_pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return 0; if (m_iPlayerQueue < 0) return 0; unsigned long iQueueTime = 0; snd_seq_queue_status_t *pQueueStatus; snd_seq_queue_status_alloca(&pQueueStatus); if (snd_seq_get_queue_status( pAlsaSeq, m_iPlayerQueue, pQueueStatus) >= 0) { iQueueTime = snd_seq_queue_status_get_tick_time(pQueueStatus); } return m_pTimeScale->timeq(iQueueTime); } // Queue time (frames) accessor. unsigned long qtractorMidiPlayer::queueFrame (void) const { return (m_pTimeScale ? m_pTimeScale->frameFromTick(queueTime()) : 0); } //---------------------------------------------------------------------- // class qtractorMidiEngine -- ALSA sequencer client instance (singleton). // // Constructor. qtractorMidiEngine::qtractorMidiEngine ( qtractorSession *pSession ) : qtractorEngine(pSession, qtractorTrack::Midi) { m_pAlsaSeq = nullptr; m_iAlsaClient = -1; m_iAlsaQueue = -1; m_iAlsaTimer = 0; m_pAlsaSubsSeq = nullptr; m_iAlsaSubsPort = -1; m_pAlsaNotifier = nullptr; m_iReadAhead = 0; m_pInputThread = nullptr; m_pOutputThread = nullptr; m_bDriftCorrect = true; m_iDriftCheck = 0; m_iDriftCount = DRIFT_CHECK; m_iTimeDrift = 0; m_iFrameDrift = 0; m_iTimeStart = 0; m_iFrameStart = 0; m_iTimeStartEx = 0; m_iAudioFrameStart = 0; m_bControlBus = false; m_pIControlBus = nullptr; m_pOControlBus = nullptr; // MIDI Metronome stuff. m_bMetronome = false; m_bMetroBus = false; m_pMetroBus = nullptr; m_iMetroChannel = 9; // GM Drums channel (10) m_iMetroBarNote = 76; // GM High-wood stick m_iMetroBarVelocity = 96; m_iMetroBarDuration = 48; m_iMetroBeatNote = 77; // GM Low-wood stick m_iMetroBeatVelocity = 64; m_iMetroBeatDuration = 24; m_iMetroOffset = 0; m_bMetroEnabled = false; // Time-scale cursor (tempo/time-signature map) m_pMetroCursor = nullptr; // Track down tempo changes. m_fMetroTempo = 0.0f; // MIDI Metronome count-in stuff. m_bCountIn = false; m_countInMode = CountInNone; m_iCountInBeats = 0; m_iCountIn = 0; m_iCountInFrame = 0; m_iCountInFrameStart = 0; m_iCountInFrameEnd = 0; m_iCountInTimeStart = 0; // SMF player stuff. m_bPlayerBus = false; m_pPlayerBus = nullptr; m_pPlayer = nullptr; // No input/capture quantization (default). m_iCaptureQuantize = 0; // MIDI controller mapping flagger. m_iResetAllControllersPending = 0; // MIDI MMC/SPP modes. m_mmcDevice = 0x7f; // All-caller-id. m_mmcMode = qtractorBus::Duplex; m_sppMode = qtractorBus::Duplex; // MIDI Clock mode. m_clockMode = qtractorBus::None; // Whether to reset all MIDI controllers (on playback start). m_bResetAllControllers = false; // MIDI Clock tempo tracking. m_iClockCount = 0; m_fClockTempo = 120.0f; } // Special event notifier proxy object. qtractorMidiEngineProxy *qtractorMidiEngine::proxy (void) { return &m_proxy; } // ALSA sequencer client descriptor accessor. snd_seq_t *qtractorMidiEngine::alsaSeq (void) const { return m_pAlsaSeq; } int qtractorMidiEngine::alsaClient (void) const { return m_iAlsaClient; } int qtractorMidiEngine::alsaQueue (void) const { return m_iAlsaQueue; } // Current ALSA queue time accessor. unsigned long qtractorMidiEngine::queueTime (void) const { qtractorSession *pSession = session(); if (pSession == nullptr) return 0; if (m_pAlsaSeq == nullptr) return 0; if (m_iAlsaQueue < 0) return 0; long iQueueTime = 0; snd_seq_queue_status_t *pQueueStatus; snd_seq_queue_status_alloca(&pQueueStatus); if (snd_seq_get_queue_status( m_pAlsaSeq, m_iAlsaQueue, pQueueStatus) >= 0) { iQueueTime = snd_seq_queue_status_get_tick_time(pQueueStatus); } iQueueTime = pSession->timeq(iQueueTime); const long iTimeStart = timeStart(); if (iQueueTime > -iTimeStart) iQueueTime += iTimeStart; return iQueueTime; } // ALSA subscription port notifier. QSocketNotifier *qtractorMidiEngine::alsaNotifier (void) const { return m_pAlsaNotifier; } // ALSA subscription notifier acknowledgment. void qtractorMidiEngine::alsaNotifyAck (void) { if (m_pAlsaSubsSeq == nullptr) return; do { snd_seq_event_t *pAlsaEvent; snd_seq_event_input(m_pAlsaSubsSeq, &pAlsaEvent); snd_seq_free_event(pAlsaEvent); } while (snd_seq_event_input_pending(m_pAlsaSubsSeq, 0) > 0); } // Special slave sync method. void qtractorMidiEngine::sync (void) { // Pure conditional thread slave synchronization... if (m_pOutputThread && midiCursorSync()) m_pOutputThread->sync(); } // Read ahead frames configuration. void qtractorMidiEngine::setReadAhead ( unsigned int iReadAhead ) { m_iReadAhead = iReadAhead; } unsigned int qtractorMidiEngine::readAhead (void) const { return m_iReadAhead; } // Audio/MIDI sync-check and cursor predicate. qtractorSessionCursor *qtractorMidiEngine::midiCursorSync ( bool bStart ) { // Must have a valid session... qtractorSession *pSession = session(); if (pSession == nullptr) return nullptr; // We'll need access to master audio engine... qtractorSessionCursor *pAudioCursor = pSession->audioEngine()->sessionCursor(); if (pAudioCursor == nullptr) return nullptr; // And to our slave MIDI engine too... qtractorSessionCursor *pMidiCursor = sessionCursor(); if (pMidiCursor == nullptr) return nullptr; // Can MIDI be ever behind audio? if (bStart) { pMidiCursor->seek(pAudioCursor->frame()); // pMidiCursor->setFrameTime(pAudioCursor->frameTime()); } else // No, it cannot be behind more than the read-ahead period... if (pMidiCursor->frameTime() > pAudioCursor->frameTime() + m_iReadAhead) return nullptr; // Nope. OK. return pMidiCursor; } // MIDI output process cycle iteration. void qtractorMidiEngine::process (void) { // Must have a valid session... qtractorSession *pSession = session(); if (pSession == nullptr) return; // Bail out if the audio-metronome is under count-in... if (pSession->audioEngine()->countIn() > 0) return; // Get a handle on our slave MIDI engine... qtractorSessionCursor *pMidiCursor = midiCursorSync(); // Isn't MIDI slightly behind audio? if (pMidiCursor == nullptr) return; // Metronome/count-in stuff... if (m_iCountIn > 0) { if (m_iCountInFrameStart < m_iCountInFrameEnd) { unsigned long iCountInFrameEnd = m_iCountInFrameStart + m_iReadAhead; if (iCountInFrameEnd > m_iCountInFrameEnd) iCountInFrameEnd = m_iCountInFrameEnd; processCountIn(m_iCountInFrameStart, iCountInFrameEnd); m_iCountInFrameStart = iCountInFrameEnd; } // Bail out... return; } // Now for the next read-ahead bunch... unsigned long iFrameStart = pMidiCursor->frame(); unsigned long iFrameEnd = iFrameStart + m_iReadAhead; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiEngine[%p]::process(%lu, %lu)", this, iFrameStart, iFrameEnd); #endif // Split processing, in case we're looping... const bool bLooping = pSession->isLooping(); const unsigned long le = pSession->loopEnd(); if (bLooping && iFrameStart < le) { // Loop-length might be shorter than the read-ahead... while (iFrameEnd >= le) { // Process metronome clicks... processMetro(iFrameStart, le); // Process the remaining until end-of-loop... pSession->process(pMidiCursor, iFrameStart, le); // Reset to start-of-loop... iFrameStart = pSession->loopStart(); iFrameEnd = iFrameStart + (iFrameEnd - le); pMidiCursor->seek(iFrameStart); // This is really a must... m_iFrameStart -= pSession->loopEnd(); m_iFrameStart += pSession->loopStart(); m_iTimeStart -= pSession->loopEndTime(); m_iTimeStart += pSession->loopStartTime(); // resetDrift(); -- Drift correction? } } // Process metronome clicks... processMetro(iFrameStart, iFrameEnd); // Regular range... pSession->process(pMidiCursor, iFrameStart, iFrameEnd); // Sync with loop boundaries (unlikely?)... if (bLooping && iFrameStart < le && iFrameEnd >= le) iFrameEnd = pSession->loopStart() + (iFrameEnd - le); // Sync to the next bunch, also critical for Audio-MIDI sync... pMidiCursor->seek(iFrameEnd); pMidiCursor->process(m_iReadAhead); // Flush the MIDI engine output queue... snd_seq_drain_output(m_pAlsaSeq); // Always do the queue drift stats // at the bottom of the pack... driftCheck(); } // Reset queue time. void qtractorMidiEngine::resetTime (void) { if (m_pOutputThread) m_pOutputThread->resetSync(); } void qtractorMidiEngine::resetSync (void) { qtractorSession *pSession = session(); if (pSession && m_pAlsaSeq) { snd_seq_stop_queue(m_pAlsaSeq, m_iAlsaQueue, nullptr); m_iAudioFrameStart = pSession->audioEngine()->jackFrameTime(); snd_seq_start_queue(m_pAlsaSeq, m_iAlsaQueue, nullptr); } } // Reset queue tempo. void qtractorMidiEngine::resetTempo (void) { // It must be surely activated... if (!isActivated()) return; // Needs a valid cursor... if (m_pMetroCursor == nullptr) return; // Reset tempo cursor. m_pMetroCursor->reset(); // There must a session reference... qtractorSession *pSession = session(); if (pSession == nullptr) return; // Recache tempo node... qtractorTimeScale::Node *pNode = m_pMetroCursor->seekFrame(pSession->playHead()); snd_seq_queue_tempo_t *pQueueTempo; snd_seq_queue_tempo_alloca(&pQueueTempo); // Fill tempo struct with current tempo info. snd_seq_get_queue_tempo(m_pAlsaSeq, m_iAlsaQueue, pQueueTempo); // Set the new intended ones... snd_seq_queue_tempo_set_ppq(pQueueTempo, qtractorTimeScale::TICKS_PER_BEAT_HRQ); snd_seq_queue_tempo_set_tempo(pQueueTempo, (unsigned int) (60000000.0f / pNode->tempo)); // Give tempo struct to the queue. snd_seq_set_queue_tempo(m_pAlsaSeq, m_iAlsaQueue, pQueueTempo); // Set queue tempo... if (m_bDriftCorrect && pSession->isPlaying()) { m_iFrameDrift = long(pNode->frameFromTick(queueTime())); m_iFrameDrift -= long(pSession->playHead()); } // Recache tempo value... m_fMetroTempo = pNode->tempo; // MIDI Clock tempo tracking. m_iClockCount = 0; m_fClockTempo = pNode->tempo; } // Reset all MIDI monitoring... void qtractorMidiEngine::resetAllMonitors (void) { // There must a session reference... qtractorSession *pSession = session(); if (pSession == nullptr) return; // Reset common MIDI monitor stuff... qtractorMidiMonitor::resetTime( pSession->timeScale(), pSession->playHead()); // Reset all MIDI bus monitors... for (qtractorBus *pBus = buses().first(); pBus; pBus = pBus->next()) { qtractorMidiBus *pMidiBus = static_cast (pBus); if (pMidiBus) { if (pMidiBus->midiMonitor_in()) pMidiBus->midiMonitor_in()->reset(); if (pMidiBus->midiMonitor_out()) pMidiBus->midiMonitor_out()->reset(); } } // Reset all MIDI track channel monitors... for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() == qtractorTrack::Midi) { qtractorMidiMonitor *pMidiMonitor = static_cast (pTrack->monitor()); if (pMidiMonitor) pMidiMonitor->reset(); } } // HACK: Reset step-input... m_proxy.notifyInpEvent(InpReset); } // Reset all MIDI instrument/controllers... void qtractorMidiEngine::resetAllControllers ( bool bForceImmediate ) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiEngine::resetAllControllers(%d)", int(bForceImmediate)); #endif // Deferred processing? if (!bForceImmediate) { ++m_iResetAllControllersPending; return; } // There must a session reference... qtractorSession *pSession = session(); if (pSession == nullptr) return; // Reset all MIDI bus controllers... for (qtractorBus *pBus = buses().first(); pBus; pBus = pBus->next()) { qtractorMidiBus *pMidiBus = static_cast (pBus); if (pMidiBus) { qtractorMidiMonitor *pOutputMonitor = pMidiBus->midiMonitor_out(); if (pOutputMonitor) { pMidiBus->sendSysexList(); // SysEx setup! pMidiBus->setMasterVolume(pOutputMonitor->gain()); pMidiBus->setMasterPanning(pOutputMonitor->panning()); } else { qtractorMidiMonitor *pInputMonitor = pMidiBus->midiMonitor_in(); if (pInputMonitor) { pMidiBus->setMasterVolume(pInputMonitor->gain()); pMidiBus->setMasterPanning(pInputMonitor->panning()); } } } } // Reset all MIDI tracks channel bank/program and controllers... for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() == qtractorTrack::Midi) { // MIDI track instrument patching (channel bank/program)... pTrack->setMidiPatch(pSession->instruments()); // MIDI track channel controllers... qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus) { pMidiBus->setVolume(pTrack, pTrack->gain()); pMidiBus->setPanning(pTrack, pTrack->panning()); } } } // Re-send all mapped feedback MIDI controllers... qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) pMidiControl->sendAllControllers(); // Done. m_iResetAllControllersPending = 0; } // Whether is actually pending a reset of // all the MIDI instrument/controllers... bool qtractorMidiEngine::isResetAllControllersPending (void) const { return (m_iResetAllControllersPending > 0); } // Shut-off all MIDI buses (stop)... void qtractorMidiEngine::shutOffAllBuses ( bool bClose ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiEngine::shutOffAllBuses(%d)", int(bClose)); #endif for (qtractorBus *pBus = qtractorEngine::buses().first(); pBus; pBus = pBus->next()) { qtractorMidiBus *pMidiBus = static_cast (pBus); if (pMidiBus) pMidiBus->shutOff(bClose); } } // Shut-off all MIDI tracks (panic)... void qtractorMidiEngine::shutOffAllTracks (void) { qtractorSession *pSession = session(); if (pSession == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMidiEngine::shutOffAllTracks()"); #endif QHash channels; const unsigned long iQueueTime = queueTime(); for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() == qtractorTrack::Midi) { qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus) { const unsigned short iChannel = pTrack->midiChannel(); const unsigned short iChannelMask = (1 << iChannel); const unsigned short iChannelFlags = channels.value(pMidiBus, 0); if ((iChannelFlags & iChannelMask) == 0) { pMidiBus->dequeueNoteOffs(iQueueTime); pMidiBus->setController(pTrack, ALL_SOUND_OFF); pMidiBus->setController(pTrack, ALL_NOTES_OFF); pMidiBus->setController(pTrack, ALL_CONTROLLERS_OFF); channels.insert(pMidiBus, iChannelFlags | iChannelMask); } } } } resetAllControllers(true); // Force immediate! } // ALSA port input registry methods. void qtractorMidiEngine::addInputBus ( qtractorMidiBus *pMidiBus ) { m_inputBuses.insert(pMidiBus->alsaPort(), pMidiBus); } void qtractorMidiEngine::removeInputBus ( qtractorMidiBus *pMidiBus ) { m_inputBuses.remove(pMidiBus->alsaPort()); } void qtractorMidiEngine::addInputBuffer ( int iAlsaPort, qtractorMidiInputBuffer *pMidiInputBuffer ) { m_inputBuffers.insert(iAlsaPort, pMidiInputBuffer); } void qtractorMidiEngine::removeInputBuffer ( int iAlsaPort ) { m_inputBuffers.remove(iAlsaPort); } // MIDI event capture method. void qtractorMidiEngine::capture ( snd_seq_event_t *pEv ) { qtractorSession *pSession = session(); if (pSession == nullptr) return; const int iAlsaPort = pEv->dest.port; qtractorMidiEvent::EventType type; unsigned char channel = 0; unsigned short param = 0; unsigned short value = 0; unsigned long duration = 0; unsigned char *pSysex = nullptr; unsigned short iSysex = 0; unsigned long tick = pSession->timeq(pEv->time.tick); // - capture quantization... if (m_iCaptureQuantize > 0) { const unsigned long q = pSession->ticksPerBeat() / m_iCaptureQuantize; tick = q * ((tick + (q >> 1)) / q); } #ifdef CONFIG_DEBUG_0 // - show event for debug purposes... fprintf(stderr, "MIDI In %d: %06lu 0x%02x", iAlsaPort, tick, pEv->type); if (pEv->type == SND_SEQ_EVENT_SYSEX) { fprintf(stderr, " sysex {"); unsigned char *data = (unsigned char *) pEv->data.ext.ptr; for (unsigned int i = 0; i < pEv->data.ext.len; ++i) fprintf(stderr, " %02x", data[i]); fprintf(stderr, " }\n"); } else { for (unsigned int i = 0; i < sizeof(pEv->data.raw8.d); ++i) fprintf(stderr, " %3d", pEv->data.raw8.d[i]); fprintf(stderr, "\n"); } #endif switch (pEv->type) { // case SND_SEQ_EVENT_NOTE: -- Unlikely real-time input. case SND_SEQ_EVENT_NOTEON: type = qtractorMidiEvent::NOTEON; channel = pEv->data.note.channel; param = pEv->data.note.note; value = pEv->data.note.velocity; // duration = pEv->data.note.duration; if (value == 0) { pEv->type = SND_SEQ_EVENT_NOTEOFF; type = qtractorMidiEvent::NOTEOFF; } break; case SND_SEQ_EVENT_NOTEOFF: type = qtractorMidiEvent::NOTEOFF; channel = pEv->data.note.channel; param = pEv->data.note.note; value = pEv->data.note.velocity; // duration = pEv->data.note.duration; break; case SND_SEQ_EVENT_KEYPRESS: type = qtractorMidiEvent::KEYPRESS; channel = pEv->data.note.channel; param = pEv->data.note.note; value = pEv->data.note.velocity; break; case SND_SEQ_EVENT_CONTROLLER: type = qtractorMidiEvent::CONTROLLER; channel = pEv->data.control.channel; param = pEv->data.control.param; value = pEv->data.control.value; break; case SND_SEQ_EVENT_REGPARAM: type = qtractorMidiEvent::REGPARAM; channel = pEv->data.control.channel; param = pEv->data.control.param; value = pEv->data.control.value; break; case SND_SEQ_EVENT_NONREGPARAM: type = qtractorMidiEvent::NONREGPARAM; channel = pEv->data.control.channel; param = pEv->data.control.param; value = pEv->data.control.value; break; case SND_SEQ_EVENT_CONTROL14: type = qtractorMidiEvent::CONTROL14; channel = pEv->data.control.channel; param = pEv->data.control.param; value = pEv->data.control.value; break; case SND_SEQ_EVENT_PGMCHANGE: type = qtractorMidiEvent::PGMCHANGE; channel = pEv->data.control.channel; param = pEv->data.control.value; value = 0x7f; break; case SND_SEQ_EVENT_CHANPRESS: type = qtractorMidiEvent::CHANPRESS; channel = pEv->data.control.channel; // param = 0; value = pEv->data.control.value; break; case SND_SEQ_EVENT_PITCHBEND: type = qtractorMidiEvent::PITCHBEND; channel = pEv->data.control.channel; // param = 0; value = (unsigned short) (0x2000 + pEv->data.control.value); break; case SND_SEQ_EVENT_START: case SND_SEQ_EVENT_STOP: case SND_SEQ_EVENT_CONTINUE: case SND_SEQ_EVENT_SONGPOS: // Trap SPP commands... if ((m_sppMode & qtractorBus::Input) && m_pIControlBus && m_pIControlBus->alsaPort() == iAlsaPort) { // Post the stuffed event... m_proxy.notifySppEvent(int(pEv->type), pEv->data.control.value); } // Not handled any longer. return; case SND_SEQ_EVENT_CLOCK: // Trap MIDI Clocks... if ((m_clockMode & qtractorBus::Input) && m_pIControlBus && m_pIControlBus->alsaPort() == iAlsaPort) { static QElapsedTimer s_clockTimer; if (++m_iClockCount == 1) s_clockTimer.start(); else if (m_iClockCount > 72) { // 3 beat averaging... m_iClockCount = 0; const float fClockTempo = ::rintf(180000.0f / float(s_clockTimer.elapsed())); if (qAbs(fClockTempo - m_fClockTempo) / m_fClockTempo > 0.01f) { m_fClockTempo = fClockTempo; // Post the stuffed event... m_proxy.notifyClkEvent(m_fClockTempo); } } } // Not handled any longer. return; case SND_SEQ_EVENT_SYSEX: type = qtractorMidiEvent::SYSEX; pSysex = (unsigned char *) pEv->data.ext.ptr; iSysex = (unsigned short) pEv->data.ext.len; // Trap MMC commands... if ((m_mmcMode & qtractorBus::Input) && pSysex[1] == 0x7f && pSysex[3] == 0x06 // MMC command mode. && m_pIControlBus && m_pIControlBus->alsaPort() == iAlsaPort) { // Post the stuffed event... m_proxy.notifyMmcEvent(qtractorMmcEvent(pSysex)); // Bail out, right now! return; } break; default: // Not handled here... return; } unsigned long iTime = m_iTimeStartEx + tick; // Wrap in loop-range, if any... if (pSession->isLooping()) { const unsigned long iLoopEndTime = pSession->loopEndTime(); if (iTime > iLoopEndTime) { const unsigned long iLoopStartTime = pSession->loopStartTime(); iTime = iLoopStartTime + (iTime - iLoopEndTime) % (iLoopEndTime - iLoopStartTime); } } // Take care of recording, if any... const bool bPlaying = isPlaying(); bool bRecording = (pSession->isRecording() && bPlaying); if (bRecording) { // Take care of punch-in/out-range... bRecording = (!pSession->isPunching() || (iTime >= pSession->punchInTime() && iTime < pSession->punchOutTime())); } #if 0//-- Unlikely real-time input. qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekTick(iTime); const unsigned long t0 = pNode->frameFromTick(iTime); const unsigned long f0 = m_iFrameStartEx; const unsigned long t1 = (t0 < f0 ? t0 : t0 - f0); unsigned long t2 = t1; if (type == qtractorMidiEvent::NOTEON && duration > 0) { const unsigned long iTimeOff = iTime + (duration - 1); pNode = cursor.seekTick(iTimeOff); t2 += (pNode->frameFromTick(iTimeOff) - t0); } #endif qtractorMidiManager *pMidiManager; // Whether to notify any step input/overdub... unsigned short iInpEvents = 0; // Now check which bus and track we're into... for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { // Must be a MIDI track... if (pTrack->trackType() != qtractorTrack::Midi) continue; // Must be capture/passthru mode // and for the intended channel... const bool bRecord = pTrack->isRecord(); const bool bMonitor = pSession->isTrackMonitor(pTrack); if ((bRecord || bMonitor) // && !pTrack->isMute() && (!pSession->soloTracks() || pTrack->isSolo()) && pSession->isTrackMidiChannel(pTrack, channel)) { qtractorMidiBus *pMidiBus = static_cast (pTrack->inputBus()); if (pMidiBus && pMidiBus->alsaPort() == iAlsaPort) { // Is it actually recording?... if (bRecord) { qtractorMidiSequence *pSeq = nullptr; qtractorMidiClip *pMidiClip = static_cast (pTrack->clipRecord()); if (pMidiClip) pSeq = pMidiClip->sequence(); if (pMidiClip && pSeq && pTrack->isClipRecordEx()) { // Account for step-input recording... if (!bPlaying) { // Check step-input auto-advance... if (type != qtractorMidiEvent::NOTEOFF) { pMidiClip->setStepInputLast( pSession->audioEngine()->jackFrameTime()); } // Set quantized step-input event time... iTime = pMidiClip->stepInputHeadTime(); if (type == qtractorMidiEvent::NOTEON) duration = pMidiClip->stepInputTailTime() - iTime; else if (type == qtractorMidiEvent::NOTEOFF) pSeq = nullptr; // ignore all note-offs... } // Make sure it falls inside the recording clip... const unsigned long iClipStartTime = pMidiClip->clipStartTime(); const unsigned long iClipEndTime = iClipStartTime + pMidiClip->clipLengthTime(); if (iTime >= iClipStartTime && (!bPlaying || iTime < iClipEndTime)) tick = iTime - iClipStartTime + pMidiClip->clipOffsetTime(); else if (type != qtractorMidiEvent::NOTEOFF) pSeq = nullptr; } else if (!bPlaying) pSeq = nullptr; // Yep, maybe we have a new MIDI event on record... if (pSeq) { qtractorMidiEvent *pEvent = new qtractorMidiEvent( tick, type, param, value, duration); if (pSysex) pEvent->setSysex(pSysex, iSysex); if (pTrack->isClipRecordEx()) { m_inpMutex.lock(); m_inpEvents.insert(pMidiClip, pEvent); m_inpMutex.unlock(); ++iInpEvents; } else if (bPlaying) pSeq->addEvent(pEvent); } } // Track input monitoring... qtractorMidiMonitor *pMidiMonitor = static_cast (pTrack->monitor()); if (pMidiMonitor) pMidiMonitor->enqueue(type, value); // Output monitoring on record... if (bMonitor) { pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus && pMidiBus->midiMonitor_out()) { // FIXME: MIDI-thru channel filtering prolog... const unsigned short iOldChannel = pEv->data.note.channel; pEv->data.note.channel = pTrack->midiChannel(); // MIDI-thru: same event redirected... snd_seq_ev_set_source(pEv, pMidiBus->alsaPort()); snd_seq_ev_set_subs(pEv); snd_seq_ev_set_direct(pEv); snd_seq_event_output_direct(m_pAlsaSeq, pEv); // Done with MIDI-thru. pMidiBus->midiMonitor_out()->enqueue(type, value); // Do it for the MIDI plugins too... pMidiManager = (pTrack->pluginList())->midiManager(); if (pMidiManager) pMidiManager->direct(pEv); //queued(pEv,t1[,t2]); if (!pMidiBus->isMonitor() && pMidiBus->pluginList_out()) { pMidiManager = (pMidiBus->pluginList_out())->midiManager(); if (pMidiManager) pMidiManager->direct(pEv); //queued(pEv,t1[,t2]); } // FIXME: MIDI-thru channel filtering epilog... pEv->data.note.channel = iOldChannel; } } } } } // MIDI Bus monitoring... qtractorMidiBus *pMidiBus = m_inputBuses.value(iAlsaPort, nullptr); if (pMidiBus) { // Input monitoring... if (pMidiBus->midiMonitor_in()) pMidiBus->midiMonitor_in()->enqueue(type, value); // Do it for the MIDI input plugins too... if (pMidiBus->pluginList_in()) { pMidiManager = (pMidiBus->pluginList_in())->midiManager(); if (pMidiManager) pMidiManager->direct(pEv); //queued(pEv,t1[,t2]); } // Output monitoring on passthru... if (pMidiBus->isMonitor()) { // Do it for the MIDI output plugins too... if (pMidiBus->pluginList_out()) { pMidiManager = (pMidiBus->pluginList_out())->midiManager(); if (pMidiManager) pMidiManager->direct(pEv); //queued(pEv,t1[,t2]); } if (pMidiBus->midiMonitor_out()) { // MIDI-thru: same event redirected... snd_seq_ev_set_source(pEv, pMidiBus->alsaPort()); snd_seq_ev_set_subs(pEv); snd_seq_ev_set_direct(pEv); snd_seq_event_output_direct(m_pAlsaSeq, pEv); // Done with MIDI-thru. pMidiBus->midiMonitor_out()->enqueue(type, value); } } } else { // Input buffers (eg. insert returns)... qtractorMidiInputBuffer *pMidiInputBuffer = m_inputBuffers.value(iAlsaPort, nullptr); if (pMidiInputBuffer) pMidiInputBuffer->enqueue(pEv); } // Trap controller commands... if (type == qtractorMidiEvent::SYSEX) return; if (m_pIControlBus && m_pIControlBus->alsaPort() == iAlsaPort) { // Post the stuffed event... m_proxy.notifyCtlEvent( qtractorCtlEvent(type, channel, param, value)); } // Notify step-input events... if (iInpEvents > 0) { // Post the stuffed event(s)... m_proxy.notifyInpEvent(InpEvent); } } // MIDI event enqueue method. void qtractorMidiEngine::enqueue ( qtractorTrack *pTrack, qtractorMidiEvent *pEvent, unsigned long iTime, float fGain ) { qtractorSession *pSession = session(); if (pSession == nullptr) return; // Target MIDI bus... qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus == nullptr) return; #if 0 // HACK: Ignore our own mixer-monitor supplied controllers... if (pEvent->type() == qtractorMidiEvent::CONTROLLER) { if (pEvent->controller() == CHANNEL_VOLUME || pEvent->controller() == CHANNEL_PANNING) return; } #endif const int iAlsaPort = pMidiBus->alsaPort(); // Scheduled delivery: take into account // the time playback/queue started... const unsigned long tick = (long(iTime) > m_iTimeStart ? iTime - m_iTimeStart : 0); #ifdef CONFIG_DEBUG_0 // - show event for debug purposes... fprintf(stderr, "MIDI Out %d: %06lu 0x%02x", iAlsaPort, tick, int(pEvent->type() | pTrack->midiChannel())); if (pEvent->type() == qtractorMidiEvent::SYSEX) { fprintf(stderr, " sysex {"); unsigned char *data = (unsigned char *) pEvent->sysex(); for (unsigned int i = 0; i < pEvent->sysex_len(); ++i) fprintf(stderr, " %02x", data[i]); fprintf(stderr, " }\n"); } else { fprintf(stderr, " %3d %3d (duration=%lu)\n", pEvent->note(), pEvent->velocity(), pEvent->duration()); } #endif // Initialize outbound event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Set Event tag... ev.tag = (unsigned char) (pTrack->midiTag() & 0xff); // Addressing... snd_seq_ev_set_source(&ev, iAlsaPort); snd_seq_ev_set_subs(&ev); // Scheduled delivery... snd_seq_ev_schedule_tick(&ev, m_iAlsaQueue, 0, pSession->timep(tick)); unsigned long iDuration = 0; // Set proper event data... switch (pEvent->type()) { case qtractorMidiEvent::NOTEON: ev.type = SND_SEQ_EVENT_NOTE; ev.data.note.channel = pTrack->midiChannel(); ev.data.note.note = pEvent->note(); ev.data.note.velocity = int(fGain * float(pEvent->value())) & 0x7f; iDuration = pEvent->duration(); if (pSession->isLooping()) { const unsigned long iLoopEndTime = pSession->tickFromFrame(pSession->loopEnd()); if (iLoopEndTime > iTime && iLoopEndTime < iTime + pEvent->duration()) iDuration = iLoopEndTime - iTime; } ev.data.note.duration = pSession->timep(iDuration); break; case qtractorMidiEvent::KEYPRESS: ev.type = SND_SEQ_EVENT_KEYPRESS; ev.data.note.channel = pTrack->midiChannel(); ev.data.note.note = pEvent->note(); ev.data.note.velocity = pEvent->velocity(); ev.data.note.duration = 0; break; case qtractorMidiEvent::CONTROLLER: ev.type = SND_SEQ_EVENT_CONTROLLER; ev.data.control.channel = pTrack->midiChannel(); ev.data.control.param = pEvent->controller(); // Track properties override... switch (pEvent->controller()) { case BANK_SELECT_MSB: if (pTrack->midiBank() >= 0) ev.data.control.value = (pTrack->midiBank() & 0x3f80) >> 7; else ev.data.control.value = pEvent->value(); break; case BANK_SELECT_LSB: if (pTrack->midiBank() >= 0) ev.data.control.value = (pTrack->midiBank() & 0x7f); else ev.data.control.value = pEvent->value(); break; case CHANNEL_VOLUME: ev.data.control.value = int(pTrack->gain() * float(pEvent->value())) & 0x7f; break; default: ev.data.control.value = pEvent->value(); break; } break; case qtractorMidiEvent::REGPARAM: ev.type = SND_SEQ_EVENT_REGPARAM; ev.data.control.channel = pTrack->midiChannel(); ev.data.control.param = pEvent->param(); ev.data.control.value = pEvent->value(); break; case qtractorMidiEvent::NONREGPARAM: ev.type = SND_SEQ_EVENT_NONREGPARAM; ev.data.control.channel = pTrack->midiChannel(); ev.data.control.param = pEvent->param(); ev.data.control.value = pEvent->value(); break; case qtractorMidiEvent::CONTROL14: ev.type = SND_SEQ_EVENT_CONTROL14; ev.data.control.channel = pTrack->midiChannel(); ev.data.control.param = pEvent->param(); ev.data.control.value = pEvent->value(); break; case qtractorMidiEvent::PGMCHANGE: ev.type = SND_SEQ_EVENT_PGMCHANGE; ev.data.control.channel = pTrack->midiChannel(); // HACK: Track properties override... if (pTrack->midiProg() >= 0) ev.data.control.value = pTrack->midiProg(); else ev.data.control.value = pEvent->param(); break; case qtractorMidiEvent::CHANPRESS: ev.type = SND_SEQ_EVENT_CHANPRESS; ev.data.control.channel = pTrack->midiChannel(); ev.data.control.value = pEvent->value(); break; case qtractorMidiEvent::PITCHBEND: ev.type = SND_SEQ_EVENT_PITCHBEND; ev.data.control.channel = pTrack->midiChannel(); ev.data.control.value = pEvent->pitchBend(); break; case qtractorMidiEvent::SYSEX: { ev.type = SND_SEQ_EVENT_SYSEX; unsigned char *data = pEvent->sysex(); unsigned short data_len = pEvent->sysex_len(); if (pMidiBus->midiMonitor_out()) { // HACK: Master volume: make a copy // and update it while queued... if (data[1] == 0x7f && data[2] == 0x7f && data[3] == 0x04 && data[4] == 0x01) { static unsigned char s_data[8]; if (data_len > sizeof(s_data)) data_len = sizeof(s_data); ::memcpy(s_data, data, data_len); const float fGain = pMidiBus->midiMonitor_out()->gain(); data = &s_data[0]; data[5] = 0; data[6] = int(fGain * float(data[6])) & 0x7f; } } snd_seq_ev_set_sysex(&ev, data_len, data); break; } default: break; } // Pump it into the queue. snd_seq_event_output(m_pAlsaSeq, &ev); // MIDI track monitoring... qtractorMidiMonitor *pMidiMonitor = static_cast (pTrack->monitor()); if (pMidiMonitor) pMidiMonitor->enqueue(pEvent->type(), pEvent->value(), tick); // MIDI bus monitoring... if (pMidiBus->midiMonitor_out()) pMidiBus->midiMonitor_out()->enqueue( pEvent->type(), pEvent->value(), tick); // Do it for the MIDI track plugins too... qtractorTimeScale::Cursor& cursor = pSession->timeScale()->cursor(); qtractorTimeScale::Node *pNode = cursor.seekTick(iTime); const long f0 = m_iFrameStart + (pTrack->pluginList())->latency(); const unsigned long t0 = pNode->frameFromTick(iTime); const unsigned long t1 = (long(t0) < f0 ? t0 : t0 - f0); unsigned long t2 = t1; if (ev.type == SND_SEQ_EVENT_NOTE && iDuration > 0) { const unsigned long iTimeOff = iTime + (iDuration - 1); pMidiBus->enqueueNoteOff(&ev, iTime, iTimeOff); pNode = cursor.seekTick(iTimeOff); t2 += (pNode->frameFromTick(iTimeOff) - t0); } qtractorMidiManager *pMidiManager = (pTrack->pluginList())->midiManager(); if (pMidiManager) pMidiManager->queued(&ev, t1, t2); // And for the MIDI output plugins as well... if (pMidiBus->pluginList_out()) { pMidiManager = (pMidiBus->pluginList_out())->midiManager(); if (pMidiManager) pMidiManager->queued(&ev, t1, t2); } } // Reset ouput queue drift stats (audio vs. MIDI)... void qtractorMidiEngine::resetDrift (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiEngine::resetDrift()"); #endif //--DRIFT-SKEW-BEGIN-- snd_seq_queue_tempo_t *pQueueTempo; snd_seq_queue_tempo_alloca(&pQueueTempo); snd_seq_get_queue_tempo(m_pAlsaSeq, m_iAlsaQueue, pQueueTempo); snd_seq_queue_tempo_set_skew(pQueueTempo, snd_seq_queue_tempo_get_skew_base(pQueueTempo)); snd_seq_set_queue_tempo(m_pAlsaSeq, m_iAlsaQueue, pQueueTempo); //--DRIFT-SKEW-END-- m_iDriftCheck = 0; m_iDriftCount = DRIFT_CHECK; m_iTimeDrift = 0; m_iFrameDrift = 0; } // Do ouput queue status (audio vs. MIDI)... void qtractorMidiEngine::driftCheck (void) { if (!m_bDriftCorrect) return; if (++m_iDriftCheck < m_iDriftCount) return; qtractorSession *pSession = session(); if (pSession == nullptr) return; // if (pSession->isRecording()) // return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; if (m_pMetroCursor == nullptr) return; // Time to have some corrective approach...? const unsigned short iTicksPerBeat = pSession->ticksPerBeat(); const long iAudioFrame = m_iFrameStart + m_iFrameDrift + pAudioEngine->jackFrameTime() - m_iAudioFrameStart; qtractorTimeScale::Node *pNode = m_pMetroCursor->seekFrame(iAudioFrame); const long iAudioTime = long(pNode->tickFromFrame(iAudioFrame)) - m_iTimeStart; const long iMidiTime = long(queueTime()); const long iMinDeltaTime = long(iTicksPerBeat >> 8) + 1; const long iMaxDeltaTime = long(iTicksPerBeat >> 4) + 1; const long iDeltaTime = (iAudioTime - iMidiTime); if (qAbs(iDeltaTime) < iMaxDeltaTime) { //--DRIFT-SKEW-BEGIN-- const long iTimeDrift = m_iTimeDrift + (iDeltaTime << 1); snd_seq_queue_tempo_t *pQueueTempo; snd_seq_queue_tempo_alloca(&pQueueTempo); snd_seq_get_queue_tempo(m_pAlsaSeq, m_iAlsaQueue, pQueueTempo); const unsigned int iSkewBase = snd_seq_queue_tempo_get_skew_base(pQueueTempo); const unsigned int iSkewPrev = snd_seq_queue_tempo_get_skew(pQueueTempo); const unsigned int iSkewNext = (unsigned int) (float(iSkewBase) * float(iAudioTime + iTimeDrift) / float(iAudioTime)); if (iSkewNext != iSkewPrev) { snd_seq_queue_tempo_set_skew(pQueueTempo, iSkewNext); snd_seq_set_queue_tempo(m_pAlsaSeq, m_iAlsaQueue, pQueueTempo); } #ifdef CONFIG_DEBUG//_0 qDebug("qtractorMidiEngine::driftCheck(%u): " "iAudioTime=%ld iMidiTime=%ld (%ld) iTimeDrift=%ld (%.2g%%)", m_iDriftCount, iAudioTime, iMidiTime, iDeltaTime, iTimeDrift, ((100.0f * float(iSkewNext)) / float(iSkewBase)) - 100.0f); #endif // Adaptive drift check... plan A. const bool bDecreased = (qAbs(m_iTimeDrift) > qAbs(iTimeDrift)); const bool bOvershoot = ((m_iTimeDrift * iTimeDrift) < 0); m_iTimeDrift = iTimeDrift; if ((!bDecreased || bOvershoot) && (qAbs(iDeltaTime) > iMinDeltaTime) && (m_iDriftCheck > DRIFT_CHECK_MIN)) { m_iDriftCount >>= 1; // m_iTimeDrift <<= 1; } else // Adaptive drift check... plan B. if ((bDecreased || !bOvershoot) // && (qAbs(iDeltaTime) < iMinDeltaTime) && (m_iDriftCheck < DRIFT_CHECK_MAX)) { m_iDriftCount <<= 1; m_iTimeDrift >>= 1; } //--DRIFT-SKEW-END-- } // Restart counting... m_iDriftCheck = 0; } // Flush ouput queue (if necessary)... void qtractorMidiEngine::flush (void) { // Really flush MIDI output... if (m_pOutputThread) m_pOutputThread->flushSync(); } // Device engine initialization method. bool qtractorMidiEngine::init (void) { // There must a session reference... qtractorSession *pSession = session(); if (pSession == nullptr) return false; // Try open a new client... if (snd_seq_open(&m_pAlsaSeq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK) < 0) return false; if (m_pAlsaSeq == nullptr) return false; // Fix client name. const QByteArray aClientName = pSession->clientName().toUtf8(); snd_seq_set_client_name(m_pAlsaSeq, aClientName.constData()); m_iAlsaClient = snd_seq_client_id(m_pAlsaSeq); m_iAlsaQueue = snd_seq_alloc_queue(m_pAlsaSeq); // Set sequencer queue timer. if (qtractorMidiTimer().indexOf(m_iAlsaTimer) > 0) { qtractorMidiTimer::Key key(m_iAlsaTimer); snd_timer_id_t *pAlsaTimerId; snd_timer_id_alloca(&pAlsaTimerId); snd_timer_id_set_class(pAlsaTimerId, key.alsaTimerClass()); snd_timer_id_set_card(pAlsaTimerId, key.alsaTimerCard()); snd_timer_id_set_device(pAlsaTimerId, key.alsaTimerDevice()); snd_timer_id_set_subdevice(pAlsaTimerId, key.alsaTimerSubDev()); snd_seq_queue_timer_t *pAlsaTimer; snd_seq_queue_timer_alloca(&pAlsaTimer); snd_seq_queue_timer_set_type(pAlsaTimer, SND_SEQ_TIMER_ALSA); snd_seq_queue_timer_set_id(pAlsaTimer, pAlsaTimerId); snd_seq_set_queue_timer(m_pAlsaSeq, m_iAlsaQueue, pAlsaTimer); } // Setup subscriptions stuff... if (snd_seq_open(&m_pAlsaSubsSeq, "hw", SND_SEQ_OPEN_DUPLEX, 0) >= 0) { m_iAlsaSubsPort = snd_seq_create_simple_port( m_pAlsaSubsSeq, clientName().toUtf8().constData(), SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT, SND_SEQ_PORT_TYPE_APPLICATION); if (m_iAlsaSubsPort >= 0) { struct pollfd pfd[1]; snd_seq_addr_t seq_addr; snd_seq_port_subscribe_t *pAlsaSubs; snd_seq_port_subscribe_alloca(&pAlsaSubs); seq_addr.client = SND_SEQ_CLIENT_SYSTEM; seq_addr.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE; snd_seq_port_subscribe_set_sender(pAlsaSubs, &seq_addr); seq_addr.client = snd_seq_client_id(m_pAlsaSubsSeq); seq_addr.port = m_iAlsaSubsPort; snd_seq_port_subscribe_set_dest(pAlsaSubs, &seq_addr); snd_seq_subscribe_port(m_pAlsaSubsSeq, pAlsaSubs); snd_seq_poll_descriptors(m_pAlsaSubsSeq, pfd, 1, POLLIN); m_pAlsaNotifier = new QSocketNotifier( pfd[0].fd, QSocketNotifier::Read); } } // Time-scale cursor (tempo/time-signature map) m_pMetroCursor = new qtractorTimeScale::Cursor(pSession->timeScale()); return true; } // Device engine activation method. bool qtractorMidiEngine::activate (void) { // There must a session reference... qtractorSession *pSession = session(); if (pSession == nullptr) return false; // Open SMF player to last... openPlayerBus(); // Open control/metronome buses, at least try... openControlBus(); openMetroBus(); // Set the read-ahead in frames (0.5s)... m_iReadAhead = (pSession->sampleRate() >> 1); // Create and start our own MIDI input queue thread... m_pInputThread = new qtractorMidiInputThread(this); m_pInputThread->start(QThread::TimeCriticalPriority); // Create and start our own MIDI output queue thread... m_pOutputThread = new qtractorMidiOutputThread(this); m_pOutputThread->start(QThread::HighPriority); // Reset/zero tickers... m_iTimeStart = 0; m_iFrameStart = 0; m_iTimeStartEx = m_iTimeStart; m_iAudioFrameStart = pSession->audioEngine()->jackFrameTime(); // Reset output queue drift compensator... resetDrift(); // Reset all dependable monitoring... resetAllMonitors(); return true; } // Device engine start method. bool qtractorMidiEngine::start (void) { // It must be surely activated... if (!isActivated()) return false; // There must a session reference... qtractorSession *pSession = session(); if (pSession == nullptr) return false; // Output thread must be around too... if (m_pOutputThread == nullptr) return false; // Close any SMF player out there... closePlayer(); // Initial output thread bumping... qtractorSessionCursor *pMidiCursor = midiCursorSync(true); if (pMidiCursor == nullptr) return false; // Reset all dependables... resetTempo(); resetAllMonitors(); // Reset output queue drift compensator... resetDrift(); // Start queue timer... m_iFrameStart = long(pMidiCursor->frame()); m_iTimeStart = long(pSession->tickFromFrame(m_iFrameStart)); m_iTimeStartEx = m_iTimeStart; m_iAudioFrameStart = pSession->audioEngine()->jackFrameTime(); // Start count-in stuff... unsigned short iCountInBeats = 0; if (m_bCountIn && ( (m_countInMode == CountInPlayback) || (m_countInMode == CountInRecording && pSession->isRecording()))) iCountInBeats = m_iCountInBeats; if (iCountInBeats > 0) { m_iCountIn = iCountInBeats; m_iCountInFrame = m_iFrameStart; qtractorTimeScale::Cursor& cursor = pSession->timeScale()->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iCountInFrame); const unsigned short iCountInBeat = pNode->beatFromFrame(m_iCountInFrame); m_iCountInFrameStart = m_iCountInFrame; m_iCountInFrameEnd = m_iCountInFrameStart + pNode->frameFromBeat(iCountInBeat + iCountInBeats) - pNode->frameFromBeat(iCountInBeat); m_iCountInTimeStart = m_iTimeStart; ++m_iCountIn; // Give some slack to the end... } else { m_iCountIn = 0; m_iCountInFrame = 0; m_iCountInFrameStart = 0; m_iCountInFrameEnd = 0; m_iCountInTimeStart = 0; } // Effectively start sequencer queue timer... snd_seq_start_queue(m_pAlsaSeq, m_iAlsaQueue, nullptr); snd_seq_drain_output(m_pAlsaSeq); // Carry on... m_pOutputThread->processSync(); return true; } // Device engine stop method. void qtractorMidiEngine::stop (void) { if (!isActivated()) return; // Cleanup queues... snd_seq_drop_input(m_pAlsaSeq); snd_seq_drop_output(m_pAlsaSeq); // Stop queue timer... snd_seq_stop_queue(m_pAlsaSeq, m_iAlsaQueue, nullptr); flush(); // Shut-off all MIDI buses... shutOffAllBuses(); // Reset all monitors... resetAllMonitors(); } // Device engine deactivation method. void qtractorMidiEngine::deactivate (void) { // We're stopping now... setPlaying(false); // Stop our queue threads... m_pInputThread->setRunState(false); m_pOutputThread->setRunState(false); m_pOutputThread->sync(); } // Device engine cleanup method. void qtractorMidiEngine::clean (void) { // Clean any (pending?) step-input/overdub events... m_inpEvents.clear(); // Clean control/metronome buses... deleteControlBus(); deleteMetroBus(); // Close SMF player last... deletePlayerBus(); // Delete output thread... if (m_pOutputThread) { // Make it nicely... if (m_pOutputThread->isRunning()) do { m_pOutputThread->setRunState(false); // m_pOutputThread->terminate(); m_pOutputThread->sync(); } while (!m_pOutputThread->wait(100)); delete m_pOutputThread; m_pOutputThread = nullptr; } // Last but not least, delete input thread... if (m_pInputThread) { // Make it nicely... if (m_pInputThread->isRunning()) do { m_pInputThread->setRunState(false); // m_pInputThread->terminate(); } while (!m_pInputThread->wait(100)); delete m_pInputThread; m_pInputThread = nullptr; } // Time-scale cursor (tempo/time-signature map) if (m_pMetroCursor) { delete m_pMetroCursor; m_pMetroCursor = nullptr; } // Drop subscription stuff. if (m_pAlsaSubsSeq) { if (m_pAlsaNotifier) { delete m_pAlsaNotifier; m_pAlsaNotifier = nullptr; } if (m_iAlsaSubsPort >= 0) { snd_seq_delete_simple_port(m_pAlsaSubsSeq, m_iAlsaSubsPort); m_iAlsaSubsPort = -1; } snd_seq_close(m_pAlsaSubsSeq); m_pAlsaSubsSeq = nullptr; } // Drop everything else, finally. if (m_pAlsaSeq) { // And now, the sequencer queue and handle... snd_seq_free_queue(m_pAlsaSeq, m_iAlsaQueue); snd_seq_close(m_pAlsaSeq); m_iAlsaQueue = -1; m_iAlsaClient = -1; m_pAlsaSeq = nullptr; } // And all other timing tracers. m_iTimeDrift = 0; m_iFrameDrift = 0; m_iTimeStart = 0; m_iFrameStart = 0; m_iTimeStartEx = 0; m_iAudioFrameStart = 0; } // The delta-time/frame accessors. long qtractorMidiEngine::timeStart (void) const { return m_iTimeStart; } // The absolute-time/frame accessors. unsigned long qtractorMidiEngine::timeStartEx (void) const { return m_iTimeStartEx; } // Immediate track mute. void qtractorMidiEngine::trackMute ( qtractorTrack *pTrack, bool bMute ) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiEngine::trackMute(%p, %d)", pTrack, bMute); #endif qtractorSession *pSession = session(); if (pSession == nullptr) return; const unsigned long iFrame = pSession->playHead(); if (bMute) { // Remove all already enqueued events // for the given track and channel... snd_seq_remove_events_t *pre; snd_seq_remove_events_alloca(&pre); snd_seq_timestamp_t ts; const unsigned long iTime = pSession->tickFromFrame(iFrame); ts.tick = ((long) iTime > m_iTimeStart ? iTime - m_iTimeStart : 0); snd_seq_remove_events_set_time(pre, &ts); snd_seq_remove_events_set_tag(pre, pTrack->midiTag()); snd_seq_remove_events_set_channel(pre, pTrack->midiChannel()); snd_seq_remove_events_set_queue(pre, m_iAlsaQueue); snd_seq_remove_events_set_condition(pre, SND_SEQ_REMOVE_OUTPUT | SND_SEQ_REMOVE_TIME_AFTER | SND_SEQ_REMOVE_TIME_TICK | SND_SEQ_REMOVE_DEST_CHANNEL | SND_SEQ_REMOVE_IGNORE_OFF | SND_SEQ_REMOVE_TAG_MATCH); snd_seq_remove_events(m_pAlsaSeq, pre); // Immediate all current notes off. qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus) pMidiBus->setController(pTrack, ALL_NOTES_OFF); // Clear/reset track monitor... qtractorMidiMonitor *pMidiMonitor = static_cast (pTrack->monitor()); if (pMidiMonitor) pMidiMonitor->clear(); // Reset track plugin buffers... if ((pTrack->pluginList())->midiManager()) (pTrack->pluginList())->midiManager()->reset(); // Done track mute. } else { // Must redirect to MIDI ouput thread: // the immediate re-enqueueing of MIDI events. m_pOutputThread->trackSync(pTrack, iFrame); // Done track unmute. } } // Immediate metronome mute. void qtractorMidiEngine::metroMute ( bool bMute ) { if (!m_bMetroEnabled) return; if (!m_bMetronome) return; #ifdef CONFIG_DEBUG qDebug("qtractorMidiEngine::metroMute(%d)\n", int(bMute)); #endif qtractorSession *pSession = session(); if (pSession == nullptr) return; const unsigned long iFrame = pSession->playHead(); if (bMute) { // Remove all already enqueued events // for the given track and channel... snd_seq_remove_events_t *pre; snd_seq_remove_events_alloca(&pre); snd_seq_timestamp_t ts; const unsigned long iTime = pSession->tickFromFrame(iFrame); ts.tick = ((long) iTime > m_iTimeStart ? iTime - m_iTimeStart : 0); snd_seq_remove_events_set_time(pre, &ts); snd_seq_remove_events_set_tag(pre, 0xff); snd_seq_remove_events_set_channel(pre, m_iMetroChannel); snd_seq_remove_events_set_queue(pre, m_iAlsaQueue); snd_seq_remove_events_set_condition(pre, SND_SEQ_REMOVE_OUTPUT | SND_SEQ_REMOVE_TIME_AFTER | SND_SEQ_REMOVE_TIME_TICK | SND_SEQ_REMOVE_DEST_CHANNEL | SND_SEQ_REMOVE_IGNORE_OFF | SND_SEQ_REMOVE_TAG_MATCH); snd_seq_remove_events(m_pAlsaSeq, pre); // Done metronome mute. } else { // Must redirect to MIDI ouput thread: // the immediate re-enqueueing of MIDI events. m_pOutputThread->metroSync(iFrame); // Done metronome unmute. } } // Control bus accessors. void qtractorMidiEngine::setControlBus ( bool bControlBus ) { qtractorBus::ConnectList ins, outs; if (isActivated() && m_bControlBus && m_pIControlBus && m_pOControlBus) { m_pIControlBus->updateConnects(qtractorBus::Input, ins); m_pOControlBus->updateConnects(qtractorBus::Output, outs); } deleteControlBus(); m_bControlBus = bControlBus; createControlBus(); if (isActivated()) { openControlBus(); if (m_bControlBus && m_pIControlBus && m_pOControlBus) { m_pIControlBus->updateConnects(qtractorBus::Input, ins, true); m_pOControlBus->updateConnects(qtractorBus::Output, outs, true); } } } bool qtractorMidiEngine::isControlBus (void) const { return m_bControlBus; } void qtractorMidiEngine::resetControlBus (void) { if (m_bControlBus && m_pOControlBus) return; createControlBus(); } // Control bus simple management. void qtractorMidiEngine::createControlBus (void) { deleteControlBus(); // Whether control bus is here owned, or... if (m_bControlBus) { m_pOControlBus = new qtractorMidiBus(this, "Control", qtractorBus::BusMode(qtractorBus::Duplex | qtractorBus::Ex)); m_pIControlBus = m_pOControlBus; } else { // Find available control buses... for (qtractorBus *pBus = qtractorEngine::buses().first(); pBus; pBus = pBus->next()) { if (m_pIControlBus == nullptr && (pBus->busMode() & qtractorBus::Input)) m_pIControlBus = static_cast (pBus); if (m_pOControlBus == nullptr && (pBus->busMode() & qtractorBus::Output)) m_pOControlBus = static_cast (pBus); } } } // Open MIDI control stuff... bool qtractorMidiEngine::openControlBus (void) { closeControlBus(); // Is there any? if (m_pOControlBus == nullptr) createControlBus(); if (m_pOControlBus == nullptr) return false; // This is it, when dedicated... if (m_bControlBus) { addBusEx(m_pOControlBus); m_pOControlBus->open(); } return true; } // Close MIDI control stuff. void qtractorMidiEngine::closeControlBus (void) { if (m_pOControlBus && m_bControlBus) { m_pOControlBus->close(); removeBusEx(m_pOControlBus); } } // Destroy MIDI control stuff. void qtractorMidiEngine::deleteControlBus (void) { closeControlBus(); // When owned, both input and output // bus are the one and the same... if (m_pOControlBus && m_bControlBus) delete m_pOControlBus; // Reset both control buses... m_pIControlBus = nullptr; m_pOControlBus = nullptr; } // Control buses accessors. qtractorMidiBus *qtractorMidiEngine::controlBus_in() const { return m_pIControlBus; } qtractorMidiBus *qtractorMidiEngine::controlBus_out() const { return m_pOControlBus; } // Player bus accessors. void qtractorMidiEngine::setPlayerBus ( bool bPlayerBus ) { qtractorBus::ConnectList outs; if (isActivated() && m_bPlayerBus && m_pPlayerBus) m_pPlayerBus->updateConnects(qtractorBus::Output, outs); deletePlayerBus(); m_bPlayerBus = bPlayerBus; createPlayerBus(); if (isActivated()) { openPlayerBus(); if (m_bPlayerBus && m_pPlayerBus) m_pPlayerBus->updateConnects(qtractorBus::Output, outs, true); } } bool qtractorMidiEngine::isPlayerBus (void) const { return m_bPlayerBus; } // Player bus simple management. void qtractorMidiEngine::createPlayerBus (void) { deletePlayerBus(); // Whether metronome bus is here owned, or... if (m_bPlayerBus) { m_pPlayerBus = new qtractorMidiBus(this, "Player", qtractorBus::BusMode(qtractorBus::Output | qtractorBus::Ex)); } else { // Find first available output buses... QListIterator iter(buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { m_pPlayerBus = static_cast (pBus); break; } } } } // Open MIDI player stuff... bool qtractorMidiEngine::openPlayerBus (void) { closePlayerBus(); // Is there any? if (m_pPlayerBus == nullptr) createPlayerBus(); if (m_pPlayerBus == nullptr) return false; // This is it, when dedicated... if (m_bPlayerBus) { addBusEx(m_pPlayerBus); m_pPlayerBus->open(); } // Time to create our player... m_pPlayer = new qtractorMidiPlayer(m_pPlayerBus); return true; } // Close MIDI player stuff. void qtractorMidiEngine::closePlayerBus (void) { if (m_pPlayer) m_pPlayer->close(); if (m_pPlayerBus && m_bPlayerBus) { m_pPlayerBus->close(); removeBusEx(m_pPlayerBus); } } // Destroy MIDI player stuff. void qtractorMidiEngine::deletePlayerBus (void) { closePlayerBus(); if (m_pPlayer) { delete m_pPlayer; m_pPlayer = nullptr; } if (m_pPlayerBus && m_bPlayerBus) delete m_pPlayerBus; m_pPlayerBus = nullptr; } // Tell whether audition/pre-listening is active... bool qtractorMidiEngine::isPlayerOpen (void) const { return (m_pPlayer ? m_pPlayer->isOpen() : false); } // Open and start audition/pre-listening... bool qtractorMidiEngine::openPlayer ( const QString& sFilename, int iTrackChannel ) { if (isPlaying()) return false; m_iFrameStart = 0; m_iTimeStart = 0; m_iTimeStartEx = m_iTimeStart; return (m_pPlayer ? m_pPlayer->open(sFilename, iTrackChannel) : false); } // Stop and close audition/pre-listening... void qtractorMidiEngine::closePlayer (void) { if (m_pPlayer) m_pPlayer->close(); } // MMC dispatch special commands. void qtractorMidiEngine::sendMmcLocate ( unsigned long iLocate ) const { unsigned char data[6]; data[0] = 0x01; data[1] = iLocate / (3600 * 30); iLocate -= (3600 * 30) * (int) data[1]; data[2] = iLocate / ( 60 * 30); iLocate -= ( 60 * 30) * (int) data[2]; data[3] = iLocate / ( 30); iLocate -= ( 30) * (int) data[3]; data[4] = iLocate; data[5] = 0; sendMmcCommand(qtractorMmcEvent::LOCATE, data, sizeof(data)); } void qtractorMidiEngine::sendMmcMaskedWrite ( qtractorMmcEvent::SubCommand scmd, int iTrack, bool bOn ) const { unsigned char data[4]; const int iMask = (1 << (iTrack < 2 ? iTrack + 5 : (iTrack - 2) % 7)); data[0] = scmd; data[1] = (unsigned char) (iTrack < 2 ? 0 : 1 + (iTrack - 2) / 7); data[2] = (unsigned char) iMask; data[3] = (unsigned char) (bOn ? iMask : 0); sendMmcCommand(qtractorMmcEvent::MASKED_WRITE, data, sizeof(data)); } void qtractorMidiEngine::sendMmcCommand ( qtractorMmcEvent::Command cmd, unsigned char *pMmcData, unsigned short iMmcData ) const { // Do we have MMC output enabled? if ((m_mmcMode & qtractorBus::Output) == 0) return; // We surely need a output control bus... if (m_pOControlBus == nullptr) return; // Build up the MMC sysex message... unsigned char *pSysex; unsigned short iSysex; iSysex = 6; if (pMmcData && iMmcData > 0) iSysex += 1 + iMmcData; pSysex = new unsigned char [iSysex]; iSysex = 0; pSysex[iSysex++] = 0xf0; // Sysex header. pSysex[iSysex++] = 0x7f; // Realtime sysex. pSysex[iSysex++] = m_mmcDevice; // MMC device id. pSysex[iSysex++] = 0x06; // MMC command mode. pSysex[iSysex++] = (unsigned char) cmd; // MMC command code. if (pMmcData && iMmcData > 0) { pSysex[iSysex++] = iMmcData; ::memcpy(&pSysex[iSysex], pMmcData, iMmcData); iSysex += iMmcData; } pSysex[iSysex++] = 0xf7; // Sysex trailer. // Send it out, now. m_pOControlBus->sendSysex(pSysex, iSysex); // Done. delete [] pSysex; } // SPP dispatch special command. void qtractorMidiEngine::sendSppCommand ( int iCmdType, unsigned int iSongPos ) const { qtractorSession *pSession = session(); if (pSession == nullptr) return; // Do we have SPP output enabled? if ((m_sppMode & qtractorBus::Output) == 0) return; // We surely need a output control bus... if (m_pOControlBus == nullptr) return; // Initialize sequencer event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Addressing... snd_seq_ev_set_source(&ev, m_pOControlBus->alsaPort()); snd_seq_ev_set_subs(&ev); // Set command parameters... // - SND_SEQ_EVENT_START // - SND_SEQ_EVENT_STOP // - SND_SEQ_EVENT_CONTINUE // - SND_SEQ_EVENT_SONGPOS ev.type = snd_seq_event_type(iCmdType); ev.data.control.value = iSongPos; // The event will be either direct or scheduled... if (iCmdType != SND_SEQ_EVENT_SONGPOS && iSongPos > 0) { // Scheduled (account for the time playback/queue started)... const unsigned long iTime = (iSongPos * pSession->ticksPerBeat()) >> 2; const unsigned long tick = (long(iTime) > m_iTimeStart ? iTime - m_iTimeStart : 0); snd_seq_ev_schedule_tick(&ev, m_iAlsaQueue, 0, pSession->timep(tick)); snd_seq_event_output(m_pAlsaSeq, &ev); } else { // Direct (immediate)... snd_seq_ev_set_direct(&ev); snd_seq_event_output_direct(m_pAlsaSeq, &ev); } } // Metronome switching. void qtractorMidiEngine::setMetronome ( bool bMetronome ) { m_bMetronome = bMetronome; if (isPlaying()) metroMute(!m_bMetronome); } bool qtractorMidiEngine::isMetronome (void) const { return m_bMetronome; } // Metronome enabled accessors. void qtractorMidiEngine::setMetroEnabled ( bool bMetroEnabled ) { m_bMetroEnabled = bMetroEnabled; } bool qtractorMidiEngine::isMetroEnabled (void) const { return m_bMetroEnabled; } // Metronome bus accessors. void qtractorMidiEngine::setMetroBus ( bool bMetroBus ) { qtractorBus::ConnectList outs; if (isActivated() && m_bMetroBus && m_pMetroBus) m_pMetroBus->updateConnects(qtractorBus::Output, outs); deleteMetroBus(); m_bMetroBus = bMetroBus; createMetroBus(); if (isActivated()) { openMetroBus(); if (m_bMetroBus && m_pMetroBus) m_pMetroBus->updateConnects(qtractorBus::Output, outs, true); } } bool qtractorMidiEngine::isMetroBus (void) const { return m_bMetroBus; } void qtractorMidiEngine::resetMetroBus (void) { if (m_bMetroBus && m_pMetroBus) return; createMetroBus(); } // Metronome bus simple management. void qtractorMidiEngine::createMetroBus (void) { deleteMetroBus(); if (!m_bMetroEnabled) return; // Whether metronome bus is here owned, or... if (m_bMetroBus) { m_pMetroBus = new qtractorMidiBus(this, "Metronome", qtractorBus::BusMode(qtractorBus::Output | qtractorBus::Ex)); } else { // Find first available output buses... QListIterator iter(buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { m_pMetroBus = static_cast (pBus); break; } } } } // Open MIDI metronome stuff... bool qtractorMidiEngine::openMetroBus (void) { closeMetroBus(); if (!m_bMetroEnabled) return false; // Is there any? if (m_pMetroBus == nullptr) createMetroBus(); if (m_pMetroBus == nullptr) return false; // This is it, when dedicated... if (m_bMetroBus) { addBusEx(m_pMetroBus); m_pMetroBus->open(); } return true; } // Close MIDI metronome stuff. void qtractorMidiEngine::closeMetroBus (void) { if (m_pMetroBus && m_bMetroBus) { m_pMetroBus->close(); removeBusEx(m_pMetroBus); } } // Destroy MIDI metronome stuff. void qtractorMidiEngine::deleteMetroBus (void) { closeMetroBus(); if (m_pMetroBus && m_bMetroBus) delete m_pMetroBus; m_pMetroBus = nullptr; } // Metronome channel accessors. void qtractorMidiEngine::setMetroChannel ( unsigned short iChannel ) { m_iMetroChannel = iChannel; } unsigned short qtractorMidiEngine::metroChannel (void) const { return m_iMetroChannel; } // Metronome bar parameters. void qtractorMidiEngine::setMetroBar ( int iNote, int iVelocity, unsigned long iDuration ) { m_iMetroBarNote = iNote; m_iMetroBarVelocity = iVelocity; m_iMetroBarDuration = iDuration; } int qtractorMidiEngine::metroBarNote (void) const { return m_iMetroBarNote; } int qtractorMidiEngine::metroBarVelocity (void) const { return m_iMetroBarVelocity; } unsigned long qtractorMidiEngine::metroBarDuration (void) const { return m_iMetroBarDuration; } // Metronome bar parameters. void qtractorMidiEngine::setMetroBeat ( int iNote, int iVelocity, unsigned long iDuration ) { m_iMetroBeatNote = iNote; m_iMetroBeatVelocity = iVelocity; m_iMetroBeatDuration = iDuration; } int qtractorMidiEngine::metroBeatNote (void) const { return m_iMetroBarNote; } int qtractorMidiEngine::metroBeatVelocity (void) const { return m_iMetroBarVelocity; } unsigned long qtractorMidiEngine::metroBeatDuration (void) const { return m_iMetroBeatDuration; } // Metronome latency offset (in ticks). void qtractorMidiEngine::setMetroOffset ( unsigned long iMetroOffset ) { m_iMetroOffset = iMetroOffset; } unsigned long qtractorMidiEngine::metroOffset (void) const { return m_iMetroOffset; } // Process metronome clicks. void qtractorMidiEngine::processMetro ( unsigned long iFrameStart, unsigned long iFrameEnd ) { if (m_pMetroCursor == nullptr) return; qtractorSession *pSession = session(); if (pSession == nullptr) return; qtractorTimeScale::Node *pNode = m_pMetroCursor->seekFrame(iFrameEnd); // Take this moment to check for tempo changes... if (pNode->tempo != m_fMetroTempo) { // New tempo node... const unsigned long iTime = (pNode->frame < iFrameStart ? pNode->tickFromFrame(iFrameStart) : pNode->tick); // Enqueue tempo event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Scheduled delivery: take into account // the time playback/queue started... const unsigned long tick = (long(iTime) > m_iTimeStart ? iTime - m_iTimeStart : 0); snd_seq_ev_schedule_tick(&ev, m_iAlsaQueue, 0, pSession->timep(tick)); ev.type = SND_SEQ_EVENT_TEMPO; ev.data.queue.queue = m_iAlsaQueue; ev.data.queue.param.value = (unsigned int) (60000000.0f / pNode->tempo); ev.dest.client = SND_SEQ_CLIENT_SYSTEM; ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER; // Pump it into the queue. snd_seq_event_output(m_pAlsaSeq, &ev); // Save for next change. m_fMetroTempo = pNode->tempo; // Update MIDI monitor slot stuff... qtractorMidiMonitor::splitTime( m_pMetroCursor->timeScale(), pNode->frame, tick); } // Get on with the actual metronome/count-in/clock stuff... if (!m_bMetronome && (m_clockMode & qtractorBus::Output) == 0) return; // Register the next metronome/clock beat slot. const unsigned long iTimeEnd = pNode->tickFromFrame(iFrameEnd); pNode = m_pMetroCursor->seekFrame(iFrameStart); const unsigned long iTimeStart = pNode->tickFromFrame(iFrameStart); unsigned int iBeat = pNode->beatFromTick(iTimeStart); unsigned long iTime = pNode->tickFromBeat(iBeat); // Initialize outbound metronome event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Addressing... if (m_pMetroBus) { snd_seq_ev_set_source(&ev, m_pMetroBus->alsaPort()); snd_seq_ev_set_subs(&ev); } // Set common event data... ev.tag = (unsigned char) 0xff; ev.type = SND_SEQ_EVENT_NOTE; ev.data.note.channel = m_iMetroChannel; // Initialize outbound clock event... snd_seq_event_t ev_clock; snd_seq_ev_clear(&ev_clock); // Addressing... if (m_pOControlBus) { snd_seq_ev_set_source(&ev_clock, m_pOControlBus->alsaPort()); snd_seq_ev_set_subs(&ev_clock); } // Set common event data... ev_clock.tag = (unsigned char) 0xff; ev_clock.type = SND_SEQ_EVENT_CLOCK; while (iTime < iTimeEnd) { // Scheduled delivery: take into account // the time playback/queue started... if (m_clockMode & qtractorBus::Output) { unsigned long iTimeClock = iTime; const unsigned int iTicksPerClock = pNode->ticksPerBeat / 24; for (unsigned int iClock = 0; iClock < 24; ++iClock) { if (iTimeClock >= iTimeEnd) break; if (iTimeClock >= iTimeStart) { const unsigned long tick = (long(iTimeClock) > m_iTimeStart ? iTimeClock - m_iTimeStart : 0); snd_seq_ev_schedule_tick(&ev_clock, m_iAlsaQueue, 0, pSession->timep(tick)); snd_seq_event_output(m_pAlsaSeq, &ev_clock); } iTimeClock += iTicksPerClock; } } if (m_bMetronome && iTime >= iTimeStart) { // Have some latency compensation... const unsigned long iTimeOffset = (m_iMetroOffset > 0 && iTime > m_iMetroOffset ? iTime - m_iMetroOffset : iTime); // Set proper event schedule time... const unsigned long tick = (long(iTimeOffset) > m_iTimeStart ? iTimeOffset - m_iTimeStart : 0); snd_seq_ev_schedule_tick(&ev, m_iAlsaQueue, 0, pSession->timep(tick)); // Set proper event data... if (pNode->beatIsBar(iBeat)) { ev.data.note.note = m_iMetroBarNote; ev.data.note.velocity = m_iMetroBarVelocity; ev.data.note.duration = m_iMetroBarDuration; } else { ev.data.note.note = m_iMetroBeatNote; ev.data.note.velocity = m_iMetroBeatVelocity; ev.data.note.duration = m_iMetroBeatDuration; } // Pump it into the queue. snd_seq_event_output(m_pAlsaSeq, &ev); // MIDI track monitoring... if (m_pMetroBus && m_pMetroBus->midiMonitor_out()) { m_pMetroBus->midiMonitor_out()->enqueue( qtractorMidiEvent::NOTEON, ev.data.note.velocity, tick); } } // Go for next beat... iTime += pNode->ticksPerBeat; pNode = m_pMetroCursor->seekBeat(++iBeat); } } // Access to current tempo/time-signature cursor. qtractorTimeScale::Cursor *qtractorMidiEngine::metroCursor (void) const { return m_pMetroCursor; } // Metronome count-in switching. void qtractorMidiEngine::setCountIn ( bool bCountIn ) { m_bCountIn = bCountIn; } bool qtractorMidiEngine::isCountIn (void) const { return m_bCountIn; } // Metronome count-in mode. void qtractorMidiEngine::setCountInMode ( CountInMode countInMode ) { m_countInMode = countInMode; } qtractorMidiEngine::CountInMode qtractorMidiEngine::countInMode (void) const { return m_countInMode; } // Metronome count-in number of beats. void qtractorMidiEngine::setCountInBeats ( unsigned short iCountInBeats ) { m_iCountInBeats = iCountInBeats; } unsigned short qtractorMidiEngine::countInBeats (void) const { return m_iCountInBeats; } // Metronome count-in status. unsigned short qtractorMidiEngine::countIn ( unsigned int nframes ) { if (m_iCountIn > 0) { m_iCountInFrame += nframes; if (m_iCountInFrame >= m_iCountInFrameEnd) { m_iCountIn = 0; resetTime(); } else { sync(); } } return m_iCountIn; } unsigned short qtractorMidiEngine::countIn (void) const { return m_iCountIn; } // Process metronome count-ins. void qtractorMidiEngine::processCountIn ( unsigned long iFrameStart, unsigned long iFrameEnd ) { if (m_pMetroCursor == nullptr) return; // Get on with the actual metronome/count-in stuff... if (m_iCountIn < 1) return; qtractorSession *pSession = session(); if (pSession == nullptr) return; // Register the next metronome/clock beat slot. qtractorTimeScale::Node *pNode = m_pMetroCursor->seekFrame(iFrameEnd); const unsigned long iTimeEnd = pNode->tickFromFrame(iFrameEnd); pNode = m_pMetroCursor->seekFrame(iFrameStart); const unsigned long iTimeStart = pNode->tickFromFrame(iFrameStart); unsigned int iBeat = pNode->beatFromTick(iTimeStart); unsigned long iTime = pNode->tickFromBeat(iBeat); // Initialize outbound metronome event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Addressing... if (m_pMetroBus) { snd_seq_ev_set_source(&ev, m_pMetroBus->alsaPort()); snd_seq_ev_set_subs(&ev); } // Set common event data... ev.tag = (unsigned char) 0xff; ev.type = SND_SEQ_EVENT_NOTE; ev.data.note.channel = m_iMetroChannel; while (iTime < iTimeEnd) { // Scheduled delivery: take into account // the time playback/queue started... if (iTime >= iTimeStart) { // Have some latency compensation... const unsigned long iTimeOffset = (m_iMetroOffset > 0 && iTime > m_iMetroOffset ? iTime - m_iMetroOffset : iTime); // Set proper event schedule time... const unsigned long tick = (long(iTimeOffset) > m_iCountInTimeStart ? iTimeOffset - m_iCountInTimeStart : 0); snd_seq_ev_schedule_tick(&ev, m_iAlsaQueue, 0, pSession->timep(tick)); // Set proper event data... if (pNode->beatIsBar(iBeat)) { ev.data.note.note = m_iMetroBarNote; ev.data.note.velocity = m_iMetroBarVelocity; ev.data.note.duration = m_iMetroBarDuration; } else { ev.data.note.note = m_iMetroBeatNote; ev.data.note.velocity = m_iMetroBeatVelocity; ev.data.note.duration = m_iMetroBeatDuration; } // Pump it into the queue. snd_seq_event_output(m_pAlsaSeq, &ev); // MIDI track monitoring... if (m_pMetroBus && m_pMetroBus->midiMonitor_out()) { m_pMetroBus->midiMonitor_out()->enqueue( qtractorMidiEvent::NOTEON, ev.data.note.velocity, tick); } // --m_iCountIn; // Not really useful? } // Go for next beat... iTime += pNode->ticksPerBeat; pNode = m_pMetroCursor->seekBeat(++iBeat); } snd_seq_drain_output(m_pAlsaSeq); } // Document element methods. bool qtractorMidiEngine::loadElement ( qtractorDocument *pDocument, QDomElement *pElement ) { qtractorEngine::clear(); createControlBus(); createMetroBus(); QStringList midi_buses2; // Load session children... for (QDomNode nChild = pElement->firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert node to element... QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; if (eChild.tagName() == "midi-control") { for (QDomNode nProp = eChild.firstChild(); !nProp.isNull(); nProp = nProp.nextSibling()) { QDomElement eProp = nProp.toElement(); if (eProp.isNull()) continue; if (eProp.tagName() == "mmc-mode") { qtractorMidiEngine::setMmcMode( qtractorBus::busModeFromText(eProp.text())); } else if (eProp.tagName() == "mmc-device") { qtractorMidiEngine::setMmcDevice( eProp.text().toInt() & 0x7f); } else if (eProp.tagName() == "spp-mode") { qtractorMidiEngine::setSppMode( qtractorBus::busModeFromText(eProp.text())); } else if (eProp.tagName() == "clock-mode") { qtractorMidiEngine::setClockMode( qtractorBus::busModeFromText(eProp.text())); } } } else if (eChild.tagName() == "midi-bus") { QString sBusName = eChild.attribute("name"); qtractorMidiBus::BusMode busMode = qtractorBus::busModeFromText(eChild.attribute("mode")); qtractorMidiBus *pMidiBus = new qtractorMidiBus(this, sBusName, busMode); if (!pMidiBus->loadElement(pDocument, &eChild)) return false; qtractorMidiEngine::addBus(pMidiBus); } else if (eChild.tagName() == "control-inputs") { if (m_bControlBus && m_pIControlBus) { m_pIControlBus->loadConnects( m_pIControlBus->inputs(), pDocument, &eChild); } } else if (eChild.tagName() == "control-outputs") { if (m_bControlBus && m_pOControlBus) { m_pOControlBus->loadConnects( m_pOControlBus->outputs(), pDocument, &eChild); } } else if (eChild.tagName() == "metronome-outputs") { if (m_bMetroBus && m_pMetroBus) { m_pMetroBus->loadConnects( m_pMetroBus->outputs(), pDocument, &eChild); } } else if (eChild.tagName() == "midi-buses2") { midi_buses2 = qtractorEngine::loadBuses2List( pDocument, &eChild, "midi-bus2"); } } qtractorEngine::setBuses2List(midi_buses2); return true; } bool qtractorMidiEngine::saveElement ( qtractorDocument *pDocument, QDomElement *pElement ) const { // Save transport/control modes... QDomElement eControl = pDocument->document()->createElement("midi-control"); pDocument->saveTextElement("mmc-mode", qtractorBus::textFromBusMode( qtractorMidiEngine::mmcMode()), &eControl); pDocument->saveTextElement("mmc-device", QString::number(int(qtractorMidiEngine::mmcDevice())), &eControl); pDocument->saveTextElement("spp-mode", qtractorBus::textFromBusMode( qtractorMidiEngine::sppMode()), &eControl); pDocument->saveTextElement("clock-mode", qtractorBus::textFromBusMode( qtractorMidiEngine::clockMode()), &eControl); pElement->appendChild(eControl); // Save MIDI buses... QListIterator iter(qtractorEngine::buses2()); while (iter.hasNext()) { qtractorMidiBus *pMidiBus = static_cast (iter.next()); if (pMidiBus) { // Create the new MIDI bus element... QDomElement eMidiBus = pDocument->document()->createElement("midi-bus"); pMidiBus->saveElement(pDocument, &eMidiBus); pElement->appendChild(eMidiBus); } } // Control bus (input) connects... if (m_bControlBus && m_pIControlBus) { QDomElement eInputs = pDocument->document()->createElement("control-inputs"); qtractorBus::ConnectList inputs; m_pIControlBus->updateConnects(qtractorBus::Input, inputs); m_pIControlBus->saveConnects(inputs, pDocument, &eInputs); pElement->appendChild(eInputs); } // Control bus (output) connects... if (m_bControlBus && m_pOControlBus) { QDomElement eOutputs = pDocument->document()->createElement("control-outputs"); qtractorBus::ConnectList outputs; m_pOControlBus->updateConnects(qtractorBus::Output, outputs); m_pOControlBus->saveConnects(outputs, pDocument, &eOutputs); pElement->appendChild(eOutputs); } // Metronome bus connects... if (m_bMetroBus && m_pMetroBus) { QDomElement eOutputs = pDocument->document()->createElement("metronome-outputs"); qtractorBus::ConnectList outputs; m_pMetroBus->updateConnects(qtractorBus::Output, outputs); m_pMetroBus->saveConnects(outputs, pDocument, &eOutputs); pElement->appendChild(eOutputs); } const QStringList& midi_buses2 = qtractorEngine::buses2List(); if (!midi_buses2.isEmpty()) { QDomElement eBuses2 = pDocument->document()->createElement("midi-buses2"); saveBuses2List(pDocument, &eBuses2, "midi-bus2", midi_buses2); pElement->appendChild(eBuses2); } return true; } // MIDI-export method. bool qtractorMidiEngine::fileExport ( const QString& sExportPath, const QList& exportBuses, unsigned long iExportStart, unsigned long iExportEnd, int iExportFormat ) { // No simultaneous or foul exports... if (isPlaying()) return false; // Make sure we have an actual session cursor... qtractorSession *pSession = session(); if (pSession == nullptr) return false; // Cannot have exports longer than current session. if (iExportStart >= iExportEnd) iExportEnd = pSession->sessionEnd(); if (iExportStart >= iExportEnd) return false; const unsigned short iTicksPerBeat = pSession->ticksPerBeat(); const unsigned long iTimeStart = pSession->tickFromFrame(iExportStart); const unsigned long iTimeEnd = pSession->tickFromFrame(iExportEnd); const unsigned short iFormat = (iExportFormat < 0 ? qtractorMidiClip::defaultFormat() : iExportFormat); unsigned short iSeq; unsigned short iSeqs = 0; QList seqs; qtractorMidiSequence **ppSeqs = nullptr; if (iFormat == 0) { iSeqs = 16; ppSeqs = new qtractorMidiSequence * [iSeqs]; for (iSeq = 0; iSeq < iSeqs; ++iSeq) { ppSeqs[iSeq] = new qtractorMidiSequence( QString(), iSeq, iTicksPerBeat); } } // Do the real grunt work, get eaach elligigle track // and copy the events in range to be written out... QListIterator bus_iter(exportBuses); unsigned short iTracks = 0; for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() != qtractorTrack::Midi) continue; if (pTrack->isMute() || (pSession->soloTracks() && !pTrack->isSolo())) continue; qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus == nullptr) continue; // Check whether this track makes it // as one of the exported buses.... qtractorMidiBus *pExportBus = nullptr; bus_iter.toFront(); while (bus_iter.hasNext()) { pExportBus = bus_iter.next(); if (pExportBus && pExportBus->alsaPort() == pMidiBus->alsaPort()) break; pExportBus = nullptr; } // Is it not? if (pExportBus == nullptr) continue; // We have a target sequence, maybe reused... qtractorMidiSequence *pSeq; if (ppSeqs) { // SMF Format 0 pSeq = ppSeqs[pTrack->midiChannel() & 0x0f]; QString sName = pSeq->name(); if (!sName.isEmpty()) sName += "; "; pSeq->setName(sName + pTrack->shortTrackName()); } else { // SMF Format 1 ++iTracks; pSeq = new qtractorMidiSequence( pTrack->shortTrackName(), iTracks, iTicksPerBeat); pSeq->setChannel(pTrack->midiChannel()); seqs.append(pSeq); } // Make this track setup... if (pSeq->bankSelMethod() < 0) pSeq->setBankSelMethod(pTrack->midiBankSelMethod()); if (pSeq->bank() < 0) pSeq->setBank(pTrack->midiBank()); if (pSeq->prog() < 0) pSeq->setProg(pTrack->midiProg()); // Now, for every clip... qtractorClip *pClip = pTrack->clips().first(); while (pClip && pClip->clipStart() + pClip->clipLength() < iExportStart) pClip = pClip->next(); while (pClip && pClip->clipStart() < iExportEnd) { qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) { const unsigned long iTimeClip = pSession->tickFromFrame(pClip->clipStart()); const unsigned long iTimeOffset = iTimeClip - iTimeStart; const float fGain = pMidiClip->clipGain(); // For each event... qtractorMidiEvent *pEvent = pMidiClip->sequence()->events().first(); while (pEvent && iTimeClip + pEvent->time() < iTimeStart) pEvent = pEvent->next(); while (pEvent && iTimeClip + pEvent->time() < iTimeEnd) { qtractorMidiEvent *pNewEvent = new qtractorMidiEvent(*pEvent); pNewEvent->setTime(iTimeOffset + pEvent->time()); if (pNewEvent->type() == qtractorMidiEvent::NOTEON) { const unsigned long iTimeEvent = iTimeClip + pEvent->time(); const float fVolume = fGain * pMidiClip->fadeInOutGain( pSession->frameFromTick(iTimeEvent) - pClip->clipStart()); pNewEvent->setVelocity((unsigned char) (fVolume * float(pEvent->velocity())) & 0x7f); if (iTimeEvent + pEvent->duration() > iTimeEnd) pNewEvent->setDuration(iTimeEnd - iTimeEvent); } pSeq->insertEvent(pNewEvent); pEvent = pEvent->next(); } } pClip = pClip->next(); } // Have a break... qtractorSession::stabilize(); } // Account for the only or META info track... ++iTracks; // Special on SMF Format 1... if (ppSeqs == nullptr) { // Sanity check... if (iTracks < 1) return false; // Number of actual track sequences... iSeqs = iTracks; ppSeqs = new qtractorMidiSequence * [iSeqs]; QListIterator seq_iter(seqs); ppSeqs[0] = nullptr; // META info track... for (iSeq = 1; iSeq < iSeqs && seq_iter.hasNext(); ++iSeq) ppSeqs[iSeq] = seq_iter.next(); // May clear it now. seqs.clear(); } // Prepare file for writing... qtractorMidiFile file; // File ready for export? const bool bResult = file.open(sExportPath, qtractorMidiFile::Write); if (bResult) { if (file.writeHeader(iFormat, iTracks, iTicksPerBeat)) { // Export SysEx setups... bus_iter.toFront(); while (bus_iter.hasNext()) { qtractorMidiBus *pExportBus = bus_iter.next(); qtractorMidiSysexList *pSysexList = pExportBus->sysexList(); if (pSysexList && pSysexList->count() > 0) { if (ppSeqs[0] == nullptr) { ppSeqs[0] = new qtractorMidiSequence( QFileInfo(sExportPath).baseName(), 0, iTicksPerBeat); } pExportBus->exportSysexList(ppSeqs[0]); } } // Export tempo map as well... if (file.tempoMap()) { file.tempoMap()->fromTimeScale( pSession->timeScale(), iTimeStart); } file.writeTracks(ppSeqs, iSeqs); } file.close(); } // Free locally allocated track/sequence array. for (iSeq = 0; iSeq < iSeqs; ++iSeq) { if (ppSeqs[iSeq]) delete ppSeqs[iSeq]; } delete [] ppSeqs; // Done successfully. return bResult; } // Retrieve/restore all connections, on all MIDI buses. // return the total number of effective (re)connection attempts... int qtractorMidiEngine::updateConnects (void) { // Do it as usual, on all standard owned dependable buses... const int iUpdate = qtractorEngine::updateConnects(); // Reset all pending controllers, if any... if (m_iResetAllControllersPending > 0) resetAllControllers(true); // Force immediate! // Done. return iUpdate; } // Capture/input (record) quantization accessors. // (value in snap-per-beat units) void qtractorMidiEngine::setCaptureQuantize ( unsigned short iCaptureQuantize ) { m_iCaptureQuantize = iCaptureQuantize; } unsigned short qtractorMidiEngine::captureQuantize (void) const { return m_iCaptureQuantize; } // ALSA device queue timer. void qtractorMidiEngine::setAlsaTimer ( int iAlsaTimer ) { m_iAlsaTimer = iAlsaTimer; } int qtractorMidiEngine::alsaTimer (void) const { return m_iAlsaTimer; } // Drift check/correction accessors. void qtractorMidiEngine::setDriftCorrect ( bool bDriftCorrect ) { m_bDriftCorrect = bDriftCorrect; } bool qtractorMidiEngine::isDriftCorrect (void) const { return m_bDriftCorrect; } // MMC device-id accessors. void qtractorMidiEngine::setMmcDevice ( unsigned char mmcDevice ) { m_mmcDevice = mmcDevice; } unsigned char qtractorMidiEngine::mmcDevice (void) const { return m_mmcDevice; } // MMC mode accessors. void qtractorMidiEngine::setMmcMode ( qtractorBus::BusMode mmcMode ) { m_mmcMode = mmcMode; } qtractorBus::BusMode qtractorMidiEngine::mmcMode (void) const { return m_mmcMode; } // SPP mode accessors. void qtractorMidiEngine::setSppMode ( qtractorBus::BusMode sppMode ) { m_sppMode = sppMode; } qtractorBus::BusMode qtractorMidiEngine::sppMode (void) const { return m_sppMode; } // MIDI Clock mode accessors. void qtractorMidiEngine::setClockMode ( qtractorBus::BusMode clockMode ) { m_clockMode = clockMode; } qtractorBus::BusMode qtractorMidiEngine::clockMode (void) const { return m_clockMode; } // Whether to reset all MIDI controllers (on playback start). void qtractorMidiEngine::setResetAllControllers ( bool bResetAllControllers ) { m_bResetAllControllers = bResetAllControllers; } bool qtractorMidiEngine::isResetAllControllers (void) const { return m_bResetAllControllers; } // Process pending step-input/overdub events... void qtractorMidiEngine::processInpEvents (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; QMutexLocker locker(&m_inpMutex); const bool bOverdub = isPlaying(); QList keys; // Avoid duplicates... QListIterator iter(m_inpEvents.keys()); while (iter.hasNext()) { qtractorMidiClip *pMidiClip = iter.next(); if (keys.contains(pMidiClip)) continue; keys.append(pMidiClip); // Step input/overdub control... const QList& events = m_inpEvents.values(pMidiClip); // Apply command *iif* MIDI clip editor is up there, // otherwise make it global to session... pMidiClip->processInpEvents(events, bOverdub); } m_inpEvents.clear(); } //---------------------------------------------------------------------- // class qtractorMidiBus -- Managed ALSA sequencer port set // // Constructor. qtractorMidiBus::qtractorMidiBus ( qtractorMidiEngine *pMidiEngine, const QString& sBusName, BusMode busMode, bool bMonitor ) : qtractorBus(pMidiEngine, sBusName, busMode, bMonitor) { m_iAlsaPort = -1; if ((busMode & qtractorBus::Input) && !(busMode & qtractorBus::Ex)) { m_pIMidiMonitor = new qtractorMidiMonitor(); m_pIPluginList = createPluginList(qtractorPluginList::MidiInBus); } else { m_pIMidiMonitor = nullptr; m_pIPluginList = nullptr; } if ((busMode & qtractorBus::Output) && !(busMode & qtractorBus::Ex)) { m_pOMidiMonitor = new qtractorMidiMonitor(); m_pOPluginList = createPluginList(qtractorPluginList::MidiOutBus); m_pSysexList = new qtractorMidiSysexList(); } else { m_pOMidiMonitor = nullptr; m_pOPluginList = nullptr; m_pSysexList = nullptr; } } // Destructor. qtractorMidiBus::~qtractorMidiBus (void) { close(); if (m_pIMidiMonitor) delete m_pIMidiMonitor; if (m_pOMidiMonitor) delete m_pOMidiMonitor; if (m_pIPluginList) delete m_pIPluginList; if (m_pOPluginList) delete m_pOPluginList; if (m_pSysexList) delete m_pSysexList; } // ALSA sequencer port accessor. int qtractorMidiBus::alsaPort (void) const { return m_iAlsaPort; } // Register and pre-allocate bus port buffers. bool qtractorMidiBus::open (void) { // close(); qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return false; snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return false; const qtractorBus::BusMode busMode = qtractorMidiBus::busMode(); // The very same port might be used for input and output... unsigned int flags = 0; if (busMode & qtractorBus::Input) flags |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; if (busMode & qtractorBus::Output) flags |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; m_iAlsaPort = snd_seq_create_simple_port( pAlsaSeq, busName().toUtf8().constData(), flags, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); if (m_iAlsaPort < 0) return false; // We want to know when the events get delivered to us... snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca(&pinfo); if (snd_seq_get_port_info(pAlsaSeq, m_iAlsaPort, pinfo) < 0) return false; snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, pMidiEngine->alsaQueue()); snd_seq_port_info_set_timestamp_real(pinfo, 0); // MIDI ticks. if (snd_seq_set_port_info(pAlsaSeq, m_iAlsaPort, pinfo) < 0) return false; // Update monitor subject names... qtractorMidiBus::updateBusName(); // Plugin lists need some buffer (re)allocation too... if (m_pIPluginList) updatePluginList(m_pIPluginList, qtractorPluginList::MidiInBus); if (m_pOPluginList) updatePluginList(m_pOPluginList, qtractorPluginList::MidiOutBus); // Finally add this to the elligible input registry... if (m_pIMidiMonitor) pMidiEngine->addInputBus(this); // Done. return true; } // Unregister and post-free bus port buffers. void qtractorMidiBus::close (void) { qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return; snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; if (m_pIMidiMonitor) pMidiEngine->removeInputBus(this); shutOff(true); snd_seq_delete_simple_port(pAlsaSeq, m_iAlsaPort); m_iAlsaPort = -1; } // Bus mode change event. void qtractorMidiBus::updateBusMode (void) { const qtractorBus::BusMode busMode = qtractorMidiBus::busMode(); // Have a new/old input monitor? if ((busMode & qtractorBus::Input) && !(busMode & qtractorBus::Ex)) { if (m_pIMidiMonitor == nullptr) m_pIMidiMonitor = new qtractorMidiMonitor(); if (m_pIPluginList == nullptr) m_pIPluginList = createPluginList(qtractorPluginList::MidiInBus); } else { if (m_pIMidiMonitor) { delete m_pIMidiMonitor; m_pIMidiMonitor = nullptr; } if (m_pIPluginList) { delete m_pIPluginList; m_pIPluginList = nullptr; } } // Have a new/old output monitor? if ((busMode & qtractorBus::Output) && !(busMode & qtractorBus::Ex)) { if (m_pOMidiMonitor == nullptr) m_pOMidiMonitor = new qtractorMidiMonitor(); if (m_pOPluginList == nullptr) m_pOPluginList = createPluginList(qtractorPluginList::MidiOutBus); if (m_pSysexList == nullptr) m_pSysexList = new qtractorMidiSysexList(); } else { if (m_pOMidiMonitor) { delete m_pOMidiMonitor; m_pOMidiMonitor = nullptr; } if (m_pOPluginList) { delete m_pOPluginList; m_pOPluginList = nullptr; } if (m_pSysexList) { delete m_pSysexList; m_pSysexList = nullptr; } } } // Bus name change event. void qtractorMidiBus::updateBusName (void) { const QString& sBusName = qtractorMidiBus::busName(); if (m_pIMidiMonitor) { const QString& sBusNameIn = QObject::tr("%1 In").arg(sBusName); m_pIMidiMonitor->gainSubject()->setName( QObject::tr("%1 Volume").arg(sBusNameIn)); m_pIMidiMonitor->panningSubject()->setName( QObject::tr("%1 Pan").arg(sBusNameIn)); } if (m_pOMidiMonitor) { const QString& sBusNameOut = QObject::tr("%1 Out").arg(sBusName); m_pOMidiMonitor->gainSubject()->setName( QObject::tr("%1 Volume").arg(sBusNameOut)); m_pOMidiMonitor->panningSubject()->setName( QObject::tr("%1 Pan").arg(sBusNameOut)); } qtractorBus::updateBusName(); } // Shut-off everything out there. void qtractorMidiBus::shutOff ( bool bClose ) { qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return; snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; if ((busMode() & qtractorBus::Output) == 0) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiBus[%p]::shutOff(%d)", this, int(bClose)); #endif dequeueNoteOffs(pMidiEngine->queueTime()); QHash::ConstIterator iter = m_patches.constBegin(); const QHash::ConstIterator& iter_end = m_patches.constEnd(); for ( ; iter != iter_end; ++iter) { const unsigned short iChannel = iter.key(); setControllerEx(iChannel, ALL_SOUND_OFF); setControllerEx(iChannel, ALL_NOTES_OFF); if (bClose) setControllerEx(iChannel, ALL_CONTROLLERS_OFF); } } // Default instrument name accessors. void qtractorMidiBus::setInstrumentName ( const QString& sInstrumentName ) { m_sInstrumentName = sInstrumentName; } const QString& qtractorMidiBus::instrumentName (void) const { return m_sInstrumentName; } // SysEx setup list accessors. qtractorMidiSysexList *qtractorMidiBus::sysexList (void) const { return m_pSysexList; } // Direct MIDI bank/program selection helper. void qtractorMidiBus::setPatch ( unsigned short iChannel, const QString& sInstrumentName, int iBankSelMethod, int iBank, int iProg, qtractorTrack *pTrack ) { // We always need our MIDI engine reference... qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMidiBus[%p]::setPatch(%d, \"%s\", %d, %d, %d)", this, iChannel, sInstrumentName.toUtf8().constData(), iBankSelMethod, iBank, iProg); #endif // Update patch mapping... Patch& patch = m_patches[iChannel & 0x0f]; patch.instrumentName = sInstrumentName; patch.bankSelMethod = iBankSelMethod; patch.bank = iBank; patch.prog = iProg; // Sanity check. if (!patch.isValid()) m_patches.remove(iChannel & 0x0f); // Don't do anything else if engine // has not been activated... snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; // Do it for the MIDI plugins if applicable... qtractorMidiManager *pTrackMidiManager = nullptr; if (pTrack) pTrackMidiManager = (pTrack->pluginList())->midiManager(); qtractorMidiManager *pBusMidiManager = nullptr; if (pluginList_out()) pBusMidiManager = pluginList_out()->midiManager(); // Initialize sequencer event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Addressing... snd_seq_ev_set_source(&ev, m_iAlsaPort); snd_seq_ev_set_subs(&ev); // The event will be direct... snd_seq_ev_set_direct(&ev); // Select Bank MSB. if (iBank >= 0 && (iBankSelMethod == 0 || iBankSelMethod == 1)) { ev.type = SND_SEQ_EVENT_CONTROLLER; ev.data.control.channel = iChannel; ev.data.control.param = BANK_SELECT_MSB; if (iBankSelMethod == 0) ev.data.control.value = (iBank & 0x3f80) >> 7; else ev.data.control.value = (iBank & 0x007f); snd_seq_event_output_direct(pAlsaSeq, &ev); if (pTrackMidiManager) pTrackMidiManager->direct(&ev); if (pBusMidiManager) pBusMidiManager->direct(&ev); } // Select Bank LSB. if (iBank >= 0 && (iBankSelMethod == 0 || iBankSelMethod == 2)) { ev.type = SND_SEQ_EVENT_CONTROLLER; ev.data.control.channel = iChannel; ev.data.control.param = BANK_SELECT_LSB; ev.data.control.value = (iBank & 0x007f); snd_seq_event_output_direct(pAlsaSeq, &ev); if (pTrackMidiManager) pTrackMidiManager->direct(&ev); if (pBusMidiManager) pBusMidiManager->direct(&ev); } // Program change... if (iProg >= 0) { ev.type = SND_SEQ_EVENT_PGMCHANGE; ev.data.control.channel = iChannel; ev.data.control.value = iProg; snd_seq_event_output_direct(pAlsaSeq, &ev); if (pTrackMidiManager) pTrackMidiManager->direct(&ev); if (pBusMidiManager) pBusMidiManager->direct(&ev); } // Bank reset to none... if (iBank < 0) { if (pTrackMidiManager) pTrackMidiManager->setCurrentBank(-1); if (pBusMidiManager) pBusMidiManager->setCurrentBank(-1); } // Program reset to none... if (iProg < 0) { if (pTrackMidiManager) pTrackMidiManager->setCurrentProg(-1); if (pBusMidiManager) pBusMidiManager->setCurrentProg(-1); } // pMidiEngine->flush(); } // Direct MIDI controller helper. void qtractorMidiBus::setController ( qtractorTrack *pTrack, int iController, int iValue ) const { setControllerEx(pTrack->midiChannel(), iController, iValue, pTrack); } void qtractorMidiBus::setController ( unsigned short iChannel, int iController, int iValue ) const { setControllerEx(iChannel, iController, iValue, nullptr); } // Direct MIDI controller common helper. void qtractorMidiBus::setControllerEx ( unsigned short iChannel, int iController, int iValue, qtractorTrack *pTrack ) const { // We always need our MIDI engine reference... qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return; // Don't do anything else if engine // has not been activated... snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiBus[%p]::setControllerEx(%d, %d, %d, %p)", this, iChannel, iController, iValue, pTrack); #endif // Initialize sequencer event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Addressing... snd_seq_ev_set_source(&ev, m_iAlsaPort); snd_seq_ev_set_subs(&ev); // The event will be direct... snd_seq_ev_set_direct(&ev); // Set controller parameters... ev.type = SND_SEQ_EVENT_CONTROLLER; ev.data.control.channel = iChannel; ev.data.control.param = iController; ev.data.control.value = iValue; snd_seq_event_output_direct(pAlsaSeq, &ev); // Do it for the MIDI plugins too... if (pTrack && (pTrack->pluginList())->midiManager()) (pTrack->pluginList())->midiManager()->direct(&ev); if (pluginList_out() && pluginList_out()->midiManager()) (pluginList_out()->midiManager())->direct(&ev); // pMidiEngine->flush(); } // Direct MIDI channel event helper. void qtractorMidiBus::sendEvent ( qtractorMidiEvent::EventType etype, unsigned short iChannel, unsigned short iParam, unsigned short iValue ) const { // We always need our MIDI engine reference... qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return; // Don't do anything else if engine // has not been activated... snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiBus[%p]::sendEvent(0x%02x, %u, %u, %u)", this, int(etype), iChannel, iParam, iValue); #endif // Initialize sequencer event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Addressing... snd_seq_ev_set_source(&ev, m_iAlsaPort); snd_seq_ev_set_subs(&ev); // The event will be direct... snd_seq_ev_set_direct(&ev); // Set controller parameters... switch (etype) { case qtractorMidiEvent::NOTEON: ev.type = SND_SEQ_EVENT_NOTEON; ev.data.note.channel = iChannel; ev.data.note.note = iParam; ev.data.note.velocity = iValue; break; case qtractorMidiEvent::NOTEOFF: ev.type = SND_SEQ_EVENT_NOTEOFF; ev.data.note.channel = iChannel; ev.data.note.note = iParam; ev.data.note.velocity = iValue; break; case qtractorMidiEvent::KEYPRESS: ev.type = SND_SEQ_EVENT_KEYPRESS; ev.data.note.channel = iChannel; ev.data.note.note = iParam; ev.data.note.velocity = iValue; break; case qtractorMidiEvent::CONTROLLER: default: ev.type = SND_SEQ_EVENT_CONTROLLER; ev.data.control.channel = iChannel; ev.data.control.param = iParam; ev.data.control.value = iValue; break; case qtractorMidiEvent::REGPARAM: ev.type = SND_SEQ_EVENT_REGPARAM; ev.data.control.channel = iChannel; ev.data.control.param = iParam; ev.data.control.value = iValue; break; case qtractorMidiEvent::NONREGPARAM: ev.type = SND_SEQ_EVENT_NONREGPARAM; ev.data.control.channel = iChannel; ev.data.control.param = iParam; ev.data.control.value = iValue; break; case qtractorMidiEvent::CONTROL14: ev.type = SND_SEQ_EVENT_CONTROL14; ev.data.control.channel = iChannel; ev.data.control.param = iParam; ev.data.control.value = iValue; break; case qtractorMidiEvent::PGMCHANGE: ev.type = SND_SEQ_EVENT_PGMCHANGE; ev.data.control.channel = iChannel; // ev.data.control.param = 0; ev.data.control.value = iParam; break; case qtractorMidiEvent::CHANPRESS: ev.type = SND_SEQ_EVENT_CHANPRESS; ev.data.control.channel = iChannel; // ev.data.control.param = 0; ev.data.control.value = iValue; break; case qtractorMidiEvent::PITCHBEND: ev.type = SND_SEQ_EVENT_PITCHBEND; ev.data.control.channel = iChannel; // ev.data.control.param = 0; ev.data.control.value = int(iValue) - 0x2000; break; } snd_seq_event_output_direct(pAlsaSeq, &ev); } // Direct MIDI note on/off helper. void qtractorMidiBus::sendNote ( qtractorTrack *pTrack, int iNote, int iVelocity, bool bForce ) const { qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; // We always need our MIDI engine reference... qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return; // Don't do anything else if engine // has not been activated... snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; const unsigned short iChannel = pTrack->midiChannel(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiBus[%p]::sendNote(%d, %d, %d, %d)", this, iChannel, iNote, iVelocity, int(bForce)); #endif // Initialize sequencer event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Addressing... snd_seq_ev_set_source(&ev, m_iAlsaPort); snd_seq_ev_set_subs(&ev); // The event will be direct... snd_seq_ev_set_direct(&ev); // Set controller parameters... ev.type = (iVelocity > 0 ? SND_SEQ_EVENT_NOTEON : SND_SEQ_EVENT_NOTEOFF); ev.data.note.channel = iChannel; ev.data.note.note = iNote; ev.data.note.velocity = iVelocity; snd_seq_event_output_direct(pAlsaSeq, &ev); // Do it for the MIDI plugins too... if ((pTrack->pluginList())->midiManager()) (pTrack->pluginList())->midiManager()->direct(&ev); if (pluginList_out() && pluginList_out()->midiManager()) (pluginList_out()->midiManager())->direct(&ev); // pMidiEngine->flush(); // Bus/track output monitoring... if (iVelocity > 0) { // Bus output monitoring... if (m_pOMidiMonitor) m_pOMidiMonitor->enqueue(qtractorMidiEvent::NOTEON, iVelocity); // Track output monitoring... qtractorMidiMonitor *pMidiMonitor = static_cast (pTrack->monitor()); if (pMidiMonitor) pMidiMonitor->enqueue(qtractorMidiEvent::NOTEON, iVelocity); } // Attempt to capture the playing note as well... if (bForce && pTrack->isRecord()) { const unsigned long tick = pSession->timep(pMidiEngine->queueTime()); snd_seq_ev_set_dest(&ev, pMidiEngine->alsaClient(), m_iAlsaPort); snd_seq_ev_schedule_tick(&ev, pMidiEngine->alsaQueue(), 0, tick); pMidiEngine->capture(&ev); } } // Direct SysEx helpers. void qtractorMidiBus::sendSysex ( unsigned char *pSysex, unsigned int iSysex ) const { // Yet again, we need our MIDI engine reference... qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return; // Don't do anything else if engine // has not been activated... snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; #ifdef CONFIG_DEBUG_0 fprintf(stderr, "qtractorMidiBus::sendSysex(%p, %u)", pSysex, iSysex); fprintf(stderr, " sysex {"); for (unsigned int i = 0; i < iSysex; ++i) fprintf(stderr, " %02x", pSysex[i]); fprintf(stderr, " }\n"); #endif // Initialize sequencer event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Addressing... snd_seq_ev_set_source(&ev, m_iAlsaPort); snd_seq_ev_set_subs(&ev); // The event will be direct... snd_seq_ev_set_direct(&ev); // Just set SYSEX stuff and send it out.. ev.type = SND_SEQ_EVENT_SYSEX; snd_seq_ev_set_sysex(&ev, iSysex, pSysex); snd_seq_event_output_direct(pAlsaSeq, &ev); // pMidiEngine->flush(); } void qtractorMidiBus::sendSysexList (void) const { // Check that we have some SysEx for setup... if (m_pSysexList == nullptr) return; if (m_pSysexList->count() < 1) return; // Yet again, we need our MIDI engine reference... qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return; // Don't do anything else if engine // has not been activated... snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; QListIterator iter(*m_pSysexList); while (iter.hasNext()) { qtractorMidiSysex *pSysex = iter.next(); #ifdef CONFIG_DEBUG_0 unsigned char *pData = pSysex->data(); unsigned short iSize = pSysex->size(); fprintf(stderr, "qtractorMidiBus::sendSysexList(%p, %u)", pData, iSize); fprintf(stderr, " sysex {"); for (unsigned short i = 0; i < iSize; ++i) fprintf(stderr, " %02x", pData[i]); fprintf(stderr, " }\n"); #endif // Initialize sequencer event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); // Addressing... snd_seq_ev_set_source(&ev, m_iAlsaPort); snd_seq_ev_set_subs(&ev); // The event will be direct... snd_seq_ev_set_direct(&ev); // Just set SYSEX stuff and send it out.. ev.type = SND_SEQ_EVENT_SYSEX; snd_seq_ev_set_sysex(&ev, pSysex->size(), pSysex->data()); snd_seq_event_output(pAlsaSeq, &ev); // AG: Do it for the MIDI plugins too... if (pluginList_out() && pluginList_out()->midiManager()) (pluginList_out()->midiManager())->direct(&ev); } pMidiEngine->flush(); } // Virtual I/O bus-monitor accessors. qtractorMonitor *qtractorMidiBus::monitor_in (void) const { return midiMonitor_in(); } qtractorMonitor *qtractorMidiBus::monitor_out (void) const { return midiMonitor_out(); } // MIDI I/O bus-monitor accessors. qtractorMidiMonitor *qtractorMidiBus::midiMonitor_in (void) const { return m_pIMidiMonitor; } qtractorMidiMonitor *qtractorMidiBus::midiMonitor_out (void) const { return m_pOMidiMonitor; } // Plugin-chain accessors. qtractorPluginList *qtractorMidiBus::pluginList_in (void) const { return m_pIPluginList; } qtractorPluginList *qtractorMidiBus::pluginList_out (void) const { return m_pOPluginList; } // Create plugin-list properly. qtractorPluginList *qtractorMidiBus::createPluginList ( int iFlags ) const { // Create plugin-list alright... qtractorPluginList *pPluginList = new qtractorPluginList(0, iFlags); // Set plugin-list title name... updatePluginListName(pPluginList, iFlags); return pPluginList; } // Update plugin-list title name... void qtractorMidiBus::updatePluginListName ( qtractorPluginList *pPluginList, int iFlags ) const { pPluginList->setName((iFlags & qtractorPluginList::In ? QObject::tr("%1 In") : QObject::tr("%1 Out")).arg(busName())); } // Update plugin-list buffers properly. void qtractorMidiBus::updatePluginList ( qtractorPluginList *pPluginList, int iFlags ) { // Sanity checks... qtractorSession *pSession = engine()->session(); if (pSession == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; // Set plugin-list title name... updatePluginListName(pPluginList, iFlags); // Get audio bus as for the plugin list... qtractorAudioBus *pAudioBus = nullptr; if (pPluginList->midiManager()) pAudioBus = (pPluginList->midiManager())->audioOutputBus(); if (pAudioBus == nullptr) { // Output bus gets to be the first available output bus... QListIterator iter(pAudioEngine->buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { pAudioBus = static_cast (pBus); break; } } } // Set plugin-list buffer alright... if (pAudioBus) pPluginList->setChannels(pAudioBus->channels(), iFlags); } // Retrieve all current ALSA connections for a given bus mode interface; // return the effective number of connection attempts... int qtractorMidiBus::updateConnects ( qtractorBus::BusMode busMode, ConnectList& connects, bool bConnect ) const { // Modes must match, at least... if ((busMode & qtractorMidiBus::busMode()) == 0) return 0; if (bConnect && connects.isEmpty()) return 0; qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return 0; snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return 0; // Which kind of subscription? snd_seq_query_subs_type_t subs_type = (busMode == qtractorBus::Input ? SND_SEQ_QUERY_SUBS_WRITE : SND_SEQ_QUERY_SUBS_READ); snd_seq_query_subscribe_t *pAlsaSubs; snd_seq_addr_t seq_addr; snd_seq_query_subscribe_alloca(&pAlsaSubs); snd_seq_client_info_t *pClientInfo; snd_seq_port_info_t *pPortInfo; snd_seq_client_info_alloca(&pClientInfo); snd_seq_port_info_alloca(&pPortInfo); ConnectItem item, *pItem; // Update current client/ports ids. unsigned int iPortFlags; if (busMode == qtractorBus::Input) iPortFlags = SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; else iPortFlags = SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; while (snd_seq_query_next_client(pAlsaSeq, pClientInfo) >= 0) { item.client = snd_seq_client_info_get_client(pClientInfo); item.clientName = QString::fromUtf8( snd_seq_client_info_get_name(pClientInfo)); snd_seq_port_info_set_client(pPortInfo, item.client); snd_seq_port_info_set_port(pPortInfo, -1); while (snd_seq_query_next_port(pAlsaSeq, pPortInfo) >= 0) { const unsigned int iPortCapability = snd_seq_port_info_get_capability(pPortInfo); if (((iPortCapability & iPortFlags) == iPortFlags) && ((iPortCapability & SND_SEQ_PORT_CAP_NO_EXPORT) == 0)) { item.port = snd_seq_port_info_get_port(pPortInfo); item.portName = QString::fromUtf8( snd_seq_port_info_get_name(pPortInfo)); pItem = connects.findItem(item); if (pItem) { pItem->port = item.port; pItem->client = item.client; } } } } // Get port connections... snd_seq_query_subscribe_set_type(pAlsaSubs, subs_type); snd_seq_query_subscribe_set_index(pAlsaSubs, 0); seq_addr.client = pMidiEngine->alsaClient(); seq_addr.port = m_iAlsaPort; snd_seq_query_subscribe_set_root(pAlsaSubs, &seq_addr); while (snd_seq_query_port_subscribers(pAlsaSeq, pAlsaSubs) >= 0) { seq_addr = *snd_seq_query_subscribe_get_addr(pAlsaSubs); snd_seq_get_any_client_info(pAlsaSeq, seq_addr.client, pClientInfo); item.client = seq_addr.client; item.clientName = QString::fromUtf8( snd_seq_client_info_get_name(pClientInfo)); snd_seq_get_any_port_info(pAlsaSeq, seq_addr.client, seq_addr.port, pPortInfo); item.port = seq_addr.port; item.portName = QString::fromUtf8( snd_seq_port_info_get_name(pPortInfo)); // Check if already in list/connected... pItem = connects.findItem(item); if (pItem && bConnect) { int iItem = connects.indexOf(pItem); if (iItem >= 0) { connects.removeAt(iItem); delete pItem; } } else if (!bConnect) connects.append(new ConnectItem(item)); // Fetch next connection... snd_seq_query_subscribe_set_index(pAlsaSubs, snd_seq_query_subscribe_get_index(pAlsaSubs) + 1); } // Shall we proceed for actual connections? if (!bConnect) return 0; snd_seq_port_subscribe_t *pPortSubs; snd_seq_port_subscribe_alloca(&pPortSubs); // For each (remaining) connection, try... int iUpdate = 0; QListIterator iter(connects); while (iter.hasNext()) { ConnectItem *pItem = iter.next(); // Don't care of non-valid client/ports... if (pItem->client < 0 || pItem->port < 0) continue; // Mangle which is output and input... if (busMode == qtractorBus::Input) { seq_addr.client = pItem->client; seq_addr.port = pItem->port; snd_seq_port_subscribe_set_sender(pPortSubs, &seq_addr); seq_addr.client = pMidiEngine->alsaClient(); seq_addr.port = m_iAlsaPort; snd_seq_port_subscribe_set_dest(pPortSubs, &seq_addr); } else { seq_addr.client = pMidiEngine->alsaClient(); seq_addr.port = m_iAlsaPort; snd_seq_port_subscribe_set_sender(pPortSubs, &seq_addr); seq_addr.client = pItem->client; seq_addr.port = pItem->port; snd_seq_port_subscribe_set_dest(pPortSubs, &seq_addr); } #ifdef CONFIG_DEBUG const QString sPortName = QString::number(m_iAlsaPort) + ':' + busName(); qDebug("qtractorMidiBus[%p]::updateConnects(%d): " "snd_seq_subscribe_port: [%d:%s] => [%d:%s]\n", this, int(busMode), pMidiEngine->alsaClient(), sPortName.toUtf8().constData(), pItem->client, pItem->portName.toUtf8().constData()); #endif if (snd_seq_subscribe_port(pAlsaSeq, pPortSubs) == 0) { const int iItem = connects.indexOf(pItem); if (iItem >= 0) { connects.removeAt(iItem); delete pItem; ++iUpdate; } } } // Remember to resend all session/tracks control stuff, // iif we've changed any of the intended MIDI connections... if (iUpdate) pMidiEngine->resetAllControllers(false); // Deferred++ // Done. return iUpdate; } // MIDI master volume. void qtractorMidiBus::setMasterVolume ( float fVolume ) { const unsigned char vol = (unsigned char) (int(127.0f * fVolume) & 0x7f); // Build Universal SysEx and let it go... unsigned char aMasterVolSysex[] = { 0xf0, 0x7f, 0x7f, 0x04, 0x01, 0x00, 0x00, 0xf7 }; // Set the course value right... if (fVolume >= +1.0f) aMasterVolSysex[5] = 0x7f; aMasterVolSysex[6] = vol; sendSysex(aMasterVolSysex, sizeof(aMasterVolSysex)); } // MIDI master panning. void qtractorMidiBus::setMasterPanning ( float fPanning ) { const unsigned char pan = (unsigned char) ((0x40 + int(63.0f * fPanning)) & 0x7f); // Build Universal SysEx and let it go... unsigned char aMasterPanSysex[] = { 0xf0, 0x7f, 0x7f, 0x04, 0x02, 0x00, 0x00, 0xf7 }; // Set the course value right... // And fine special for hard right... if (fPanning >= +1.0f) aMasterPanSysex[5] = 0x7f; if (fPanning > -1.0f) aMasterPanSysex[6] = pan; sendSysex(aMasterPanSysex, sizeof(aMasterPanSysex)); } // MIDI channel volume. void qtractorMidiBus::setVolume ( qtractorTrack *pTrack, float fVolume ) { const unsigned char vol = (unsigned char) (int(127.0f * fVolume) & 0x7f); setController(pTrack, CHANNEL_VOLUME, vol); } // MIDI channel stereo panning. void qtractorMidiBus::setPanning ( qtractorTrack *pTrack, float fPanning ) { const unsigned char pan = (unsigned char) ((0x40 + int(63.0f * fPanning)) & 0x7f); setController(pTrack, CHANNEL_PANNING, pan); } // Document element methods. bool qtractorMidiBus::loadElement ( qtractorDocument *pDocument, QDomElement *pElement ) { for (QDomNode nProp = pElement->firstChild(); !nProp.isNull(); nProp = nProp.nextSibling()) { // Convert node to element... QDomElement eProp = nProp.toElement(); if (eProp.isNull()) continue; // Load map elements (non-critical)... if (eProp.tagName() == "pass-through" || // Legacy compat. eProp.tagName() == "midi-thru" || eProp.tagName() == "monitor") { qtractorMidiBus::setMonitor( qtractorDocument::boolFromText(eProp.text())); } else if (eProp.tagName() == "midi-sysex-list") { qtractorMidiBus::loadSysexList(pDocument, &eProp); } else if (eProp.tagName() == "midi-map") { qtractorMidiBus::loadMidiMap(pDocument, &eProp); } else if (eProp.tagName() == "midi-instrument-name") { qtractorMidiBus::setInstrumentName(eProp.text()); } else if (eProp.tagName() == "input-gain") { if (qtractorMidiBus::monitor_in()) qtractorMidiBus::monitor_in()->setGain( eProp.text().toFloat()); } else if (eProp.tagName() == "input-panning") { if (qtractorMidiBus::monitor_in()) qtractorMidiBus::monitor_in()->setPanning( eProp.text().toFloat()); } else if (eProp.tagName() == "input-controllers") { qtractorMidiBus::loadControllers(&eProp, qtractorBus::Input); } else if (eProp.tagName() == "input-plugins") { if (qtractorMidiBus::pluginList_in()) qtractorMidiBus::pluginList_in()->loadElement( pDocument, &eProp); } else if (eProp.tagName() == "input-connects") { qtractorMidiBus::loadConnects( qtractorMidiBus::inputs(), pDocument, &eProp); } else if (eProp.tagName() == "output-gain") { if (qtractorMidiBus::monitor_out()) qtractorMidiBus::monitor_out()->setGain( eProp.text().toFloat()); } else if (eProp.tagName() == "output-panning") { if (qtractorMidiBus::monitor_out()) qtractorMidiBus::monitor_out()->setPanning( eProp.text().toFloat()); } else if (eProp.tagName() == "output-controllers") { qtractorMidiBus::loadControllers(&eProp, qtractorBus::Output); } else if (eProp.tagName() == "output-plugins") { if (qtractorMidiBus::pluginList_out()) qtractorMidiBus::pluginList_out()->loadElement( pDocument, &eProp); } else if (eProp.tagName() == "output-connects") { qtractorMidiBus::loadConnects( qtractorMidiBus::outputs(), pDocument, &eProp); } } return true; } bool qtractorMidiBus::saveElement ( qtractorDocument *pDocument, QDomElement *pElement ) const { const qtractorBus::BusMode busMode = qtractorMidiBus::busMode(); pElement->setAttribute("name", qtractorMidiBus::busName()); pElement->setAttribute("mode", qtractorBus::textFromBusMode(busMode)); pDocument->saveTextElement("monitor", qtractorDocument::textFromBool( qtractorMidiBus::isMonitor()), pElement); if (busMode & qtractorBus::Input) { pDocument->saveTextElement("input-gain", QString::number(qtractorMidiBus::monitor_in()->gain()), pElement); pDocument->saveTextElement("input-panning", QString::number(qtractorMidiBus::monitor_in()->panning()), pElement); // Save input bus controllers... QDomElement eInputControllers = pDocument->document()->createElement("input-controllers"); qtractorMidiBus::saveControllers(pDocument, &eInputControllers, qtractorBus::Input); pElement->appendChild(eInputControllers); // Save input bus plugins... if (qtractorMidiBus::pluginList_in()) { QDomElement eInputPlugins = pDocument->document()->createElement("input-plugins"); qtractorMidiBus::pluginList_in()->saveElement( pDocument, &eInputPlugins); pElement->appendChild(eInputPlugins); } // Save input bus connections... QDomElement eMidiInputs = pDocument->document()->createElement("input-connects"); qtractorBus::ConnectList inputs; qtractorMidiBus::updateConnects(qtractorBus::Input, inputs); qtractorMidiBus::saveConnects(inputs, pDocument, &eMidiInputs); pElement->appendChild(eMidiInputs); } if (busMode & qtractorBus::Output) { pDocument->saveTextElement("output-gain", QString::number(qtractorMidiBus::monitor_out()->gain()), pElement); pDocument->saveTextElement("output-panning", QString::number(qtractorMidiBus::monitor_out()->panning()), pElement); // Save output bus controllers... QDomElement eOutputControllers = pDocument->document()->createElement("output-controllers"); qtractorMidiBus::saveControllers(pDocument, &eOutputControllers, qtractorBus::Output); pElement->appendChild(eOutputControllers); // Save output bus plugins... if (qtractorMidiBus::pluginList_out()) { QDomElement eOutputPlugins = pDocument->document()->createElement("output-plugins"); qtractorMidiBus::pluginList_out()->saveElement( pDocument, &eOutputPlugins); pElement->appendChild(eOutputPlugins); } // Save output bus connections... QDomElement eMidiOutputs = pDocument->document()->createElement("output-connects"); qtractorBus::ConnectList outputs; qtractorMidiBus::updateConnects(qtractorBus::Output, outputs); qtractorMidiBus::saveConnects(outputs, pDocument, &eMidiOutputs); pElement->appendChild(eMidiOutputs); } // Save default instrument name, if any... if (!qtractorMidiBus::instrumentName().isEmpty()) { pDocument->saveTextElement("midi-instrument-name", qtractorMidiBus::instrumentName(), pElement); } // Create the sysex element... if (m_pSysexList && m_pSysexList->count() > 0) { QDomElement eSysexList = pDocument->document()->createElement("midi-sysex-list"); qtractorMidiBus::saveSysexList(pDocument, &eSysexList); pElement->appendChild(eSysexList); } // Create the map element... if (m_patches.count() > 0) { QDomElement eMidiMap = pDocument->document()->createElement("midi-map"); qtractorMidiBus::saveMidiMap(pDocument, &eMidiMap); pElement->appendChild(eMidiMap); } return true; } // Document instrument map methods. bool qtractorMidiBus::loadMidiMap ( qtractorDocument * /*pDocument*/, QDomElement *pElement ) { m_patches.clear(); // Load map items... for (QDomNode nChild = pElement->firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert node to element... QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; // Load map item... if (eChild.tagName() == "midi-patch") { const unsigned short iChannel = eChild.attribute("channel").toUShort(); Patch& patch = m_patches[iChannel & 0x0f]; for (QDomNode nPatch = eChild.firstChild(); !nPatch.isNull(); nPatch = nPatch.nextSibling()) { // Convert patch node to element... QDomElement ePatch = nPatch.toElement(); if (ePatch.isNull()) continue; // Add this one to map... if (ePatch.tagName() == "midi-instrument") patch.instrumentName = ePatch.text(); else if (ePatch.tagName() == "midi-bank-sel-method") patch.bankSelMethod = ePatch.text().toInt(); else if (ePatch.tagName() == "midi-bank") patch.bank = ePatch.text().toInt(); else if (ePatch.tagName() == "midi-program") patch.prog = ePatch.text().toInt(); } // Rollback if instrument-patch is invalid... if (!patch.isValid()) m_patches.remove(iChannel & 0x0f); } } return true; } bool qtractorMidiBus::saveMidiMap ( qtractorDocument *pDocument, QDomElement *pElement ) const { // Save map items... QHash::ConstIterator iter = m_patches.constBegin(); const QHash::ConstIterator& iter_end = m_patches.constEnd(); for ( ; iter != iter_end; ++iter) { const Patch& patch = iter.value(); if (!patch.isValid()) continue; QDomElement ePatch = pDocument->document()->createElement("midi-patch"); ePatch.setAttribute("channel", QString::number(iter.key())); if (!patch.instrumentName.isEmpty()) { pDocument->saveTextElement("midi-instrument", patch.instrumentName, &ePatch); } if (patch.bankSelMethod >= 0) { pDocument->saveTextElement("midi-bank-sel-method", QString::number(patch.bankSelMethod), &ePatch); } if (patch.bank >= 0) { pDocument->saveTextElement("midi-bank", QString::number(patch.bank), &ePatch); } if (patch.prog >= 0) { pDocument->saveTextElement("midi-program", QString::number(patch.prog), &ePatch); } pElement->appendChild(ePatch); } return true; } // Document SysEx setup list methods. bool qtractorMidiBus::loadSysexList ( qtractorDocument * /*pDocument*/, QDomElement *pElement ) { // Must have one... if (m_pSysexList == nullptr) return false; // Crystal clear... m_pSysexList->clear(); // Load map items... for (QDomNode nChild = pElement->firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert node to element... QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; // Load map item... if (eChild.tagName() == "midi-sysex") { qtractorMidiSysex *pSysex = new qtractorMidiSysex( eChild.attribute("name"), eChild.text()); if (pSysex->size() > 0) m_pSysexList->append(pSysex); else delete pSysex; } } return true; } bool qtractorMidiBus::saveSysexList ( qtractorDocument *pDocument, QDomElement *pElement ) const { // Must have one... if (m_pSysexList == nullptr) return false; // Save map items... QListIterator iter(*m_pSysexList); while (iter.hasNext()) { qtractorMidiSysex *pSysex = iter.next(); QDomElement eSysex = pDocument->document()->createElement("midi-sysex"); eSysex.setAttribute("name", pSysex->name()); eSysex.appendChild( pDocument->document()->createTextNode(pSysex->text())); pElement->appendChild(eSysex); } return true; } // Import SysEx setup from event sequence. bool qtractorMidiBus::importSysexList ( qtractorMidiSequence *pSeq ) { if (m_pSysexList == nullptr) return false; m_pSysexList->clear(); int iSysex = 0; qtractorMidiEvent *pEvent = pSeq->events().first(); while (pEvent) { if (pEvent->type() == qtractorMidiEvent::SYSEX) { m_pSysexList->append( new qtractorMidiSysex(pSeq->name() + '-' + QString::number(++iSysex), pEvent->sysex(), pEvent->sysex_len()) ); } pEvent = pEvent->next(); } return true; } // Export SysEx setup to event sequence. bool qtractorMidiBus::exportSysexList ( qtractorMidiSequence *pSeq ) { if (m_pSysexList == nullptr) return false; QListIterator iter(*m_pSysexList); while (iter.hasNext()) { qtractorMidiSysex *pSysex = iter.next(); qtractorMidiEvent *pEvent = new qtractorMidiEvent(0, qtractorMidiEvent::SYSEX); pEvent->setSysex(pSysex->data(), pSysex->size()); pSeq->addEvent(pEvent); } return true; } // Pending note-offs processing methods. // void qtractorMidiBus::enqueueNoteOff ( snd_seq_event_t *pEv, unsigned long iTimeOn, unsigned long iTimeOff ) { const unsigned short key = (pEv->data.note.channel << 7) | (pEv->data.note.note & 0x7f); m_noteOffs.insert(key, NoteOff(iTimeOn, iTimeOff)); } void qtractorMidiBus::dequeueNoteOffs ( unsigned long iQueueTime ) { // Whether we have anything pending... if (m_noteOffs.isEmpty()) return; // We always need our MIDI engine reference... qtractorMidiEngine *pMidiEngine = static_cast (engine()); if (pMidiEngine == nullptr) return; // Don't do anything else if engine // has not been activated... snd_seq_t *pAlsaSeq = pMidiEngine->alsaSeq(); if (pAlsaSeq == nullptr) return; NoteOffs::ConstIterator iter = m_noteOffs.constBegin(); const NoteOffs::ConstIterator& iter_end = m_noteOffs.constEnd(); for ( ; iter != iter_end; ++iter) { const unsigned short key = iter.key(); const NoteOff& value = iter.value(); if (value.time_on < iQueueTime && value.time_off >= iQueueTime) { // Determina channel and note address... const unsigned short iChannel = (key >> 7); const unsigned short iNote = (key & 0x7f); // Send NOTE_OFF event... snd_seq_event_t ev; snd_seq_ev_clear(&ev); snd_seq_ev_set_source(&ev, m_iAlsaPort); snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); ev.type = SND_SEQ_EVENT_NOTEOFF; ev.data.note.channel = iChannel; ev.data.note.note = iNote; ev.data.note.velocity = 0; snd_seq_event_output_direct(pAlsaSeq, &ev); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiBus[%p]::dequeueNoteOffs(%lu):" " channel=%u note=%u", this, iQueueTime, iChannel, iNote); #endif } } m_noteOffs.clear(); } // Update all aux-sends to this very bus... // void qtractorMidiBus::updateMidiAuxSends ( const QString& sMidiBusName ) { if ((busMode() & qtractorBus::Output) == 0) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; // Make it to all MIDI output buses... for (qtractorBus *pBus = pMidiEngine->buses().first(); pBus; pBus = pBus->next()) { qtractorMidiBus *pMidiBus = static_cast (pBus); if (pMidiBus == this) continue; if ((pMidiBus->busMode() & qtractorBus::Output) == 0) continue; qtractorPluginList *pPluginList = pMidiBus->pluginList_out(); if (pPluginList == nullptr) continue; for (qtractorPlugin *pPlugin = pPluginList->first(); pPlugin; pPlugin = pPlugin->next()) { qtractorPluginType *pType = pPlugin->type(); if (pType && pType->typeHint() == qtractorPluginType::AuxSend && pType->index() == 0) { // index == 0 => MIDI aux-send. qtractorMidiAuxSendPlugin *pMidiAuxSendPlugin = static_cast (pPlugin); if (pMidiAuxSendPlugin && pMidiAuxSendPlugin->midiBus() == this) pMidiAuxSendPlugin->setMidiBusName(sMidiBusName); } } } // Make it to MIDI tracks only... for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() != qtractorTrack::Midi) continue; qtractorPluginList *pPluginList = pTrack->pluginList(); if (pPluginList == nullptr) continue; for (qtractorPlugin *pPlugin = pPluginList->first(); pPlugin; pPlugin = pPlugin->next()) { qtractorPluginType *pType = pPlugin->type(); if (pType && pType->typeHint() != qtractorPluginType::AuxSend) continue; if (pType->index() == 0) { // index == 0 => MIDI aux-send. qtractorMidiAuxSendPlugin *pMidiAuxSendPlugin = static_cast (pPlugin); if (pMidiAuxSendPlugin && pMidiAuxSendPlugin->midiBus() == this) pMidiAuxSendPlugin->setMidiBusName(sMidiBusName); } } } } // end of qtractorMidiEngine.cpp qtractor-1.5.11/src/PaxHeaders/qtractorConnections.h0000644000000000000000000000012415124701674017526 xustar0028 mtime=1767080892.7822635 28 atime=1767080892.7822635 28 ctime=1767080892.7822635 qtractor-1.5.11/src/qtractorConnections.h0000644000175000001440000000413015124701674017513 0ustar00rncbcusers// qtractorConnections.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorConnections_h #define __qtractorConnections_h #include "qtractorEngine.h" #include // Forward declarations. class qtractorConnectForm; //------------------------------------------------------------------------- // qtractorConnections - Connections dockable window. // class qtractorConnections : public QWidget { Q_OBJECT public: // Constructor. qtractorConnections(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()); // Destructor. ~qtractorConnections(); // Connect form accessor. qtractorConnectForm *connectForm() const; // Main bus mode switching. void showBus(qtractorBus *pBus, qtractorBus::BusMode busMode); // Complete connections refreshment. void refresh(); // Complete connections recycle. void clear(); // Conditional connections recycle. void reset(); protected: // Just about to notify main-window that we're closing. void closeEvent(QCloseEvent *); // Keyboard event handler. void keyPressEvent(QKeyEvent *); private: // Instance variables. qtractorConnectForm *m_pConnectForm; }; #endif // __qtractorConnections_h // end of qtractorConnections.h qtractor-1.5.11/src/PaxHeaders/mimetypes0000644000000000000000000000013215124701674015251 xustar0030 mtime=1767080892.777263521 30 atime=1767080892.777188194 30 ctime=1767080892.777263521 qtractor-1.5.11/src/mimetypes/0000755000175000001440000000000015124701674015316 5ustar00rncbcusersqtractor-1.5.11/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-archive.svg0000644000000000000000000000013215124701674027605 xustar0030 mtime=1767080892.777245158 30 atime=1767080892.777188194 30 ctime=1767080892.777245158 qtractor-1.5.11/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-archive.svg0000644000175000001440000030167315124701674027607 0ustar00rncbcusers qtractor-1.5.11/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-template.svg0000644000000000000000000000013215124701674027777 xustar0030 mtime=1767080892.777263521 30 atime=1767080892.777263521 30 ctime=1767080892.777263521 qtractor-1.5.11/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-template.svg0000644000175000001440000004223115124701674027771 0ustar00rncbcusers qtractor-1.5.11/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-session.svg0000644000000000000000000000013215124701674027647 xustar0030 mtime=1767080892.777263521 30 atime=1767080892.777245158 30 ctime=1767080892.777263521 qtractor-1.5.11/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-session.svg0000644000175000001440000004223115124701674027641 0ustar00rncbcusers qtractor-1.5.11/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-archive.png0000644000000000000000000000013215124701674027572 xustar0030 mtime=1767080892.777188194 30 atime=1767080892.777188194 30 ctime=1767080892.777188194 qtractor-1.5.11/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-archive.png0000644000175000001440000000354515124701674027571 0ustar00rncbcusers‰PNG  IHDR szzô pHYsÃÃÇo¨dtEXtSoftwarewww.inkscape.org›î<òIDATX…¥—YlœW†Ÿsο̾xIâgmš48‰T’ Ú T­ ¢-[¥^ .BBâ *R¹AHHHÜ ±¨¢$¤ qY R«´ Y [M³/ãØ‰—Ø{<Žg<óÿgá“IŸpÎíïÞ°aïõ«W=4€Ž Ç“O~–w÷í_R›J¥(•+L•§Ée3|ù…/…Zë«×®Ý3ríÚ‡`47ÆoP½Y¥T_R&’\¹>È©3ÿ`rb ),«Wu†¯üðÇï !zœsC $“Ü€HÇôõ]EÈÖ5)›Ë±å±¶lÛN„aç3åRvÓæß|]ñ»; ÖrÕ €­Ûv ‚,étÇwí! -µžç“ÍåÉfs :!ù¶NÙ³ý3«€°I?BH¤ç³a]7µÆò¥[Ç1cýéÌ4p6b.òñ‹[Éär>Ðt…î°Ö.âŠ# Òé4ÚEK&·Ö2ráŸxfœ·Þÿ¨1G.“`[ϧÙÝÓöô›ðó;õ‹ÁÝýÁ‹’›³UœXúÔúÎ¥šãÌÙ‹!б¦<=É£Gèj³»û«|YkM„sŽöö6¦&'‰K;`jC „FGEõ†æüÙÓòÞ«¿xå[ŸZçš <ßCy>¾ïëFëäÆPc,Ú,.ŽÎ¡#Žc‘aøúÕL¶#óËepM΂L&½ôîFHG"2]*151Á\µŠ”`,c™­VQ¾ý¢BµpÎ6AŒŒÞ Ž5b‰ŒçùTk\½2ÈLå&Q½‰ 8µ`-=×ñÝo¿´³€ƘfgÐ:"ŸÏ‘Jµv¡R.Q;K±X ÿZ‰ê\!J ‚ À"‘*¤Å"•´;Z:`­áNÇH)Pž-ÑLeŠ}{•ñ±~¤§xtS…\†D"I*•$L~H6¿ð˜«Ö´(DÖÞ~Ú)5çó9&KSÈ0³07:t…éÉQÛ¹‡‡ß¥¦Û°ÙÎ÷•xù›/sáÔY´‰‘¾O¬-)ÐÖ¾ŠÒ耛¸´(€skÌB<ÿ…d"IyºŒo=œs!˜žEèƆ.áSeæ-ì}á;ô?„uçØñÄÌLߤV«a,dòDQÌ•jmøwz{[:`¬¹#Ò8çH¥RÔëuêZc­AJ… ‹ ôóÑ©ó\¸H]'u›6SšÉ±6_¢ØÑΪÄ&Š8Ò\¼Ðk>:=ð{çÜ\kmšf„€±ñ'0q´p;¤üçÀÛÔj<%™.Oјà|/$’YÎæŠlÙØÅÊžçñqÿÀìûô¾~èè¹_ßZ{Q­uÓ¬ÖšRi‚ë×»kRB[g'åJ™É—QÒC)Éxc¥$žRt?²‹§žÿƒWú¨ÍU9püØ_=÷³[»oí€Ñ·o¾„a@*¢®«ŸÜ‰'=ré$^Gå…(%QJ-´µ·S,¶SÜÕÀ¾·ß*wžo+€& ±ŽBP«Õ1vþVH —Î%›´$e/ ‚p@)E&Õúݰ$@E·´!ŸË111Ž 3£iÔ5ƒ—³iã:FûÓ)2Ùà“Rá‹›”KÃ;æ{]ä;pÑB¤Í|ó0Æ`ŒÆ:G:“AJAÔ¨c­Ey çµ3Y®!EœÌRkøÔtÀ\2‡ÄÆã½¿¿q£Ë:`´nŠo5¥0 ˆjÖ¤RìýÂKL•>ß÷‘J"Ä­Ÿ@ êž=6¹p@ÍVOŸì=†aCÊó\½VõNŸîmK§³qW׺ê±#ëB¤TBJ7_°RJ¢)ÉåKçqΉëƒý£wç»çë@Ñ ÷LüÿÃ7ÜIÿB˜vfécIEND®B`‚qtractor-1.5.11/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-template.png0000644000000000000000000000013215124701674027764 xustar0030 mtime=1767080892.777263521 30 atime=1767080892.777263521 30 ctime=1767080892.777263521 qtractor-1.5.11/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-template.png0000644000175000001440000000270315124701674027756 0ustar00rncbcusers‰PNG  IHDR szzô pHYsÃÃÇo¨dtEXtSoftwarewww.inkscape.org›î<PIDATX…½×Ol\ÅÀñï̼»Þ?^ÛÛ$‚LZÇ=‘*¨UB(­Z¤œz¨ªJ•z@BH\8¡JE•z©Z!ÑÂ…ªjoT¢"…’¨$iâ‚mˆÿ%vŒã]{wãÝõ¾?=8Nx½6ð“žôÞÓìÌg~ïíoÞç…BrƒfÎ9g7ììξïÜÛÛ»ëå7ÞxË9'6@9k­\iç¬ÕçNœøôèK/=[«Õ¦7 ðÖÜ2µo``ÿf;Xôõ tDÑcÝÝÝO‹ÅáÍü¦UZÛμ]t¤Rìܽ{Ç+¯½v&›ÍØ*à®B+Ňu¼úúëÿÚ¶sçw6j¿æÜmtvu1_.ó£Ã‡ιwwÞÿ÷¦&&N}m€T6Ëb½Nüð©§"çÜ{÷íÙsðêøø™¯L&)”ËÌ—JdR)~ðä“¡Öúý{vízüÚ•+ç¾r@˜H0:9ÉGo¾Iqvi-÷äóásÏ?\ÑúJéL†¾þ~úöï'Â(Â9G¥PH÷=úc!ÄïW¬/ý_àù>él–t&CE!Èæó²ÿᇷámí¿l€n6™>AÞoàlLÝøøÛ"•Éø€j °ÖÞUV¬µ\;ûÞÒuŽ:Gܨ“IE|sÿ·xt_ס?ïV·o9Øf¨õâòùÓtúu>ºˆÝÔ,”*œ=uŠYûèË¿}ö™ Ö˜-#LyŠééka€Ñ1Æ4‰ã˜¥†fdèYï…_?÷Ó{ÛpnKc ‘ÐciÔqÍé:Öèf“Fl˜¾:žJ÷¤~Ó`ÛÂhŽ( ) ÌÏÍQ¯V‘Œc,‹Õ*Ê·ßB¨uÎÚ-!<ß§ÚÐ(`|t’JùñRÓ4àÖ‚µ`t½ç?{úÛ­Î cÌ–åbòµ!r¹NÆ®¨ÖáPR‰T!¸)’ ;°n¬1|QDeažwþø×gÆžâÁÞtfRDQ‚d2A~H:û À£^]R°N!²öÿŸv °R"o¤R,"ÂÌä(¥¹ö=ò8gßû5×…íìgd¼À‘ŸᓆЦ‰ô}šÚ’T]ÝÛ)ÌL¸ÉÙ¹K-„5æÖµbyðˆÒì "®0;q ßTÙ³·ƒG~Îà'±7†8p€JéµZ c!•ÍÇMF«µé·ß\7f­ÁóZ"TGމñ1ΟáêèE–\‚©â+ìÞ»—B%î¨@®§›íQ/E3Ö\üdМ¿0ñç\}s€v!øàøß©-–ñ Qtractor session Qtractor template Qtractor archive qtractor-1.5.11/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-session.png0000644000000000000000000000013215124701674027634 xustar0030 mtime=1767080892.777245158 30 atime=1767080892.777245158 30 ctime=1767080892.777245158 qtractor-1.5.11/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-session.png0000644000175000001440000000272315124701674027630 0ustar00rncbcusers‰PNG  IHDR szzô pHYsÃÃÇo¨dtEXtSoftwarewww.inkscape.org›î<`IDATX…½—[l\ÅÇ3söœÝõ^¼^»$ ‰¡&Ô @$UP+¨¸´P$žúPUHH}¨D‘xᩪT„ÄH@…€¶/EˆGn—65NˆI'!±³Ží'k¯½ñžõž3Œ'Þµƒü¥ó03ß|ßo¾ÑùfF8çXMBÈUÌœsήêìrß—\×ݽ饗ÿýžsN¬嬳rÑÎY«ìýô«Ÿæ‰0 ǯÀ[Ö¡ef[ïŽíWê`©¶Þг£­-yg±X¼gjjêÈ•Ìi–ÖW¾’Ú26lܼþ•Wÿ¹/ŸÏï\+ÀUI;Å=¿¼»íµ×ÿõ¿k6l¸c5ûe[pµj/t0=3ËÃ=˜r¸O6\ý/ÆJ¥½?@&›g®VÇ÷}~óàýIçÜ7nÙ²kôÔ©}? @:¦\™eº2C.›á×Üh­w_»iÓ]gNŸ>ðƒÉC£#<ü.Sç&‘Âríº®àÉ??õ‘¢×97öƒds9z¶õÒsóv|ß'’8ç¨VÊÙî_ü­âå¥ë{ÿ ~" ›ÿ àQ¯Í+hQˆ¬½xµS ¬•H¹&¥báŠcCÌLM°í–»øü³ÿêl¶—Á“eýÝ£=8€612‘ Ö–´òé(®£a8‹§$3•iÕƒýLeÈèÙºžkÚÇð<¯†KsÒÿÚž¾#/,zn  µn2º¢£«‹Êl…©¯O ¤‡R’³*JI<¥ØxÃmÜyÿ:IX¯±{ÿ¾7÷ôùËâê[gÀè‹ <é‘kKáu¶£¼¥$J© Å"…B‘ÂmE>|ÿí2°t[˜K;Z@?ÜG6eIÉžàûÁ¥™t²™ûÖ®GøþŠyÍȉýtoÝÌÄÈI‚¶4™lnáQñíË"!ÎS)Sè\8{l“BÒ´i³pxc0F£µÆhƒ±k ÖZ”§p^‘©JˆHp2KØHjŸzPbãñÑ;ÿXžÑÕ2`´nÖ}É ©»î}„éòÏR’H$J"Äâ'¤Z¶F×ÄÝEEÑ\íЗý_AЀò<§¤)%•C.(åp ¤t ÕÑ!¥t . râø Î91:2 qtractor - An Audio/MIDI multi-track sequencer. Copyright (C) 2005-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. qtractorPaletteForm 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 Delete current color palette name Delete Palette 280 360 Current color palette true Generate: 0 0 Qt::StrongFocus Base color to generate palette Reset all current palette colors Reset Qt::Horizontal 8 20 Import a custom color theme (palette) from file Import... Export a custom color theme (palette) to file Export... Qt::Horizontal 8 20 Show Details Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorPaletteForm::ColorButton nameCombo saveButton deleteButton paletteView generateButton resetButton importButton exportButton detailsCheck dialogButtons qtractor-1.5.11/src/PaxHeaders/qtractorCurveFile.h0000644000000000000000000000013215124701674017127 xustar0030 mtime=1767080892.783263496 30 atime=1767080892.783263496 30 ctime=1767080892.783263496 qtractor-1.5.11/src/qtractorCurveFile.h0000644000175000001440000000663715124701674017133 0ustar00rncbcusers// qtractorCurveFile.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorCurveFile_h #define __qtractorCurveFile_h #include "qtractorCurve.h" // Forward declarations. class qtractorDocument; class QDomElement; //---------------------------------------------------------------------- // class qtractorCurveFile -- Automation curve file interface decl. // class qtractorCurveFile { public: // Controller types. typedef qtractorMidiEvent::EventType ControlType; // Constructor. qtractorCurveFile(qtractorCurveList *pCurveList) : m_pCurveList(pCurveList), m_iCurrentIndex(0) {} // Destructor. ~qtractorCurveFile() { clear(); } // Curve list accessor. qtractorCurveList *list() const { return m_pCurveList; } // Base directory path. void setBaseDir(const QString& sBaseDir) { m_sBaseDir = sBaseDir; } const QString& baseDir() const { return m_sBaseDir; } // Filename accessors. void setFilename(const QString& sFilename) { m_sFilename = sFilename; } const QString& filename() const { return m_sFilename; } // Current curve index accesors. void setCurrentIndex(unsigned long iCurrentIndex) { m_iCurrentIndex = iCurrentIndex; } unsigned long currentIndex() const { return m_iCurrentIndex; } // Curve item escriptor. struct Item { QString name; unsigned long index; ControlType ctype; unsigned short channel; unsigned short param; qtractorCurve::Mode mode; bool process; bool capture; bool locked; bool logarithmic; QColor color; qtractorSubject *subject; }; // Curve item list accessors. const QList& items() const { return m_items; } void addItem(Item *pCurveItem) { m_items.append(pCurveItem); } // Curve item list emptyness predicate. bool isEmpty() const { return m_items.isEmpty(); } // Curve item list cleanup. void clear() { qDeleteAll(m_items); m_items.clear(); m_iCurrentIndex = 0; } // Curve item list serialization methods. void load(QDomElement *pElement); void save(qtractorDocument *pDocument, QDomElement *pElement, qtractorTimeScale *pTimeScale) const; void apply(qtractorTimeScale *pTimeScale); // Text/curve-mode converters... static qtractorCurve::Mode modeFromText(const QString& sText); static QString textFromMode(qtractorCurve::Mode mode); private: // Instance variables. qtractorCurveList *m_pCurveList; QString m_sBaseDir; QString m_sFilename; QList m_items; unsigned long m_iCurrentIndex; }; #endif // __qtractorCurveFile_h qtractor-1.5.11/src/PaxHeaders/qtractorMidiEditSelect.cpp0000644000000000000000000000013215124701674020426 xustar0030 mtime=1767080892.791263462 30 atime=1767080892.791263462 30 ctime=1767080892.791263462 qtractor-1.5.11/src/qtractorMidiEditSelect.cpp0000644000175000001440000000767115124701674020431 0ustar00rncbcusers// qtractorMidiEditSelect.cpp // /**************************************************************************** Copyright (C) 2005-2019, 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 "qtractorMidiEditSelect.h" #include "qtractorMidiSequence.h" //------------------------------------------------------------------------- // qtractorMidiEditSelect -- MIDI event selection capsule. // Constructor. qtractorMidiEditSelect::qtractorMidiEditSelect (void) { m_pAnchorEvent = nullptr; } // Default destructor. qtractorMidiEditSelect::~qtractorMidiEditSelect (void) { clear(); } // Event selection item lookup. qtractorMidiEditSelect::Item *qtractorMidiEditSelect::findItem ( qtractorMidiEvent *pEvent ) { // Check if this very event already exists... return m_items.value(pEvent, nullptr); } // Item insertion method. void qtractorMidiEditSelect::addItem ( qtractorMidiEvent *pEvent, const QRect& rectEvent, const QRect& rectView, unsigned long iDeltaTime ) { m_items.insert(pEvent, new Item(rectEvent, rectView, iDeltaTime)); m_rectEvent = m_rectEvent.united(rectEvent); m_rectView = m_rectView.united(rectView); } // Item selection method. void qtractorMidiEditSelect::selectItem ( qtractorMidiEvent *pEvent, const QRect& rectEvent, const QRect& rectView, bool bSelect, bool bToggle ) { Item *pItem = findItem(pEvent); if (pItem) { const unsigned int flags = pItem->flags; if ( (!bSelect && (flags & 2) == 0) || (( bSelect && (flags & 3) == 3) && bToggle)) pItem->flags &= ~1; else if ( ( bSelect && (flags & 2) == 0) || ((!bSelect && (flags & 3) == 2) && bToggle)) pItem->flags |= 1; } else if (bSelect) addItem(pEvent, rectEvent, rectView); } // Selection commit method. void qtractorMidiEditSelect::update ( bool bCommit ) { // Remove unselected... int iUpdate = 0; m_pAnchorEvent = nullptr; ItemList::Iterator iter = m_items.begin(); const ItemList::Iterator& iter_end = m_items.end(); while (iter != iter_end) { Item *pItem = iter.value(); if (bCommit) { if (pItem->flags & 1) pItem->flags |= 2; else pItem->flags &= ~2; } if (pItem->flags & 1) { qtractorMidiEvent *pEvent = iter.key(); if (m_pAnchorEvent == nullptr || m_pAnchorEvent->time() > pEvent->time()) m_pAnchorEvent = pEvent; } if ((pItem->flags & 3) == 0) { delete pItem; iter = m_items.erase(iter); ++iUpdate; } else ++iter; } // Did we remove any? if (iUpdate > 0) commit(); } // Selection commit method. void qtractorMidiEditSelect::commit (void) { // Reset united selection rectangle... m_rectEvent.setRect(0, 0, 0, 0); m_rectView.setRect(0, 0, 0, 0); ItemList::ConstIterator iter = m_items.constBegin(); const ItemList::ConstIterator iter_end = m_items.constEnd(); for ( ; iter != iter_end; ++iter) { Item *pItem = iter.value(); if (pItem->flags & 1) { m_rectEvent = m_rectEvent.united(pItem->rectEvent); m_rectView = m_rectView.united(pItem->rectView); } } } // Reset event selection. void qtractorMidiEditSelect::clear (void) { m_rectEvent.setRect(0, 0, 0, 0); m_rectView.setRect(0, 0, 0, 0); qDeleteAll(m_items); m_items.clear(); m_pAnchorEvent = nullptr; } // end of qtractorMidiEditSelect.cpp qtractor-1.5.11/src/PaxHeaders/qtractorClipSelect.h0000644000000000000000000000012415124701674017273 xustar0028 mtime=1767080892.7822635 28 atime=1767080892.7822635 28 ctime=1767080892.7822635 qtractor-1.5.11/src/qtractorClipSelect.h0000644000175000001440000000534315124701674017267 0ustar00rncbcusers// qtractorClipSelect.h // /**************************************************************************** Copyright (C) 2005-2019, 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 __qtractorClipSelect_h #define __qtractorClipSelect_h #include "qtractorClip.h" #include "qtractorRubberBand.h" #include #include //------------------------------------------------------------------------- // qtractorClipSelect -- Track clip selection capsule. class qtractorClipSelect { public: // Constructor. qtractorClipSelect(); // Default constructor. ~qtractorClipSelect(); // Selection item struct. struct Item { // Item constructor. Item(const QRect& clipRect, unsigned long iClipOffset) : rect(clipRect), offset(iClipOffset), rubberBand(nullptr) {} // Item destructor. ~Item() { if (rubberBand) { rubberBand->hide(); delete rubberBand; } } // Item members. QRect rect; unsigned long offset; qtractorRubberBand *rubberBand; }; // Selection list definition. typedef QMultiHash ItemList; // Clip selection method. void selectItem( qtractorClip *pClip, const QRect& rect, bool bSelect = true); // Clip addition (no actual selection). void addItem( qtractorClip *pClip, const QRect& rect, unsigned long offset); // The united selection rectangle. const QRect& rect() const; // Dynamic helper: // Do all selected clips belong to the same track? qtractorTrack *singleTrack(); // Selection list accessor. const ItemList& items() const; // Clip selection item lookup. Item *findItem(qtractorClip *pClip) const; // Reset clip selection. void reset(); // Clear clip selection. void clear(); private: // The clip selection list. ItemList m_items; // The united selection rectangle. QRect m_rect; // To cache single track selection. qtractorTrack *m_pTrackSingle; bool m_bTrackSingle; }; #endif // __qtractorClipSelect_h // end of qtractorClipSelect.h qtractor-1.5.11/src/PaxHeaders/qtractorExportForm.ui0000644000000000000000000000013215124701674017536 xustar0030 mtime=1767080892.784263491 30 atime=1767080892.784263491 30 ctime=1767080892.784263491 qtractor-1.5.11/src/qtractorExportForm.ui0000644000175000001440000004331015124701674017527 0ustar00rncbcusers rncbc aka Rui Nuno Capela qtractor - An Audio/MIDI multi-track sequencer. Copyright (C) 2005-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. qtractorExportForm 0 0 440 240 Qt::StrongFocus Export :/images/qtractor.svg &File: ExportPathComboBox 4 0 320 22 Export file name true 22 22 24 24 Qt::TabFocus Browse export file name 4 4 50 false Qt::AlignRight|Qt::AlignVCenter File &type: AudioExportTypeComboBox 50 false Audio file type to use on export 50 false Qt::AlignRight|Qt::AlignVCenter Sample &format: AudioExportFormatComboBox 50 false Audio sample format to use on export 50 false Qt::AlignRight|Qt::AlignVCenter &Quality: AudioExportQualitySpinBox 60 32767 50 false Audio compression quality to use on export 0 10 1 4 Qt::Horizontal QSizePolicy::Fixed 22 22 4 4 50 false Qt::AlignRight|Qt::AlignVCenter File &format: MidiExportFormatComboBox 50 false MIDI file format to use on export Qt::Horizontal QSizePolicy::Fixed 22 22 Range 8 8 8 8 4 Session range &Session false Loop range &Loop false Punch range &Punch false Edit range &Edit false Custom range &Custom true Qt::Horizontal QSizePolicy::Fixed 16 16 St&art: ExportStartSpinBox 120 0 Custom start En&d: ExportEndSpinBox 120 0 Custom end Qt::Horizontal 20 20 160 120 Outputs 4 4 Output bus names QAbstractItemView::ExtendedSelection Format Time display format Frames Time BBT 4 8 Whether to add/import new track(s) with export result &Add new track(s) Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorTimeSpinBox QSpinBox
qtractorSpinBox.h
qtractorAudioFileFormatComboBox QComboBox
qtractorComboBox.h
qtractorAudioFileTypeComboBox QComboBox
qtractorComboBox.h
qtractorMidiFileFormatComboBox QComboBox
qtractorComboBox.h
ExportPathComboBox ExportPathToolButton AudioExportTypeComboBox AudioExportFormatComboBox AudioExportQualitySpinBox MidiExportFormatComboBox SessionRangeRadioButton LoopRangeRadioButton PunchRangeRadioButton EditRangeRadioButton CustomRangeRadioButton ExportStartSpinBox ExportEndSpinBox ExportBusNameListBox FormatComboBox AddTrackCheckBox
qtractor-1.5.11/src/PaxHeaders/vestige0000644000000000000000000000013215124701674014703 xustar0030 mtime=1767080892.816263357 30 atime=1767080892.816263357 30 ctime=1767080892.816263357 qtractor-1.5.11/src/vestige/0000755000175000001440000000000015124701674014750 5ustar00rncbcusersqtractor-1.5.11/src/vestige/PaxHeaders/vestige.h0000644000000000000000000000013215124701674016577 xustar0030 mtime=1767080892.816263357 30 atime=1767080892.816263357 30 ctime=1767080892.816263357 qtractor-1.5.11/src/vestige/vestige.h0000644000175000001440000002340515124701674016573 0ustar00rncbcusers/* * aeffectx.h - simple header to allow VeSTige compilation and eventually work * * Copyright (c) 2006 Javier Serrano Polo * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * 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 (see COPYING); if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include #ifndef _AEFFECTX_H #define _AEFFECTX_H #define CCONST(a, b, c, d)( ( ( (int) a ) << 24 ) | \ ( ( (int) b ) << 16 ) | \ ( ( (int) c ) << 8 ) | \ ( ( (int) d ) << 0 ) ) #define audioMasterAutomate 0 #define audioMasterVersion 1 #define audioMasterCurrentId 2 #define audioMasterIdle 3 #define audioMasterPinConnected 4 // unsupported? 5 #define audioMasterWantMidi 6 #define audioMasterGetTime 7 #define audioMasterProcessEvents 8 #define audioMasterSetTime 9 #define audioMasterTempoAt 10 #define audioMasterGetNumAutomatableParameters 11 #define audioMasterGetParameterQuantization 12 #define audioMasterIOChanged 13 #define audioMasterNeedIdle 14 #define audioMasterSizeWindow 15 #define audioMasterGetSampleRate 16 #define audioMasterGetBlockSize 17 #define audioMasterGetInputLatency 18 #define audioMasterGetOutputLatency 19 #define audioMasterGetPreviousPlug 20 #define audioMasterGetNextPlug 21 #define audioMasterWillReplaceOrAccumulate 22 #define audioMasterGetCurrentProcessLevel 23 #define audioMasterGetAutomationState 24 #define audioMasterOfflineStart 25 #define audioMasterOfflineRead 26 #define audioMasterOfflineWrite 27 #define audioMasterOfflineGetCurrentPass 28 #define audioMasterOfflineGetCurrentMetaPass 29 #define audioMasterSetOutputSampleRate 30 // unsupported? 31 #define audioMasterGetSpeakerArrangement 31 // deprecated in 2.4? #define audioMasterGetVendorString 32 #define audioMasterGetProductString 33 #define audioMasterGetVendorVersion 34 #define audioMasterVendorSpecific 35 #define audioMasterSetIcon 36 #define audioMasterCanDo 37 #define audioMasterGetLanguage 38 #define audioMasterOpenWindow 39 #define audioMasterCloseWindow 40 #define audioMasterGetDirectory 41 #define audioMasterUpdateDisplay 42 #define audioMasterBeginEdit 43 //BeginGesture #define audioMasterEndEdit 44 //EndGesture #define audioMasterOpenFileSelector 45 #define audioMasterCloseFileSelector 46 // currently unused #define audioMasterEditFile 47 // currently unused #define audioMasterGetChunkFile 48 // currently unused #define audioMasterGetInputSpeakerArrangement 49 // currently unused #define effFlagsHasEditor 1 #define effFlagsCanReplacing (1 << 4) // very likely #define effFlagsIsSynth (1 << 8) // currently unused #define effOpen 0 #define effClose 1 // currently unused #define effSetProgram 2 // currently unused #define effGetProgram 3 // currently unused #define effGetProgramName 5 // currently unused #define effGetParamName 8 // currently unused #define effSetSampleRate 10 #define effSetBlockSize 11 #define effMainsChanged 12 #define effEditGetRect 13 #define effEditOpen 14 #define effEditClose 15 #define effEditIdle 19 #define effEditTop 20 #define effProcessEvents 25 // the next one from http://asseca.com/vst-24-specs/index.html #define effGetPlugCategory 35 #define effGetEffectName 45 #define effGetVendorString 47 #define effGetProductString 48 #define effGetVendorVersion 49 #define effCanDo 51 // currently unused /* from http://asseca.com/vst-24-specs/efIdle.html */ #define effIdle 53 /* from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ #define effGetParameterProperties 56 #define effGetVstVersion 58 // currently unused /* http://asseca.com/vst-24-specs/efShellGetNextPlugin.html */ #define effShellGetNextPlugin 70 /* The next two were gleaned from http://www.kvraudio.com/forum/printview.php?t=143587&start=0 */ #define effStartProcess 71 #define effStopProcess 72 #define effBeginSetProgram 67 #define effEndSetProgram 68 #ifdef WORDS_BIGENDIAN // "VstP" #define kEffectMagic 0x50747356 #else // "PtsV" #define kEffectMagic 0x56737450 #endif #define kVstLangEnglish 1 #define kVstMidiType 1 struct RemoteVstPlugin; #define kVstTransportChanged 1 #define kVstTransportPlaying (1 << 1) #define kVstTransportCycleActive (1 << 2) #define kVstTransportRecording (1 << 3) #define kVstAutomationWriting (1 << 6) #define kVstAutomationReading (1 << 7) #define kVstNanosValid (1 << 8) #define kVstPpqPosValid (1 << 9) #define kVstTempoValid (1 << 10) #define kVstBarsValid (1 << 11) #define kVstCyclePosValid (1 << 12) #define kVstTimeSigValid (1 << 13) #define kVstSmpteValid (1 << 14) #define kVstClockValid (1 << 15) struct _VstMidiEvent { // 00 int type; // 04 int byteSize; // 08 int deltaFrames; // 0c? int flags; // 10? int noteLength; // 14? int noteOffset; // 18 char midiData[4]; // 1c? char detune; // 1d? char noteOffVelocity; // 1e? char reserved1; // 1f? char reserved2; }; typedef struct _VstMidiEvent VstMidiEvent; struct _VstEvent { char dump[sizeof (VstMidiEvent)]; }; typedef struct _VstEvent VstEvent; struct _VstEvents { // 00 int numEvents; // 04 void *reserved; // 08 VstEvent * events[]; }; /* constants from http://www.rawmaterialsoftware.com/juceforum/viewtopic.php?t=3740&sid=183f74631fee71a493316735e2b9f28b */ enum Vestige2StringConstants { VestigeMaxNameLen = 64, VestigeMaxLabelLen = 128, VestigeMaxShortLabelLen = 8, VestigeMaxCategLabelLen = 24, VestigeMaxFileNameLen = 100 }; /* constants from http://asseca.com/vst-24-specs/efGetPlugCategory.html */ enum VstPlugCategory { kPlugCategUnknown = 0, kPlugCategEffect, kPlugCategSynth, kPlugCategAnalysis, kPlugCategMastering, kPlugCategSpacializer, kPlugCategRoomFx, kPlugSurroundFx, kPlugCategRestoration, kPlugCategOfflineProcess, kPlugCategShell, kPlugCategGenerator, kPlugCategMaxCount }; typedef struct _VstEvents VstEvents; /* this struct taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ struct _VstParameterProperties { float stepFloat; /* float step */ float smallStepFloat; /* small float step */ float largeStepFloat; /* large float step */ char label[64]; /* parameter label */ int32_t flags; /* @see VstParameterFlags */ int32_t minInteger; /* integer minimum */ int32_t maxInteger; /* integer maximum */ int32_t stepInteger; /* integer step */ int32_t largeStepInteger; /* large integer step */ char shortLabel[VestigeMaxShortLabelLen]; /* short label, recommended: 6 + delimiter */ int16_t displayIndex; /* index where this parameter should be displayed (starting with 0) */ int16_t category; /* 0: no category, else group index + 1 */ int16_t numParametersInCategory; /* number of parameters in category */ int16_t reserved; /* zero */ char categoryLabel[VestigeMaxCategLabelLen]; /* category label, e.g. "Osc 1" */ char future[16]; /* reserved for future use */ }; typedef struct _VstParameterProperties VstParameterProperties; /* this enum taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ enum VstParameterFlags { kVstParameterIsSwitch = 1 << 0, /* parameter is a switch (on/off) */ kVstParameterUsesIntegerMinMax = 1 << 1, /* minInteger, maxInteger valid */ kVstParameterUsesFloatStep = 1 << 2, /* stepFloat, smallStepFloat, largeStepFloat valid */ kVstParameterUsesIntStep = 1 << 3, /* stepInteger, largeStepInteger valid */ kVstParameterSupportsDisplayIndex = 1 << 4, /* displayIndex valid */ kVstParameterSupportsDisplayCategory = 1 << 5, /* category, etc. valid */ kVstParameterCanRamp = 1 << 6 /* set if parameter value can ramp up/down */ }; struct _AEffect { // Never use virtual functions!!! // 00-03 int magic; // dispatcher 04-07 intptr_t (* dispatcher) (struct _AEffect *, int, int, intptr_t, void *, float); // process, quite sure 08-0b void (* process) (struct _AEffect *, float **, float **, int); // setParameter 0c-0f void (* setParameter) (struct _AEffect *, int, float); // getParameter 10-13 float (* getParameter) (struct _AEffect *, int); // programs 14-17 int numPrograms; // Params 18-1b int numParams; // Input 1c-1f int numInputs; // Output 20-23 int numOutputs; // flags 24-27 int flags; // Fill somewhere 28-2b void *ptr1; void *ptr2; // Zeroes 2c-2f 30-33 34-37 38-3b char empty3[4 + 4 + 4]; // 1.0f 3c-3f float unkown_float; // An object? pointer 40-43 void *ptr3; // Zeroes 44-47 void *user; // Id 48-4b int32_t uniqueID; // Don't know 4c-4f char unknown1[4]; // processReplacing 50-53 void (* processReplacing) (struct _AEffect *, float **, float **, int); }; typedef struct _AEffect AEffect; typedef struct _VstTimeInfo { /* info from online documentation of VST provided by Steinberg */ double samplePos; double sampleRate; double nanoSeconds; double ppqPos; double tempo; double barStartPos; double cycleStartPos; double cycleEndPos; int32_t timeSigNumerator; int32_t timeSigDenominator; int32_t smpteOffset; int32_t smpteFrameRate; int32_t samplesToNextClock; int32_t flags; } VstTimeInfo; typedef intptr_t (* audioMasterCallback) (AEffect *, int32_t, int32_t, intptr_t, void *, float); #endif qtractor-1.5.11/src/PaxHeaders/qtractorTrackView.h0000644000000000000000000000013215124701674017142 xustar0030 mtime=1767080892.803263411 30 atime=1767080892.803263411 30 ctime=1767080892.803263411 qtractor-1.5.11/src/qtractorTrackView.h0000644000175000001440000004312215124701674017134 0ustar00rncbcusers// qtractorTrackView.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorTrackView_h #define __qtractorTrackView_h #include "qtractorScrollView.h" #include "qtractorRubberBand.h" #include "qtractorTrack.h" #include "qtractorCurve.h" #include #include // Forward declarations. class qtractorTracks; class qtractorClipSelect; class qtractorCurveSelect; class qtractorMidiSequence; class qtractorSessionCursor; class qtractorTrackListItem; class qtractorCurveEditCommand; class QToolButton; class QDoubleSpinBox; class QDragEnterEvent; class QDragLeaveEvent; class QDragMoveEvent; class QDropEvent; class QMouseEvent; class QResizeEvent; class QKeyEvent; class QMimeData; class QCursor; //---------------------------------------------------------------------------- // qtractorTrackView -- Track view widget. class qtractorTrackView : public qtractorScrollView { Q_OBJECT public: // Constructor. qtractorTrackView(qtractorTracks *pTracks, QWidget *pParent = nullptr); // Destructor. ~qtractorTrackView(); // Track view state reset. void clear(); // Update track view content height. void updateContentsHeight(); // Update track view content width. void updateContentsWidth(int iContentsWidth = 0); // Contents update overloaded methods. void updateContents(const QRect& rect); void updateContents(); // Special recording visual feedback. void updateContentsRecord(); // The current clip selection mode. enum SelectMode { SelectClip = 0, SelectRange, SelectRect }; enum SelectEdit { EditNone = 0, EditHead, EditTail, EditBoth }; // Selection flags enum { SelectNone = 0, SelectClear = 1, SelectToggle = 2, SelectCommit = 4 }; // Convert selection flags from keyboard modifiers. static int selectFlags(const Qt::KeyboardModifiers modifiers); // Clip selection mode accessors. void setSelectMode(SelectMode selectMode); SelectMode selectMode() const; // Select every clip under a given (rubber-band) rectangle. void selectClipRect(const QRect& rectDrag, SelectMode selectMode, int flags, SelectEdit selectEdit = EditNone); // Select every clip of a given track-range. void selectClipTrackRange(qtractorTrack *pTrackPtr, bool bReset = true); // Select every clip of a given track. void selectClipTrack(qtractorTrack *pTrackPtr, bool bReset = true); // Select all clip contents. void selectClipAll(); // Invert selection on all tracks and clips. void selectClipInvert(); // Select all clips of given filename and track/channel. void selectClipFile(qtractorTrack::TrackType trackType, const QString& sFilename, int iTrackChannel, bool bSelect); // Select curve nodes under a given (rubber-band) rectangle. void selectCurveRect(const QRect& rectDrag, SelectMode selectMode, int flags, SelectEdit selectEdit = EditNone); // Select every current curve/automation node of a given track-range. void selectCurveTrackRange(qtractorTrack *pTrackPtr, bool bReset = true); // Select every current curve/automation node of a given track. void selectCurveTrack(qtractorTrack *pTrackPtr, bool bReset = true); // Select all current track curve/automation nodes. void selectCurveAll(); // Invert selection on current track curve/automation nodes. void selectCurveInvert(); // Contents update overloaded methods. void updateRect(const QRect& rect); // Whether there's any clip currently editable. void setCurrentClip(qtractorClip *pClip); qtractorClip *currentClip() const; // Clip selection accessor. qtractorClipSelect *clipSelect() const; // Whether there's any clip currently selected. bool isClipSelected() const; // Whether there's any curve/automation currently selected. bool isCurveSelected() const; // Whether there's a single track selection. qtractorTrack *singleTrackSelected(); // Clear current selection (no notify). void clearSelect(bool bReset = false); // Update current selection (no notify). void updateSelect(); // Whether there's anything on clipboard. static bool isClipboard(); // Whether there's a single track on clipboard. static qtractorTrack *singleTrackClipboard(); // Clear current clipboard (no notify). static void clearClipboard(); // Paste from clipboard (start). void pasteClipboard( unsigned short iPasteCount = 1, unsigned long iPastePeriod = 0); // Retrieve current paste period. // (as from current clipboard width) unsigned long pastePeriod() const; // Clip selection command types. enum Command { Cut, Copy, Delete, Split }; // Clip selection executive method. void executeClipSelect( qtractorTrackView::Command cmd, qtractorClip *pClip = nullptr); // Intra-drag-n-drop clip move method. void moveClipSelect(qtractorTrack *pTrack); // Paste from clipboard (execute). void pasteClipSelect(qtractorTrack *pTrack, bool bUnlink = false); // Curve/automation selection executive method. void executeCurveSelect(qtractorTrackView::Command cmd); // Intra-drag-n-drop curve/automation node move method. void moveCurveSelect(const QPoint& pos); // Paste from clipboard (execute). void pasteCurveSelect(const QPoint& pos); // Play-head positioning. void setPlayHead(unsigned long iPlayHead, bool bSyncView = false); int playHeadX() const; // Auto-backwatrd interim play-head positioning. void setPlayHeadAutoBackward(unsigned long iPlayHead); int playHeadAutoBackwardX() const; // Edit-head/tail positioning. void setEditHead(unsigned long iEditHead); int editHeadX() const; void setEditTail(unsigned long iEditTail); int editTailX() const; // Make given contents position visible in view. void ensureVisible(int cx, int cy, int mx, int my); // Make given frame position visible in view. void ensureVisibleFrame(unsigned long iFrame); // Current session cursor accessor. qtractorSessionCursor *sessionCursor() const; // Clip cloner helper. static qtractorClip *cloneClip(qtractorClip *pClip, bool bUnlink = false); // Multi-item drop mode (whether to span clips horixontally). void setDropSpan(bool bDropSpan); bool isDropSpan() const; // Snap-to-bar zebra mode. void setSnapZebra(bool bSnapZebra); bool isSnapZebra() const; // Snap-to-beat grid mode. void setSnapGrid(bool bSnapGrid); bool isSnapGrid() const; // Floating tool-tips mode. void setToolTips(bool bToolTips); bool isToolTips() const; // Automation curve node editing mode. void setCurveEdit(bool bCurveEdit); bool isCurveEdit() const; // Temporary sync-view/follow-playhead hold state. void setSyncViewHoldOn(bool bOn); void setSyncViewHold(bool bSyncViewHold); bool isSyncViewHold() const; // Return either the snapped pixel/frame, // or the passed one if [Alt] key is pressed. unsigned int pixelSnap(unsigned int x) const; unsigned long frameSnap(unsigned long iFrame) const; // (Un)set edit mode cursors. void setEditCursor(const QCursor& cursr); void unsetEditCursor(); protected: // Resize event handler. void resizeEvent(QResizeEvent *pResizeEvent); // Draw the track view void drawContents(QPainter *pPainter, const QRect& rect); // Track view state info. struct TrackViewInfo { int trackIndex; // Track index. unsigned long trackStart; // First track frame on view. unsigned long trackEnd; // Last track frame on view. QRect trackRect; // The track view rectangle. }; // Get track from given contents vertical position. qtractorTrack *trackAt(const QPoint& pos, bool bSelectTrack = false, TrackViewInfo *pTrackViewInfo = nullptr) const; // Get clip from given contents position. qtractorClip *clipAt(const QPoint& pos, bool bSelectTrack = false, QRect *pClipRect = nullptr) const; // Get clip from given contents position. qtractorClip *clipAtTrack(const QPoint& pos, QRect *pClipRect, qtractorTrack *pTrack, TrackViewInfo *pTrackViewInfo) const; // Get automation curve node from given contents position. qtractorCurve::Node *nodeAtTrack(const QPoint& pos, qtractorTrack *pTrack, TrackViewInfo *pTrackViewInfo) const; qtractorCurve::Node *nodeAt(const QPoint& pos) const; // Get contents visible rectangle from given track. bool trackInfo(qtractorTrack *pTrack, TrackViewInfo *pTrackViewInfo) const; // Get contents rectangle from given clip. bool clipInfo(qtractorClip *pClip, QRect *pClipRect, TrackViewInfo *pTrackViewInfo) const; // Drag-n-drop event stuffers (for clips). qtractorTrack *dragClipMove(const QPoint& pos, bool bKeyStep = false); qtractorTrack *dragClipDrop(const QPoint& pos, bool bKeyStep = false, const QMimeData *pMimeData = nullptr); qtractorTrack *dragClipDropEvent(QDropEvent *pDropEvent); bool canClipDropEvent(QDropEvent *pDropEvent); // Drag-n-drop event stuffers (for curve/automation nodes). void dragCurveMove(const QPoint& pos, bool bKeyStep = false); // Drag-n-drop event handlers. void dragEnterEvent(QDragEnterEvent *pDragEnterEvent); void dragMoveEvent(QDragMoveEvent *pDragMoveEvent); void dragLeaveEvent(QDragLeaveEvent *pDragLeaveEvent); void dropEvent(QDropEvent *pDropEvent); bool dropClip(const QPoint& pos, const QMimeData *pMimeData = nullptr); // Handle item selection with mouse. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseMoveEvent(QMouseEvent *pMouseEvent); void mouseReleaseEvent(QMouseEvent *pMouseEvent); // Handle item/clip editing from mouse. void mouseDoubleClickEvent(QMouseEvent *pMouseEvent); // Handle zoom with mouse wheel. void wheelEvent(QWheelEvent *pWheelEvent); // Focus lost event. void focusOutEvent(QFocusEvent *pFocusEvent); // Trap for help/tool-tip events. bool eventFilter(QObject *pObject, QEvent *pEvent); // Clip file(item) selection convenience method. void selectClip(bool bReset); // Clip selection sanity check method. bool queryClipSelect(qtractorClip *pClip = nullptr); // Update whole clip selection. void updateClipSelect(); // Draw/hide the whole current clip selection. void showClipSelect() const; void hideClipSelect() const; // Update whole automation/curve selection. void updateCurveSelect(); // Show selection tooltip... void showToolTip(const QRect& rect, int dx) const; // Draw/hide the whole drop rectagle list void updateClipDropRects(int y, int h) const; void showClipDropRects() const; void hideClipDropRects() const; // Draw/hide a dragging rectangular selection. void moveRubberBand(qtractorRubberBand **ppRubberBand, const QRect& rectDrag, int thick = 1) const; // Check whether we're up to drag something on a track or one of its clips. qtractorClip *dragClipStart(const QPoint& pos, const Qt::KeyboardModifiers& modifiers, bool bSelectTrack = false, QRect *pClipRect = nullptr); // Check whether we're up to drag a clip fade-in/out or resize handles. bool dragClipStartEx(const QPoint& pos, const Qt::KeyboardModifiers& modifiers, qtractorClip *pClip, const QRect& rectClip); // Clip fade-in/out handle drag-move methods. void dragClipFadeMove(const QPoint& pos); void dragClipFadeDrop(const QPoint& pos); // Clip select drag-move and settler method. void dragClipSelectMove(const QPoint& pos); // Clip resize drag-move methods. void dragClipResizeMove(const QPoint& pos); void dragClipResizeDrop(const QPoint& pos, bool bTimeStretch = false); // Clip resize/repeat handler (over to the left/right). void dragClipRepeatLeft(const QPoint& pos); void dragClipRepeatRight(const QPoint& pos); // Automation curve node drag-move methods. void dragCurveNode(const QPoint& pos, const Qt::KeyboardModifiers& modifiers); // Common tool-tip builder for automation nodes. QString nodeToolTip(qtractorCurve *pCurve, qtractorCurve::Node *pNode) const; // Reset drag/select/move state. void resetDragState(); // Keyboard event handler. void keyPressEvent(QKeyEvent *pKeyEvent); // Keyboard step handler. bool keyStep(int iKey, const Qt::KeyboardModifiers& modifiers); // Vertical line positioning. void drawPositionX(int& iPositionX, int x, bool bSyncView = false); // Automation/curve node editor methods. void openEditCurveNode(qtractorCurve *pCurve, qtractorCurve::Node *pNode); void closeEditCurveNode(); protected slots: // To have track view in v-sync with track list. void contentsYMovingSlot(int cx, int cy); // (Re)create the complete track view pixmap. void updatePixmap(int cx, int cy); // Drag-reset timer slot. void dragTimeout(); // Automatio/curve node editor slots. void editCurveNodeChanged(); void editCurveNodeFinished(); private: // The logical parent binding. qtractorTracks *m_pTracks; // Local zoom control widgets. QToolButton *m_pHzoomIn; QToolButton *m_pHzoomOut; QToolButton *m_pVzoomIn; QToolButton *m_pVzoomOut; QToolButton *m_pXzoomReset; // Local double-buffering pixmap. QPixmap m_pixmap; // To maintain the current track/clip positioning. qtractorSessionCursor *m_pSessionCursor; // Drag-n-dropping stuff. struct DropItem { // Item constructor. DropItem(const QString& sPath, int iChannel = -1) : path(sPath), channel(iChannel), rubberBand(nullptr) {} // Item destructor. ~DropItem() { if (rubberBand) { rubberBand->hide(); delete rubberBand; } } // Item members. QString path; int channel; QRect rect; qtractorRubberBand *rubberBand; }; QList m_dropItems; qtractorTrack::TrackType m_dropType; // The current selecting/dragging clip stuff. enum DragState { DragNone = 0, DragStart, DragSelect, DragClipMove, DragClipStep, DragClipDrop, DragClipPaste, DragClipPasteDrop, DragClipFadeIn, DragClipFadeOut, DragClipSelectLeft, DragClipSelectRight, DragClipResizeLeft, DragClipResizeRight, DragClipRepeatLeft, DragClipRepeatRight, DragCurveMove, DragCurveStep, DragCurvePaste, DragCurveNode } m_dragState, m_dragCursor; qtractorClip *m_pClipDrag; QPoint m_posDrag; QRect m_rectDrag; QRect m_rectHandle; int m_iDragClipX; bool m_bDragTimer; QPoint m_posStep; qtractorRubberBand *m_pRubberBand; qtractorClipSelect *m_pClipSelect; qtractorCurveSelect *m_pCurveSelect; // The clip select mode. SelectMode m_selectMode; // The multi-item drop mode. bool m_bDropSpan; // Snap-to-beat/bar grid/zebra view mode. bool m_bSnapZebra; bool m_bSnapGrid; // Floating tool-tips mode. bool m_bToolTips; // Automation curve node editing mode. bool m_bCurveEdit; // The local clipboard items. struct ClipItem { // Clipboard item constructor. ClipItem(qtractorClip *pClip, const QRect& clipRect, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength) : clip(pClip), rect(clipRect), clipStart(iClipStart), clipOffset(iClipOffset), clipLength(iClipLength), fadeInLength(0), fadeOutLength(0) {} // Clipboard item members. qtractorClip *clip; QRect rect; unsigned long clipStart; unsigned long clipOffset; unsigned long clipLength; unsigned long fadeInLength; unsigned long fadeOutLength; }; struct NodeItem { // Clipboard item constructor. NodeItem(qtractorCurve::Node *pNode, const QRect& nodeRect, unsigned long iFrame, float fValue) : node(pNode), rect(nodeRect), frame(iFrame), value(fValue) {} // Clipboard item members. qtractorCurve::Node *node; QRect rect; unsigned long frame; float value; }; // The local clipboard stuff (singleton). static struct ClipBoard { // Clipboard constructor. ClipBoard() : singleTrack(nullptr), frames(0) {} // Destructor. ~ClipBoard() { clear(); } // Clipboard stuffer methods. void addClip(qtractorClip *pClip, const QRect& clipRect, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength); void addNode(qtractorCurve::Node *pNode, const QRect& nodeRect, unsigned long iFrame, float fValue); // Clipboard reset method. void clear(); // Clipboard members. QList clips; QList nodes; qtractorTrack *singleTrack; unsigned long frames; } g_clipboard; // Playhead and edit shadow pixel positioning. int m_iPlayHeadX; int m_iEditHeadX; int m_iEditTailX; int m_iPlayHeadAutoBackwardX; // Record rolling update width. int m_iLastRecordX; // Paste interim parameters. unsigned short m_iPasteCount; unsigned long m_iPastePeriod; // Single track dragging interim parameters. bool m_bDragSingleTrack; int m_iDragSingleTrackY; int m_iDragSingleTrackHeight; // Automation curve editing node. qtractorCurve *m_pDragCurve; qtractorCurve::Node *m_pDragCurveNode; int m_iDragCurveX; qtractorCurveEditCommand *m_pCurveEditCommand; // Temporary sync-view/follow-playhead hold state. bool m_bSyncViewHold; int m_iSyncViewHold; // Automation curve node editor widget. qtractorCurve *m_pEditCurve; qtractorCurve::Node *m_pEditCurveNode; QDoubleSpinBox *m_pEditCurveNodeSpinBox; int m_iEditCurveNodeDirty; // Optional edge-shadow gradient brushes. QBrush m_gradLeft; QBrush m_gradRight; }; #endif // __qtractorTrackView_h // end of qtractorTrackView.h qtractor-1.5.11/src/PaxHeaders/qtractorSession.cpp0000644000000000000000000000013215124701674017221 xustar0030 mtime=1767080892.799263428 30 atime=1767080892.799263428 30 ctime=1767080892.799263428 qtractor-1.5.11/src/qtractorSession.cpp0000644000175000001440000021107715124701674017221 0ustar00rncbcusers// qtractorSession.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorSession.h" #include "qtractorSessionCursor.h" #include "qtractorAudioEngine.h" #include "qtractorAudioPeak.h" #include "qtractorAudioClip.h" #include "qtractorMidiEngine.h" #include "qtractorMidiClip.h" #include "qtractorMidiManager.h" #include "qtractorPlugin.h" #include "qtractorCurve.h" #include "qtractorInstrument.h" #include "qtractorCommand.h" #include "qtractorFileList.h" #include "qtractorFiles.h" #include "qtractorMainForm.h" #include #include #include #include #include #include #include #include //------------------------------------------------------------------------- // qtractorSession::Properties -- Session properties structure. // Helper clear/reset method. void qtractorSession::Properties::clear (void) { sessionDir = QDir().absolutePath(); sessionName.clear(); description.clear(); timeScale.clear(); } // Helper copy method. qtractorSession::Properties& qtractorSession::Properties::copy ( const Properties& props ) { if (&props != this) { sessionDir = props.sessionDir; sessionName = props.sessionName; description = props.description; timeScale = props.timeScale; } return *this; } //------------------------------------------------------------------------- // qtractorSession -- Session container. // pSeudo-singleton instance pointer. qtractorSession *qtractorSession::g_pSession = nullptr; // Pseudo-singleton instance accessor (static). qtractorSession *qtractorSession::getInstance (void) { return g_pSession; } // Constructor. qtractorSession::qtractorSession (void) { g_pSession = this; m_tracks.setAutoDelete(true); m_cursors.setAutoDelete(false); m_midiManagers.setAutoDelete(false); // Initial comon client name. m_sClientName = PROJECT_TITLE; // Singleton ownings. m_pFiles = new qtractorFileList(); m_pCommands = new qtractorCommandList(); m_pInstruments = new qtractorInstrumentList(); // The dubious permanency of the crucial device engines. m_pMidiEngine = new qtractorMidiEngine(this); m_pAudioEngine = new qtractorAudioEngine(this); m_pAudioPeakFactory = new qtractorAudioPeakFactory(); m_bAutoTimeStretch = false; m_bAutoDeactivate = false; m_iLoopRecordingMode = 0; clear(); } // Default destructor. qtractorSession::~qtractorSession (void) { close(); clear(); delete m_pAudioPeakFactory; delete m_pAudioEngine; delete m_pMidiEngine; delete m_pInstruments; delete m_pCommands; delete m_pFiles; g_pSession = nullptr; } // Initialize session engine(s). bool qtractorSession::init (void) { // Lock it up... lock(); // Actually init session device engines... const bool bResult = (m_pAudioEngine->init() && m_pMidiEngine->init()); // Done. unlock(); // HACK: Make sure we'll wait some time (~200 msec) // for the JACK server start up and stabilizing... if (bResult) { setSampleRate(m_pAudioEngine->sampleRate()); stabilize(); } return bResult; } // Open session engine(s). bool qtractorSession::open (void) { // Lock it up... lock(); // A default MIDI master bus is always in order... const QString sMaster("Master"); if (m_pMidiEngine->buses().count() == 0) { qtractorMidiBus *pMidiMasterBus = new qtractorMidiBus(m_pMidiEngine, sMaster, qtractorBus::Duplex); m_pMidiEngine->addBus(pMidiMasterBus); } // Get over the stereo playback default master bus... if (m_pAudioEngine->buses().count() == 0) { qtractorAudioBus *pAudioMasterBus = new qtractorAudioBus(m_pAudioEngine, sMaster, qtractorBus::Duplex); pAudioMasterBus->setAutoConnect(m_pAudioEngine->isMasterAutoConnect()); m_pAudioEngine->addBus(pAudioMasterBus); } // Actually open session device engines... if (!m_pAudioEngine->open() || !m_pMidiEngine->open()) { unlock(); close(); return false; } // Open all tracks (assign buses)... qtractorTrack *pTrack = m_tracks.first(); while (pTrack) { if (!pTrack->open()) { unlock(); close(); return false; } pTrack = pTrack->next(); } // Done. unlock(); return true; } // Close session engine(s). void qtractorSession::close (void) { // Lock it up... lock(); m_pAudioEngine->close(); m_pMidiEngine->close(); // Close all tracks (unassign buses)... for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { pTrack->close(); } unlock(); m_pFiles->cleanup(true); // clear(); } // Reset session. void qtractorSession::clear (void) { ATOMIC_SET(&m_locks, 0); ATOMIC_SET(&m_mutex, 0); m_pAudioPeakFactory->sync(); m_pCurrentTrack = nullptr; m_pCommands->clear(); m_tracks.clear(); m_cursors.clear(); m_props.clear(); m_midiTags.clear(); // m_midiManagers.clear(); m_pMidiEngine->clear(); m_pAudioEngine->clear(); m_pFiles->clear(); m_filePaths.clear(); m_trackNames.clear(); qtractorAudioClip::clearHashTable(); qtractorMidiClip::clearHashTable(); m_iSessionStart = 0; m_iSessionEnd = 0; m_iRecordTracks = 0; m_iMuteTracks = 0; m_iSoloTracks = 0; m_iAudioRecord = 0; m_iMidiRecord = 0; m_iMidiTag = 0; m_iEditHead = 0; m_iEditTail = 0; m_iEditHeadTime = 0; m_iEditTailTime = 0; m_iLoopStart = 0; m_iLoopEnd = 0; m_iLoopStartTime = 0; m_iLoopEndTime = 0; m_iPunchIn = 0; m_iPunchOut = 0; m_iPunchInTime = 0; m_iPunchOutTime = 0; m_iPlayHeadAutoBackward = 0; m_bRecording = false; updateTimeScale(); qtractorSessionCursor *pAudioCursor = m_pAudioEngine->sessionCursor(); if (pAudioCursor) { pAudioCursor->resetClips(); pAudioCursor->reset(); pAudioCursor->seek(0); m_cursors.append(pAudioCursor); } qtractorSessionCursor *pMidiCursor = m_pMidiEngine->sessionCursor(); if (pMidiCursor) { pMidiCursor->resetClips(); pMidiCursor->reset(); pMidiCursor->seek(0); m_cursors.append(pMidiCursor); } m_pAudioPeakFactory->cleanup(); qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) pMidiControl->clearControlMap(); } // The global undoable command execuive. bool qtractorSession::execute ( qtractorCommand *pCommand ) { return m_pCommands->exec(pCommand); } // The global undoable command list reference. qtractorCommandList *qtractorSession::commands (void) const { return m_pCommands; } // Session instruments repository. qtractorInstrumentList *qtractorSession::instruments (void) const { return m_pInstruments; } // Session directory path accessors. void qtractorSession::setSessionDir ( const QString& sSessionDir ) { const QDir sdir(sSessionDir); if (sdir.exists()) { m_props.sessionDir = sdir.absolutePath(); if (!QFileInfo(m_props.sessionDir).isWritable()) m_props.sessionDir = QDir::homePath(); } } const QString& qtractorSession::sessionDir (void) const { return m_props.sessionDir; } // Session filename accessors. void qtractorSession::setSessionName ( const QString& sSessionName ) { m_props.sessionName = sSessionName; } const QString& qtractorSession::sessionName (void) const { return m_props.sessionName; } // Session description accessors. void qtractorSession::setDescription ( const QString& sDescription ) { m_props.description = sDescription; } const QString& qtractorSession::description (void) const { return m_props.description; } // Adjust session length to the latest and/or longer clip. void qtractorSession::updateSession ( unsigned long iSessionStart, unsigned long iSessionEnd ) { // Maybe we just don't need to know more... // (recording ongoing?) if (iSessionEnd > 0) { if (m_iSessionEnd < iSessionEnd) m_iSessionEnd = iSessionEnd; // Enough! return; } // Set initial one... m_iSessionStart = iSessionStart; m_iSessionEnd = iSessionEnd; int i = 0; for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { // Find the first and last or longest clip frame position... for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipEnd = iClipStart + pClip->clipLength(); if (m_iSessionStart > iClipStart || i == 0) m_iSessionStart = iClipStart; if (m_iSessionEnd < iClipEnd) m_iSessionEnd = iClipEnd; ++i; } // Find the first and last automation curve frame position... qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList == nullptr) continue; for (qtractorCurve *pCurve = pCurveList->first(); pCurve; pCurve = pCurve->next()) { qtractorCurve::Node *pCurveNode = pCurve->nodes().first(); if (pCurveNode && ( m_iSessionStart > pCurveNode->frame || i == 0)) { m_iSessionStart = pCurveNode->frame; } pCurveNode = pCurve->nodes().last(); if (pCurveNode && m_iSessionEnd < pCurveNode->frame) { m_iSessionEnd = pCurveNode->frame; } } } // Account for the last tempo-map node qtractorTimeScale::Node *pNode = m_props.timeScale.nodes().last(); if (pNode && m_iSessionEnd < pNode->frame) m_iSessionEnd = pNode->frame; // Account for the last marker qtractorTimeScale::Marker *pMarker = m_props.timeScale.markers().last(); if (pMarker && m_iSessionEnd < pMarker->frame) m_iSessionEnd = pMarker->frame; } // Session start/end accessors. unsigned long qtractorSession::sessionStart (void) const { return m_iSessionStart; } unsigned long qtractorSession::sessionEnd (void) const { return m_iSessionEnd; } // Time-scale helper accessors. qtractorTimeScale *qtractorSession::timeScale (void) { return &(m_props.timeScale); } // Device engine common client name accessors. void qtractorSession::setClientName ( const QString& sClientName ) { m_sClientName = sClientName; } const QString& qtractorSession::clientName (void) const { return m_sClientName; } // Sample rate accessors. void qtractorSession::setSampleRate ( unsigned int iSampleRate ) { m_props.timeScale.setSampleRate(iSampleRate); } unsigned int qtractorSession::sampleRate (void) const { return m_props.timeScale.sampleRate(); } // Session tempo accessors. void qtractorSession::setTempo ( float fTempo ) { m_props.timeScale.setTempo(fTempo); m_props.timeScale.updateScale(); } float qtractorSession::tempo (void) const { return m_props.timeScale.tempo(); } // Tempo beat type accessors. void qtractorSession::setBeatType ( unsigned short iBeatType ) { m_props.timeScale.setBeatType(iBeatType); } unsigned short qtractorSession::beatType (void) const { return m_props.timeScale.beatType(); } // Resolution accessors. void qtractorSession::setTicksPerBeat ( unsigned short iTicksPerBeat ) { if (iTicksPerBeat < qtractorTimeScale::TICKS_PER_BEAT_MIN) iTicksPerBeat = qtractorTimeScale::TICKS_PER_BEAT_MIN; else if (iTicksPerBeat > qtractorTimeScale::TICKS_PER_BEAT_MAX) iTicksPerBeat = qtractorTimeScale::TICKS_PER_BEAT_MAX; m_props.timeScale.setTicksPerBeat(iTicksPerBeat); } unsigned short qtractorSession::ticksPerBeat (void) const { return m_props.timeScale.ticksPerBeat(); } // Beats/Bar(measure) accessors. void qtractorSession::setBeatsPerBar ( unsigned short iBeatsPerBar ) { m_props.timeScale.setBeatsPerBar(iBeatsPerBar); } unsigned short qtractorSession::beatsPerBar (void) const { return m_props.timeScale.beatsPerBar(); } // Time signature (denominator) accessors. void qtractorSession::setBeatDivisor ( unsigned short iBeatDivisor ) { m_props.timeScale.setBeatDivisor(iBeatDivisor); } unsigned short qtractorSession::beatDivisor (void) const { return m_props.timeScale.beatDivisor(); } // Pixels per beat (width). void qtractorSession::setPixelsPerBeat ( unsigned short iPixelsPerBeat ) { m_props.timeScale.setPixelsPerBeat(iPixelsPerBeat); } unsigned short qtractorSession::pixelsPerBeat (void) const { return m_props.timeScale.pixelsPerBeat(); } // Horizontal zoom factor. void qtractorSession::setHorizontalZoom ( unsigned short iHorizontalZoom ) { m_props.timeScale.setHorizontalZoom(iHorizontalZoom); } unsigned short qtractorSession::horizontalZoom (void) const { return m_props.timeScale.horizontalZoom(); } // Vertical zoom factor. void qtractorSession::setVerticalZoom ( unsigned short iVerticalZoom ) { m_props.timeScale.setVerticalZoom(iVerticalZoom); } unsigned short qtractorSession::verticalZoom (void) const { return m_props.timeScale.verticalZoom(); } // Beat divisor (snap) accessors. void qtractorSession::setSnapPerBeat ( unsigned short iSnapPerBeat ) { m_props.timeScale.setSnapPerBeat(iSnapPerBeat); } unsigned short qtractorSession::snapPerBeat (void) const { return m_props.timeScale.snapPerBeat(); } // Edit-head frame accessors. void qtractorSession::setEditHead ( unsigned long iEditHead ) { m_iEditHead = iEditHead; m_iEditHeadTime = tickFromFrame(iEditHead); } unsigned long qtractorSession::editHead (void) const { return m_iEditHead; } void qtractorSession::setEditTail ( unsigned long iEditTail ) { m_iEditTail = iEditTail; m_iEditTailTime = tickFromFrame(iEditTail); } unsigned long qtractorSession::editTail (void) const { return m_iEditTail; } // Pixel/Tick number conversion. unsigned long qtractorSession::tickFromPixel ( unsigned int x ) { return m_props.timeScale.tickFromPixel(x); } unsigned int qtractorSession::pixelFromTick ( unsigned long iTick ) { return m_props.timeScale.pixelFromTick(iTick); } // Pixel/Frame number conversion. unsigned long qtractorSession::frameFromPixel ( unsigned int x ) const { return m_props.timeScale.frameFromPixel(x); } unsigned int qtractorSession::pixelFromFrame ( unsigned long iFrame ) const { return m_props.timeScale.pixelFromFrame(iFrame); } // Beat/frame conversion. unsigned long qtractorSession::frameFromBeat ( unsigned int iBeat ) { return m_props.timeScale.frameFromBeat(iBeat); } unsigned int qtractorSession::beatFromFrame ( unsigned long iFrame ) { return m_props.timeScale.beatFromFrame(iFrame); } // Tick/Frame number conversion. unsigned long qtractorSession::frameFromTick ( unsigned long iTick ) { return m_props.timeScale.frameFromTick(iTick); } unsigned long qtractorSession::tickFromFrame ( unsigned long iFrame ) { return m_props.timeScale.tickFromFrame(iFrame); } // Tick/Frame range conversion (delta conversion). unsigned long qtractorSession::frameFromTickRange ( unsigned long iTickStart, unsigned long iTickEnd, bool bOffset ) { return m_props.timeScale.frameFromTickRange(iTickStart, iTickEnd, bOffset); } unsigned long qtractorSession::tickFromFrameRange ( unsigned long iFrameStart, unsigned long iFrameEnd, bool bOffset ) { return m_props.timeScale.tickFromFrameRange(iFrameStart, iFrameEnd, bOffset); } // Beat/frame snap filters. unsigned long qtractorSession::tickSnap ( unsigned long iTick ) { return m_props.timeScale.tickSnap(iTick); } unsigned long qtractorSession::frameSnap ( unsigned long iFrame ) { return m_props.timeScale.frameSnap(iFrame); } unsigned int qtractorSession::pixelSnap ( unsigned int x ) { return m_props.timeScale.pixelSnap(x); } // Frame/locate (SMPTE) conversion. unsigned long qtractorSession::frameFromLocate ( unsigned int iLocate ) const { return (iLocate * m_props.timeScale.sampleRate()) / 30; } unsigned int qtractorSession::locateFromFrame ( unsigned long iFrame ) const { return (30 * iFrame) / m_props.timeScale.sampleRate(); } // Song position pointer (SPP=MIDI beats) to frame converters. unsigned long qtractorSession::frameFromSongPos ( unsigned int iSongPos ) { return frameFromTick((iSongPos * ticksPerBeat()) >> 2); } unsigned int qtractorSession::songPosFromFrame ( unsigned long iFrame ) { return ((tickFromFrame(iFrame) << 2) / ticksPerBeat()); } // Update scale divisor factors. void qtractorSession::updateTimeScale (void) { // Recompute scale divisor factors... m_props.timeScale.updateScale(); // Just (re)synchronize all clips to new tempo state, if any; for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { pClip->updateClipTime(); } } // Update loop points... if (m_iLoopStart < m_iLoopEnd) { m_iLoopStart = frameFromTick(m_iLoopStartTime); m_iLoopEnd = frameFromTick(m_iLoopEndTime); // Set proper loop points for every track, clip and buffer... qtractorTrack *pTrack = m_tracks.first(); while (pTrack) { pTrack->setLoop(m_iLoopStart, m_iLoopEnd); pTrack = pTrack->next(); } } // Update punch points... if (m_iPunchIn < m_iPunchOut) { m_iPunchIn = frameFromTick(m_iPunchInTime); m_iPunchOut = frameFromTick(m_iPunchOutTime); } // Do not forget those edit points too... m_iEditHead = frameFromTick(m_iEditHeadTime); m_iEditTail = frameFromTick(m_iEditTailTime); } void qtractorSession::updateTimeScaleEx (void) { updateTimeScale(); if (m_pAudioEngine) m_pAudioEngine->resetTimebase(); } // Update time resolution divisor factors. void qtractorSession::updateTimeResolution (void) { // Recompute scale divisor factors... m_props.timeScale.updateScale(); // Gotta (re)synchronize all MIDI clips to new resolution... for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { if (pTrack->trackType() == qtractorTrack::Midi) pClip->close(); pClip->setClipStart(pClip->clipStart()); pClip->setClipOffset(pClip->clipOffset()); pClip->setClipLength(pClip->clipLength()); if (pTrack->trackType() == qtractorTrack::Midi) pClip->open(); } } // Update loop points... if (m_iLoopStart < m_iLoopEnd) { m_iLoopStartTime = tickFromFrame(m_iLoopStart); m_iLoopEndTime = tickFromFrame(m_iLoopEnd); } // Update punch points... if (m_iPunchIn < m_iPunchOut) { m_iPunchInTime = tickFromFrame(m_iPunchIn); m_iPunchOutTime = tickFromFrame(m_iPunchOut); } // Do not forget those edit points too... m_iEditHeadTime = tickFromFrame(m_iEditHead); m_iEditTailTime = tickFromFrame(m_iEditTail); } // Update from disparate sample-rate. void qtractorSession::updateSampleRate ( unsigned int iSampleRate ) { if (iSampleRate == m_props.timeScale.sampleRate()) return; #if 0 // Unfortunatelly we must close all clips first, // so let at least all audio peaks be refreshned... for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { pClip->close(); } } #endif // Set the conversion ratio... const float fRatio = float(iSampleRate) / float(m_props.timeScale.sampleRate()); // Set actual sample-rate... m_props.timeScale.setSampleRate(iSampleRate); // Give it some room for just that... stabilize(); updateTimeScale(); stabilize(); // Adjust all tracks and clips (reopening all those...) for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { // Update automation stuff... qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList) { for (qtractorCurve *pCurve = pCurveList->first(); pCurve; pCurve = pCurve->next()) { for (qtractorCurve::Node *pNode = pCurve->nodes().first(); pNode; pNode = pNode->next()) { pNode->frame = qtractorTimeScale::uroundf( fRatio * float(pNode->frame)); } pCurve->update(); } } // Update regular clip stuff... for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { // pClip->setClipStart(qtractorTimeScale::uroundf( // fRatio * float(pClip->clipStart()))); #if 1// FIXUP: Don't quantize to MIDI metronomic time-scale... pClip->setClipOffset(qtractorTimeScale::uroundf( fRatio * float(pClip->clipOffset()))); #endif // pClip->setClipLength(qtractorTimeScale::uroundf( // fRatio * float(pClip->clipLength()))); pClip->setFadeInLength(qtractorTimeScale::uroundf( fRatio * float(pClip->fadeInLength()))); pClip->setFadeOutLength(qtractorTimeScale::uroundf( fRatio * float(pClip->fadeOutLength()))); pClip->open(); } } } // Alternate properties accessor. qtractorSession::Properties& qtractorSession::properties (void) { return m_props; } // Track list management methods. const qtractorList& qtractorSession::tracks (void) const { return m_tracks; } void qtractorSession::addTrack ( qtractorTrack *pTrack ) { insertTrack(pTrack, m_tracks.last()); } void qtractorSession::insertTrack ( qtractorTrack *pTrack, qtractorTrack *pPrevTrack ) { // lock(); if (pTrack->trackType() == qtractorTrack::Midi) acquireMidiTag(pTrack); if (pPrevTrack) { m_tracks.insertAfter(pTrack, pPrevTrack); } else { m_tracks.prepend(pTrack); } #if 0 if (pTrack->isRecord()) setRecordTracks(true); if (pTrack->isMute()) setMuteTracks(true); if (pTrack->isSolo()) setSoloTracks(true); #endif // Acquire track-name for uniqueness... acquireTrackName(pTrack); // Associate track to its curve-list... acquireTrackCurveList(pTrack); qtractorSessionCursor *pSessionCursor = m_cursors.first(); while (pSessionCursor) { pSessionCursor->addTrack(pTrack); pSessionCursor = pSessionCursor->next(); } pTrack->setLoop(m_iLoopStart, m_iLoopEnd); pTrack->open(); // unlock(); } void qtractorSession::moveTrack ( qtractorTrack *pTrack, qtractorTrack *pNextTrack ) { // lock(); m_tracks.unlink(pTrack); if (pNextTrack) m_tracks.insertBefore(pTrack, pNextTrack); else m_tracks.append(pTrack); qtractorSessionCursor *pSessionCursor = m_cursors.first(); while (pSessionCursor) { pSessionCursor->resetClips(); pSessionCursor = pSessionCursor->next(); } // unlock(); } void qtractorSession::updateTrack ( qtractorTrack *pTrack ) { // lock(); pTrack->setLoop(m_iLoopStart, m_iLoopEnd); qtractorSessionCursor *pSessionCursor = m_cursors.first(); while (pSessionCursor) { pSessionCursor->updateTrack(pTrack); pSessionCursor = pSessionCursor->next(); } // unlock(); } void qtractorSession::unlinkTrack ( qtractorTrack *pTrack ) { // lock(); pTrack->setLoop(0, 0); pTrack->close(); qtractorSessionCursor *pSessionCursor = m_cursors.first(); while (pSessionCursor) { pSessionCursor->removeTrack(pTrack); pSessionCursor = pSessionCursor->next(); } if (pTrack->isRecord()) setRecordTracks(false); if (pTrack->isMute()) setMuteTracks(false); if (pTrack->isSolo()) setSoloTracks(false); // Release track-name from uniqueness... releaseTrackName(pTrack); // Dessociate track from its curve-list... releaseTrackCurveList(pTrack); if (pTrack->trackType() == qtractorTrack::Midi) releaseMidiTag(pTrack); m_tracks.unlink(pTrack); // unlock(); } qtractorTrack *qtractorSession::trackAt ( int iTrack ) const { return m_tracks.at(iTrack); } // Current number of record-armed tracks. void qtractorSession::setRecordTracks ( bool bRecord ) { if (bRecord) { ++m_iRecordTracks; } else if (m_iRecordTracks > 0) { --m_iRecordTracks; } } unsigned int qtractorSession::recordTracks (void) const { return m_iRecordTracks; } // Current number of muted tracks. void qtractorSession::setMuteTracks ( bool bMute ) { if (bMute) { ++m_iMuteTracks; } else if (m_iMuteTracks > 0) { --m_iMuteTracks; } } unsigned int qtractorSession::muteTracks (void) const { return m_iMuteTracks; } // Current number of solo tracks. void qtractorSession::setSoloTracks ( bool bSolo ) { if (bSolo) { ++m_iSoloTracks; } else if (m_iSoloTracks > 0) { --m_iSoloTracks; } } unsigned int qtractorSession::soloTracks (void) const { return m_iSoloTracks; } // Temporary current track accessors. void qtractorSession::setCurrentTrack ( qtractorTrack *pTrack ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorSession::setCurrentTrack(%p)", pTrack); #endif qtractorTrack *pOldCurrentTrack = m_pCurrentTrack; m_pCurrentTrack = pTrack; // Notify auto-plugin-deactivate... if (m_pCurrentTrack != pOldCurrentTrack) autoDeactivatePlugins(); // Current-track plugin editors (GUI) auto-focus... if (m_pCurrentTrack && m_pCurrentTrack->pluginList()) m_pCurrentTrack->pluginList()->setEditorVisibleAll(true); } qtractorTrack *qtractorSession::currentTrack (void) const { return m_pCurrentTrack; } // Temporary current track predicates. bool qtractorSession::isTrackMonitor ( qtractorTrack *pTrack ) const { return pTrack->isMonitor() || pTrack == m_pCurrentTrack; } bool qtractorSession::isTrackMidiChannel ( qtractorTrack *pTrack, unsigned short iChannel ) const { return pTrack->isMidiOmni() || pTrack->midiChannel() == iChannel || pTrack == m_pCurrentTrack; } // Session cursor factory methods. qtractorSessionCursor *qtractorSession::createSessionCursor ( unsigned long iFrame, qtractorTrack::TrackType syncType ) { qtractorSessionCursor *pSessionCursor = new qtractorSessionCursor(this, iFrame, syncType); m_cursors.append(pSessionCursor); return pSessionCursor; } void qtractorSession::unlinkSessionCursor ( qtractorSessionCursor *pSessionCursor ) { m_cursors.unlink(pSessionCursor); } // Reset (reactivate) all plugin chains... void qtractorSession::resetAllPlugins (void) { // All tracks... for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { (pTrack->pluginList())->resetBuffers(); } // All audio buses... for (qtractorBus *pBus = m_pAudioEngine->buses().first(); pBus; pBus = pBus->next()) { qtractorAudioBus *pAudioBus = static_cast (pBus); if (pAudioBus) { if (pAudioBus->pluginList_in()) pAudioBus->pluginList_in()->resetBuffers(); if (pAudioBus->pluginList_out()) pAudioBus->pluginList_out()->resetBuffers(); } } } // MIDI engine accessor. qtractorMidiEngine *qtractorSession::midiEngine (void) const { return m_pMidiEngine; } // Audio engine accessor. qtractorAudioEngine *qtractorSession::audioEngine (void) const { return m_pAudioEngine; } // Wait for application stabilization. void qtractorSession::stabilize ( int msecs ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorSession::stabilize(%d)", msecs); #endif // Wait a litle bit before continue... QElapsedTimer timer; timer.start(); while (timer.elapsed() < msecs) { QThread::yieldCurrentThread(); QApplication::processEvents(/* QEventLoop::ExcludeUserInputEvents */); } } // Consolidated session engine activation status. bool qtractorSession::isActivated (void) const { return (m_pAudioEngine->isActivated() && m_pMidiEngine->isActivated()); } // Consolidated session engine start status. void qtractorSession::setPlaying ( bool bPlaying ) { // For all armed tracks... if (bPlaying && isRecording()) { // Take a snapshot on where recording // clips are about to start... const unsigned long iPlayHead = playHead(); unsigned long iClipStart = iPlayHead; if (isPunching()) { const unsigned long iPunchIn = punchIn(); if (iClipStart < iPunchIn) iClipStart = iPunchIn; } // Of course, mark those clips alright... for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { qtractorClip *pClipRecord = pTrack->clipRecord(); if (pClipRecord && !pTrack->isClipRecordEx()) { pTrack->setClipRecordStart(iClipStart); pClipRecord->setClipStart(iClipStart); // MIDI adjust to playing queue start... if (pTrack->trackType() == qtractorTrack::Midi && iClipStart > iPlayHead) { qtractorMidiClip *pMidiClip = static_cast (pClipRecord); if (pMidiClip) { pMidiClip->sequence()->setTimeOffset( tickFromFrame(iClipStart - iPlayHead)); } } } } } // Have all MIDI instrument plugins be shut up // if start playing, otherwise do ramping down... if (bPlaying) { qtractorMidiManager *pMidiManager = m_midiManagers.first(); while (pMidiManager) { pMidiManager->reset(); pMidiManager = pMidiManager->next(); } } // Do it. m_pAudioEngine->setPlaying(bPlaying); m_pMidiEngine->setPlaying(bPlaying); // notify auto-plugin-deactivate autoDeactivatePlugins(); } bool qtractorSession::isPlaying() const { return (m_pAudioEngine->isPlaying() && m_pMidiEngine->isPlaying()); } // Shutdown procedure. void qtractorSession::shutdown (void) { m_pAudioEngine->setPlaying(false); m_pMidiEngine->setPlaying(false); close(); } // (Hazardous) bi-directional locate method. void qtractorSession::seek ( unsigned long iFrame, bool bSync ) { if (bSync) resetAllPlugins(); m_pAudioEngine->sessionCursor()->seek(iFrame, bSync); m_pMidiEngine->sessionCursor()->seek(iFrame, bSync); } // Session RT-safe pseudo-locking primitives. bool qtractorSession::acquire (void) { // Are we in business? return ATOMIC_TAS(&m_mutex); } void qtractorSession::release (void) { // We're not in business anymore. ATOMIC_SET(&m_mutex, 0); } void qtractorSession::lock (void) { // Wind up as pending lock... if (ATOMIC_INC(&m_locks) == 1) { // Get lost for a while... while (!acquire()) stabilize(); } } void qtractorSession::unlock (void) { // Unwind pending locks and force back to business... if (ATOMIC_DEC(&m_locks) < 1) { ATOMIC_SET(&m_locks, 0); release(); } } // Re-entrancy check. bool qtractorSession::isBusy (void) const { return (ATOMIC_GET(&m_locks) > 0); } // Playhead positioning. void qtractorSession::setPlayHead ( unsigned long iPlayHead ) { const bool bPlaying = isPlaying(); if (bPlaying && isRecording()) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorSession::setPlayHead(%lu)", iPlayHead); #endif lock(); setPlaying(false); if (m_pAudioEngine->transportMode() & qtractorBus::Output) m_pAudioEngine->transport_locate(iPlayHead); seek(iPlayHead, true); if (!bPlaying) { // Update time(base)/BBT info... m_pAudioEngine->updateTimeInfo(iPlayHead); // Reset step-input tracking... m_pMidiEngine->proxy()->notifyInpEvent(qtractorMidiEngine::InpReset); // Sync all track automation... process_curve(iPlayHead); } setPlaying(bPlaying); unlock(); } void qtractorSession::setPlayHeadEx ( unsigned long iPlayHead ) { const bool bPlaying = isPlaying(); if (bPlaying && isRecording()) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorSession::setPlayHeadEx(%lu)", iPlayHead); #endif lock(); m_pMidiEngine->setPlaying(false); seek(iPlayHead, true); // Have all MIDI instrument plugins be shut up // if start playing, otherwise do ramping down... qtractorMidiManager *pMidiManager = m_midiManagers.first(); while (pMidiManager) { pMidiManager->reset(); pMidiManager = pMidiManager->next(); } if (bPlaying) { // Reset all dependables... m_pAudioEngine->resetAllMonitors(); // Make sure we have an actual session cursor... //m_pAudioEngine->resetMetro(); } else { // Update time(base)/BBT info... m_pAudioEngine->updateTimeInfo(iPlayHead); // Reset step-input tracking... m_pMidiEngine->proxy()->notifyInpEvent(qtractorMidiEngine::InpReset); // Sync all track automation... process_curve(iPlayHead); } m_pMidiEngine->setPlaying(bPlaying); unlock(); } unsigned long qtractorSession::playHead (void) const { return m_pAudioEngine->sessionCursor()->frame(); } // Auto-backward save play-head frame accessors. void qtractorSession::setPlayHeadAutoBackward ( unsigned long iPlayHead ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorSession::setPlayHeadAutoBackward(%lu)", iPlayHead); #endif m_iPlayHeadAutoBackward = iPlayHead; } unsigned long qtractorSession::playHeadAutoBackward (void) const { return m_iPlayHeadAutoBackward; } // Session loop points accessors. void qtractorSession::setLoop ( unsigned long iLoopStart, unsigned long iLoopEnd ) { const bool bPlaying = isPlaying(); if (bPlaying && isRecording()) return; lock(); setPlaying(false); // Local prepare... if (iLoopStart >= iLoopEnd) { iLoopStart = 0; iLoopEnd = 0; } // Save exact current play-head position... const unsigned long iFrame = playHead(); // Set proper loop points for every track, clip and buffer... qtractorTrack *pTrack = m_tracks.first(); while (pTrack) { pTrack->setLoop(iLoopStart, iLoopEnd); pTrack = pTrack->next(); } // Local commit... m_iLoopStart = iLoopStart; m_iLoopEnd = iLoopEnd; // Time-normalized references too... m_iLoopStartTime = tickFromFrame(iLoopStart); m_iLoopEndTime = tickFromFrame(iLoopEnd); // Replace last known play-head... m_pAudioEngine->sessionCursor()->seek(iFrame, true); m_pMidiEngine->sessionCursor()->seek(iFrame, true); setPlaying(bPlaying); unlock(); } unsigned long qtractorSession::loopStart (void) const { return m_iLoopStart; } unsigned long qtractorSession::loopEnd (void) const { return m_iLoopEnd; } bool qtractorSession::isLooping (void) const { return (m_iLoopStart < m_iLoopEnd); } unsigned long qtractorSession::loopStartTime (void) const { return m_iLoopStartTime; } unsigned long qtractorSession::loopEndTime (void) const { return m_iLoopEndTime; } // Session punch points accessors. void qtractorSession::setPunch ( unsigned long iPunchIn, unsigned long iPunchOut ) { // Local prepare... if (iPunchIn >= iPunchOut) { iPunchIn = 0; iPunchOut = 0; } // Local commit... m_iPunchIn = iPunchIn; m_iPunchOut = iPunchOut; // Time-normalized references too... m_iPunchInTime = tickFromFrame(iPunchIn); m_iPunchOutTime = tickFromFrame(iPunchOut); } unsigned long qtractorSession::punchIn (void) const { return m_iPunchIn; } unsigned long qtractorSession::punchOut (void) const { return m_iPunchOut; } bool qtractorSession::isPunching (void) const { return (m_iPunchIn < m_iPunchOut); } unsigned long qtractorSession::punchInTime (void) const { return m_iPunchInTime; } unsigned long qtractorSession::punchOutTime (void) const { return m_iPunchOutTime; } unsigned long qtractorSession::frameTime (void) const { return m_pAudioEngine->sessionCursor()->frameTime(); } unsigned long qtractorSession::frameTimeEx (void) const { return m_pAudioEngine->sessionCursor()->frameTimeEx(); } // Sanitize a given name. QString qtractorSession::sanitize ( const QString& s ) { // return s.simplified().replace(QRegularExpression("[\\s|\\.|\\-|/]+"), '_'); const QChar space(' '); const QRegularExpression rx("[^\\w]+"); return QString(s).replace(rx, space).simplified().replace(space, '_'); } // Provide an unique track-name if applicable, // append an incremental numerical suffix... QString qtractorSession::uniqueTrackName ( const QString& sTrackName ) const { QString sNewTrackName = sTrackName; const QString& sShortTrackName = qtractorTrack::shortTrackName(sTrackName); if (!findTrack(sShortTrackName)) return sTrackName; QString sOldShortTrackName = sShortTrackName; QString sNewShortTrackName; QRegularExpression rxTrackNo("([0-9]+)$"); QRegularExpressionMatch match = rxTrackNo.match(sOldShortTrackName); int iTrackNo = 0; if (match.hasMatch()) { iTrackNo = match.captured(1).toInt(); sOldShortTrackName.remove(rxTrackNo); } else sOldShortTrackName += ' '; do { sNewShortTrackName = sOldShortTrackName + QString::number(++iTrackNo); } while (findTrack(sNewShortTrackName)); return sNewTrackName.replace(sShortTrackName, sNewShortTrackName); } void qtractorSession::acquireTrackName ( qtractorTrack *pTrack ) { if (pTrack) m_trackNames.insert(pTrack->shortTrackName(), pTrack); } void qtractorSession::releaseTrackName ( qtractorTrack *pTrack ) { if (pTrack) m_trackNames.remove(pTrack->shortTrackName()); } // Transient file-name registry methods as far to // avoid duplicates across load/save/record cycles... void qtractorSession::acquireFilePath ( const QString& sFilename ) { m_filePaths.append(sFilename); } void qtractorSession::releaseFilePath ( const QString& sFilename ) { m_filePaths.removeAll(sFilename); } // Create a brand new filename (absolute file path). QString qtractorSession::createFilePath ( const QString& sBaseName, const QString& sExt, bool bAcquire ) { QString sFilename = qtractorSession::sanitize(m_props.sessionName); if (!sFilename.isEmpty()) sFilename += '-'; sFilename += qtractorSession::sanitize(sBaseName) + "-%1." + sExt; // If there are any existing, similar filenames, // take the version suffix from the most recent... int iFileNo = 0; QDir dir(m_props.sessionDir); const QStringList filter(sFilename.arg('*')); const QStringList& files = dir.entryList(filter, QDir::Files, QDir::Time); if (!files.isEmpty()) { QRegularExpression rx(sFilename.arg("([\\d]+)")); QRegularExpressionMatchIterator iter = rx.globalMatch(files.first()); if (iter.hasNext()) { QRegularExpressionMatch match = iter.next(); while (iter.hasNext()) match = iter.next(); iFileNo = match.captured(1).toInt(); } } // Check whether it's not aquired as our own already, // otherwise increment version suffix until it is. if (iFileNo == 0 || !bAcquire) ++iFileNo; QFileInfo fi(m_props.sessionDir, sFilename.arg(iFileNo)); if (!m_filePaths.contains(fi.absoluteFilePath())) { while (fi.exists() || m_filePaths.contains(fi.absoluteFilePath())) fi.setFile(m_props.sessionDir, sFilename.arg(++iFileNo)); if (bAcquire) m_filePaths.append(fi.absoluteFilePath()); // register new name! } #ifdef CONFIG_DEBUG qDebug("qtractorSession::createFilePath(\"%s\")", fi.absoluteFilePath().toUtf8().constData()); #endif return fi.absoluteFilePath(); } // Session directory relative/absolute file path helpers. QString qtractorSession::relativeFilePath ( const QString& sFilename ) const { return QDir(sessionDir()).relativeFilePath(sFilename); } QString qtractorSession::absoluteFilePath ( const QString& sFilename ) const { return QDir::cleanPath(QDir(sessionDir()).absoluteFilePath(sFilename)); } // Consolidated session record state. void qtractorSession::setRecording ( bool bRecording ) { m_bRecording = bRecording; // For all armed tracks... unsigned long iClipStart = playHead(); if (isPunching()) { const unsigned long iPunchIn = punchIn(); if (iClipStart < iPunchIn) iClipStart = iPunchIn; } const unsigned long iFrameTime = frameTimeEx(); for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { if (pTrack->isRecord()) trackRecord(pTrack, bRecording, iClipStart, iFrameTime); } } bool qtractorSession::isRecording (void) const { return m_bRecording; } // Loop-recording/take mode. void qtractorSession::setLoopRecordingMode ( int iLoopRecordingMode ) { m_iLoopRecordingMode = iLoopRecordingMode; } int qtractorSession::loopRecordingMode (void) const { return m_iLoopRecordingMode; } // Immediate track record-arming. void qtractorSession::trackRecord ( qtractorTrack *pTrack, bool bRecord, unsigned long iClipStart, unsigned long iFrameTime ) { #ifdef CONFIG_DEBUG qDebug("qtractorSession::trackRecord(\"%s\", %d, %lu, %lu)", pTrack->shortTrackName().toUtf8().constData(), int(bRecord), iClipStart, iFrameTime); #endif const qtractorTrack::TrackType trackType = pTrack->trackType(); // Just ditch the in-record clip... if (!bRecord) { pTrack->setClipRecord(nullptr); // Check whether we set recording off... if (recordTracks() < 1) setRecording(false); // One-down current tracks in record mode. switch (trackType) { case qtractorTrack::Audio: --m_iAudioRecord; break; case qtractorTrack::Midi: --m_iMidiRecord; break; default: break; } #if 0 // Re-sync as appropriate... if (isPlaying()) trackMute(pTrack, false); #endif // Done. return; } // Are we set record exclusive? if (pTrack->isClipRecordEx()) { // One-up current tracks in record mode. switch (trackType) { case qtractorTrack::Audio: ++m_iAudioRecord; break; case qtractorTrack::Midi: ++m_iMidiRecord; break; default: break; } // Bail out. return; } const bool bPlaying = isPlaying(); switch (trackType) { case qtractorTrack::Audio: { qtractorAudioClip *pAudioClip = new qtractorAudioClip(pTrack); pAudioClip->setClipStart(iClipStart); pAudioClip->openAudioFile( createFilePath(pTrack->shortTrackName(), qtractorAudioFileFactory::defaultExt(), true), qtractorAudioFile::Write); pTrack->setClipRecord(pAudioClip); // Adjust for some input latency compensation already... qtractorAudioBus *pAudioBus = static_cast (pTrack->inputBus()); if (pAudioBus) // This is currently just a workaround, because // we consider here only the output latency of // the current audio bus. // TODO: calculate the overall out latency somehow pAudioClip->setClipOffset( pAudioBus->latency_in() + pAudioBus->latency_out()); // One-up audio tracks in record mode. ++m_iAudioRecord; break; } case qtractorTrack::Midi: { qtractorMidiClip *pMidiClip = new qtractorMidiClip(pTrack); pMidiClip->setClipStart(iClipStart); pMidiClip->openMidiFile( createFilePath(pTrack->shortTrackName(), "mid", true), pTrack->midiChannel(), qtractorMidiFile::Write); pTrack->setClipRecord(pMidiClip); // MIDI adjust to playing queue start // iif armed while already playing ... if (bPlaying) { const unsigned long iTime = pMidiClip->clipStartTime(); const unsigned long iTimeStart = m_pMidiEngine->timeStartEx(); if (iTime > iTimeStart) pMidiClip->sequence()->setTimeOffset(iTime - iTimeStart); } // One-up MIDI tracks in record mode. ++m_iMidiRecord; break; } default: break; } // Make sure the recording clip // starts on some exact location... pTrack->setClipRecordStart(iFrameTime); #if 0 // Mute track as appropriate... if (bPlaying) trackMute(pTrack, true); #endif } // Immediate track mute (engine indirection). void qtractorSession::trackMute ( qtractorTrack *pTrack, bool bMute ) { // For the time being, only needed for ALSA sequencer... switch (pTrack->trackType()) { case qtractorTrack::Audio: m_pAudioEngine->trackMute(pTrack, bMute); break; case qtractorTrack::Midi: m_pMidiEngine->trackMute(pTrack, bMute); break; case qtractorTrack::None: default: break; } } // Immediate track solo (engine indirection). void qtractorSession::trackSolo ( qtractorTrack *pTrack, bool bSolo ) { // Check if we're going to (un)mute all others, // due to soloing this one; if already soloing, // no need for anything ado... if ((bSolo && m_iSoloTracks > 1) || (!bSolo && m_iSoloTracks > 0)) { trackMute(pTrack, !bSolo); return; } for (qtractorTrack *pTrackMute = m_tracks.first(); pTrackMute; pTrackMute = pTrackMute->next()) { // For all other track, but this one. if (pTrackMute == pTrack || pTrackMute->isMute()) continue; // (Un)mute each other track... trackMute(pTrackMute, bSolo); } } // Auto plugin deactivation specifics void qtractorSession::autoDeactivatePlugins ( bool bForce ) { // Enabled && not if busy (e.g loading session) if (m_bAutoDeactivate && !isBusy()) { for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { pTrack->pluginList()->autoDeactivatePlugins( canTrackBeAutoDeactivated(pTrack), bForce); } } } void qtractorSession::undoAutoDeactivatePlugins (void) { for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { pTrack->pluginList()->autoDeactivatePlugins(false); } } void qtractorSession::setAutoDeactivate ( bool bOn ) { m_bAutoDeactivate = bOn; if (bOn) autoDeactivatePlugins(); else undoAutoDeactivatePlugins(); } bool qtractorSession::isAutoDeactivate (void) const { return m_bAutoDeactivate; } bool qtractorSession::canTrackBeAutoDeactivated ( qtractorTrack *pTrack ) const { bool bCanBeDeactivated = false; // Check automation bool bAutomationActive = false; qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList) bAutomationActive = pCurveList->isProcess() || pCurveList->isCapture(); // No Auto-plugin-deactivation when automation active // Note: freewheeling case is done the hard way by disabling // auto-deactivate as whole - see qtractorMainForm::trackExportAudio if (!bAutomationActive) { if (isPlaying()) { // Monitored and recording tracks cannot be deactivated if(!isTrackMonitor(pTrack) && !pTrack->isRecord()) { bool bMute = pTrack->isMute(); if(!bMute) bMute = m_iSoloTracks > 0 && !pTrack->isSolo(); // TBD: We know when clips start/end. So we 'just' need a // clever song pos synced call of autoPluginsDeactivate // For now only check if track contains clips. bool bHasClips = pTrack->clips().count() > 0; if (bMute || !bHasClips) bCanBeDeactivated = true; } } else { bCanBeDeactivated = !isTrackMonitor(pTrack); } } return bCanBeDeactivated; } // Track recording specifics. unsigned short qtractorSession::audioRecord (void) const { return m_iAudioRecord; } unsigned short qtractorSession::midiRecord (void) const { return m_iMidiRecord; } // Audio peak factory accessor. qtractorAudioPeakFactory *qtractorSession::audioPeakFactory (void) const { return m_pAudioPeakFactory; } // MIDI track tagging specifics. unsigned short qtractorSession::midiTag (void) const { return m_iMidiTag; } void qtractorSession::acquireMidiTag ( qtractorTrack *pTrack ) { if (pTrack->midiTag() > 0) return; if (m_midiTags.isEmpty()) { pTrack->setMidiTag(++m_iMidiTag); } else { pTrack->setMidiTag(m_midiTags.front()); m_midiTags.pop_front(); } } void qtractorSession::releaseMidiTag ( qtractorTrack *pTrack ) { const unsigned short iMidiTag = pTrack->midiTag(); if (iMidiTag > 0) { m_midiTags.push_back(iMidiTag); pTrack->setMidiTag(0); } } // MIDI session/tracks instrument/controller patching (conditional). void qtractorSession::resetAllMidiControllers ( bool bForceImmediate ) { m_pMidiEngine->resetAllControllers( ( bForceImmediate && m_pMidiEngine->isResetAllControllers()) || (!bForceImmediate && m_pMidiEngine->isResetAllControllersPending())); } // MIDI manager list accessors. void qtractorSession::addMidiManager ( qtractorMidiManager *pMidiManager ) { m_midiManagers.append(pMidiManager); } void qtractorSession::removeMidiManager ( qtractorMidiManager *pMidiManager ) { m_midiManagers.remove(pMidiManager); } const qtractorList& qtractorSession::midiManagers (void) const { return m_midiManagers; } // Auto time-stretching global flag (when tempo changes) void qtractorSession::setAutoTimeStretch ( bool bAutoTimeStretch ) { m_bAutoTimeStretch = bAutoTimeStretch; } bool qtractorSession::isAutoTimeStretch (void) const { return m_bAutoTimeStretch; } // Session special process cycle executive. void qtractorSession::process ( qtractorSessionCursor *pSessionCursor, unsigned long iFrameStart, unsigned long iFrameEnd ) { const qtractorTrack::TrackType syncType = pSessionCursor->syncType(); // Now, for every track... int iTrack = 0; qtractorTrack *pTrack = m_tracks.first(); while (pTrack) { // Track automation processing... if (syncType == qtractorTrack::Audio) pTrack->process_curve(iFrameStart); // Track clip processing... if (syncType == pTrack->trackType()) { pTrack->process(pSessionCursor->clip(iTrack), iFrameStart, iFrameEnd); } pTrack = pTrack->next(); ++iTrack; } } // Session special process record executive (audio recording only). void qtractorSession::process_record ( unsigned long iFrameStart, unsigned long iFrameEnd ) { // Now, for every Audio track... for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() == qtractorTrack::Audio && pTrack->isRecord()) pTrack->process_record(iFrameStart, iFrameEnd); } } // Session special process automation executive. void qtractorSession::process_curve ( unsigned long iFrame ) { // Now, for every track... qtractorTrack *pTrack = m_tracks.first(); while (pTrack) { pTrack->process_curve(iFrame); pTrack = pTrack->next(); } } // Manage curve-lists to specific tracks. void qtractorSession::acquireTrackCurveList ( qtractorTrack *pTrack ) { if (pTrack && pTrack->curveList()) m_curves.insert(pTrack->curveList(), pTrack); } void qtractorSession::releaseTrackCurveList ( qtractorTrack *pTrack ) { if (pTrack && pTrack->curveList()) m_curves.remove(pTrack->curveList()); } // Find track of specific curve-list. qtractorTrack *qtractorSession::findTrackCurveList ( qtractorCurveList *pCurveList ) const { return m_curves.value(pCurveList, nullptr); } // Find track of specific name. qtractorTrack *qtractorSession::findTrack ( const QString& sTrackName ) const { return m_trackNames.value(sTrackName, nullptr); } // Session files registry accessor. qtractorFileList *qtractorSession::files (void) const { return m_pFiles; } // Document element methods. bool qtractorSession::loadElement ( Document *pDocument, QDomElement *pElement ) { qtractorSession::clear(); qtractorSession::lock(); // Templates have no session name... const bool bTemplate = pDocument->isTemplate(); if (!bTemplate) qtractorSession::setSessionName(pElement->attribute("name")); // Session state should be postponed... unsigned long iLoopStart = 0; unsigned long iLoopEnd = 0; unsigned long iPunchIn = 0; unsigned long iPunchOut = 0; // Load session children... for (QDomNode nChild = pElement->firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert node to element... QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; // Load session properties... if (eChild.tagName() == "properties") { for (QDomNode nProp = eChild.firstChild(); !nProp.isNull(); nProp = nProp.nextSibling()) { // Convert property node to element... QDomElement eProp = nProp.toElement(); if (eProp.isNull()) continue; if (eProp.tagName() == "directory") qtractorSession::setSessionDir(eProp.text()); else if (eProp.tagName() == "description") qtractorSession::setDescription(eProp.text()); else if (eProp.tagName() == "sample-rate" && !bTemplate) qtractorSession::setSampleRate(eProp.text().toUInt()); else if (eProp.tagName() == "tempo") qtractorSession::setTempo(eProp.text().toFloat()); else if (eProp.tagName() == "ticks-per-beat") qtractorSession::setTicksPerBeat(eProp.text().toUShort()); else if (eProp.tagName() == "beats-per-bar") qtractorSession::setBeatsPerBar(eProp.text().toUShort()); else if (eProp.tagName() == "beat-divisor") qtractorSession::setBeatDivisor(eProp.text().toUShort()); } // We need to make this permanent, right now. qtractorSession::updateTimeScale(); } else if (eChild.tagName() == "state" && !bTemplate) { for (QDomNode nState = eChild.firstChild(); !nState.isNull(); nState = nState.nextSibling()) { // Convert state node to element... QDomElement eState = nState.toElement(); if (eState.isNull()) continue; if (eState.tagName() == "loop-start") iLoopStart = eState.text().toULong(); else if (eState.tagName() == "loop-end") iLoopEnd = eState.text().toULong(); else if (eState.tagName() == "punch-in") iPunchIn = eState.text().toULong(); else if (eState.tagName() == "punch-out") iPunchOut = eState.text().toULong(); } } else // Load file lists... if (eChild.tagName() == "files" && !bTemplate) { for (QDomNode nList = eChild.firstChild(); !nList.isNull(); nList = nList.nextSibling()) { // Convert filelist node to element... QDomElement eList = nList.toElement(); if (eList.isNull()) continue; if (eList.tagName() == "audio-list") { qtractorAudioListView *pAudioList = nullptr; if (pDocument->files()) pAudioList = pDocument->files()->audioListView(); if (pAudioList == nullptr) return false; if (!pAudioList->loadElement(pDocument, &eList)) return false; } else if (eList.tagName() == "midi-list") { qtractorMidiListView *pMidiList = nullptr; if (pDocument->files()) pMidiList = pDocument->files()->midiListView(); if (pMidiList == nullptr) return false; if (!pMidiList->loadElement(pDocument, &eList)) return false; } } // Stabilize things a bit... stabilize(); } else // Load device lists... if (eChild.tagName() == "devices") { for (QDomNode nDevice = eChild.firstChild(); !nDevice.isNull(); nDevice = nDevice.nextSibling()) { // Convert buses list node to element... QDomElement eDevice = nDevice.toElement(); if (eDevice.isNull()) continue; if (eDevice.tagName() == "audio-engine") { if (!qtractorSession::audioEngine() ->loadElement(pDocument, &eDevice)) { return false; } } else if (eDevice.tagName() == "midi-engine") { if (!qtractorSession::midiEngine() ->loadElement(pDocument, &eDevice)) { return false; } } } // Stabilize things a bit... stabilize(); } else // Load tempo/time-signature map... if (eChild.tagName() == "tempo-map") { for (QDomNode nNode = eChild.firstChild(); !nNode.isNull(); nNode = nNode.nextSibling()) { // Convert tempo node to element... QDomElement eNode = nNode.toElement(); if (eNode.isNull()) continue; // Load tempo-map... if (eNode.tagName() == "tempo-node") { const unsigned long iFrame = eNode.attribute("frame").toULong(); float fTempo = 120.0f; unsigned short iBeatType = 2; unsigned short iBeatsPerBar = 4; unsigned short iBeatDivisor = 2; for (QDomNode nItem = eNode.firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { // Convert node to element... QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; if (eItem.tagName() == "tempo") fTempo = eItem.text().toFloat(); else if (eItem.tagName() == "beat-type") iBeatType = eItem.text().toUShort(); else if (eItem.tagName() == "beats-per-bar") iBeatsPerBar = eItem.text().toUShort(); else if (eItem.tagName() == "beat-divisor") iBeatDivisor = eItem.text().toUShort(); } // Add new node to tempo/time-signature map... qtractorSession::timeScale()->addNode(iFrame, fTempo, iBeatType, iBeatsPerBar, iBeatDivisor); } } // Again, make view/time scaling factors permanent. qtractorSession::updateTimeScale(); } else // Load location markers... if (eChild.tagName() == "markers") { for (QDomNode nMarker = eChild.firstChild(); !nMarker.isNull(); nMarker = nMarker.nextSibling()) { // Convert tempo node to element... QDomElement eMarker = nMarker.toElement(); if (eMarker.isNull()) continue; // Load markers/key-signatures... if (eMarker.tagName() == "marker") { const unsigned long iFrame = eMarker.attribute("frame").toULong(); QString sText; QColor rgbColor = Qt::darkGray; int iAccidentals = qtractorTimeScale::MinAccidentals; int iMode = -1; for (QDomNode nItem = eMarker.firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { // Convert node to element... QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; if (eItem.tagName() == "text") sText = eItem.text(); else if (eItem.tagName() == "color") { #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) rgbColor = QColor::fromString(eItem.text()); #else rgbColor = QColor(eItem.text()); #endif } else if (eItem.tagName() == "accidentals") iAccidentals = eItem.text().toInt(); else if (eItem.tagName() == "mode") iMode = eItem.text().toInt(); } // Add new marker... if (!sText.isEmpty()) { qtractorSession::timeScale()->addMarker( iFrame, sText, rgbColor); } // Or/and key-signature... if (qtractorTimeScale::isKeySignature(iAccidentals, iMode)) { qtractorSession::timeScale()->addKeySignature( iFrame, iAccidentals, iMode); } } } } else // Load tracks... if (eChild.tagName() == "tracks") { for (QDomNode nTrack = eChild.firstChild(); !nTrack.isNull(); nTrack = nTrack.nextSibling()) { // Convert track node to element... QDomElement eTrack = nTrack.toElement(); if (eTrack.isNull()) continue; // Load track-view state... if (eTrack.tagName() == "view") { for (QDomNode nView = eTrack.firstChild(); !nView.isNull(); nView = nView.nextSibling()) { // Convert state node to element... QDomElement eView = nView.toElement(); if (eView.isNull()) continue; if (eView.tagName() == "pixels-per-beat") qtractorSession::setPixelsPerBeat(eView.text().toUShort()); else if (eView.tagName() == "horizontal-zoom") qtractorSession::setHorizontalZoom(eView.text().toUShort()); else if (eView.tagName() == "vertical-zoom") qtractorSession::setVerticalZoom(eView.text().toUShort()); else if (eView.tagName() == "snap-per-beat") qtractorSession::setSnapPerBeat(eView.text().toUShort()); else if (eView.tagName() == "edit-head" && !bTemplate) qtractorSession::setEditHead(eView.text().toULong()); else if (eView.tagName() == "edit-tail" && !bTemplate) qtractorSession::setEditTail(eView.text().toULong()); } // Again, make view/time scaling factors permanent. qtractorSession::updateTimeScale(); } else // Load track... if (eTrack.tagName() == "track") { qtractorTrack *pTrack = new qtractorTrack(this); if (!pTrack->loadElement(pDocument, &eTrack)) return false; qtractorSession::addTrack(pTrack); } } // Stabilize things a bit... stabilize(); } } // Just stabilize things around. qtractorSession::updateSession(); // Check whether some deferred state needs to be set... if (iLoopStart < iLoopEnd) qtractorSession::setLoop(iLoopStart, iLoopEnd); if (iPunchIn < iPunchOut) qtractorSession::setPunch(iPunchIn, iPunchOut); qtractorSession::unlock(); return true; } bool qtractorSession::saveElement ( Document *pDocument, QDomElement *pElement ) { // Save this program version (informational)... pElement->setAttribute("version", PROJECT_TITLE " " PROJECT_VERSION); // Templates should have no session name... const bool bTemplate = pDocument->isTemplate(); if (!bTemplate) pElement->setAttribute("name", qtractorSession::sessionName()); // Save session properties... QDomElement eProps = pDocument->document()->createElement("properties"); if (!pDocument->isArchive()) { const QString& sSessionDir = QDir().relativeFilePath(qtractorSession::sessionDir()); if (!sSessionDir.isEmpty() && sSessionDir != '.') pDocument->saveTextElement("directory", sSessionDir, &eProps); } pDocument->saveTextElement("description", qtractorSession::description(), &eProps); if (!bTemplate) { pDocument->saveTextElement("sample-rate", QString::number(qtractorSession::sampleRate()), &eProps); } pDocument->saveTextElement("tempo", QString::number(qtractorSession::tempo()), &eProps); pDocument->saveTextElement("ticks-per-beat", QString::number(qtractorSession::ticksPerBeat()), &eProps); pDocument->saveTextElement("beats-per-bar", QString::number(qtractorSession::beatsPerBar()), &eProps); pDocument->saveTextElement("beat-divisor", QString::number(qtractorSession::beatDivisor()), &eProps); pElement->appendChild(eProps); // State and files are not saved when in template mode... if (!bTemplate) { // Save session state... QDomElement eState = pDocument->document()->createElement("state"); pDocument->saveTextElement("loop-start", QString::number(qtractorSession::loopStart()), &eState); pDocument->saveTextElement("loop-end", QString::number(qtractorSession::loopEnd()), &eState); pDocument->saveTextElement("punch-in", QString::number(qtractorSession::punchIn()), &eState); pDocument->saveTextElement("punch-out", QString::number(qtractorSession::punchOut()), &eState); pElement->appendChild(eState); // Save file lists... QDomElement eFiles = pDocument->document()->createElement("files"); // Audio files... QDomElement eAudioList = pDocument->document()->createElement("audio-list"); qtractorAudioListView *pAudioList = nullptr; if (pDocument->files()) pAudioList = pDocument->files()->audioListView(); if (pAudioList == nullptr) return false; if (!pAudioList->saveElement(pDocument, &eAudioList)) return false; eFiles.appendChild(eAudioList); // MIDI files... QDomElement eMidiList = pDocument->document()->createElement("midi-list"); qtractorMidiListView *pMidiList = nullptr; if (pDocument->files()) pMidiList = pDocument->files()->midiListView(); if (pMidiList == nullptr) return false; if (!pMidiList->saveElement(pDocument, &eMidiList)) return false; eFiles.appendChild(eMidiList); pElement->appendChild(eFiles); } // Save device lists... QDomElement eDevices = pDocument->document()->createElement("devices"); // Audio engine... QDomElement eAudioEngine = pDocument->document()->createElement("audio-engine"); if (!qtractorSession::audioEngine()->saveElement(pDocument, &eAudioEngine)) return false; eDevices.appendChild(eAudioEngine); // MIDI engine... QDomElement eMidiEngine = pDocument->document()->createElement("midi-engine"); if (!qtractorSession::midiEngine()->saveElement(pDocument, &eMidiEngine)) return false; eDevices.appendChild(eMidiEngine); pElement->appendChild(eDevices); // Save tempo/time-signature, if any... qtractorTimeScale::Node *pNode = qtractorSession::timeScale()->nodes().first(); if (pNode) pNode = pNode->next(); // Skip first anchor node. if (pNode) { QDomElement eTempoMap = pDocument->document()->createElement("tempo-map"); while (pNode) { QDomElement eNode = pDocument->document()->createElement("tempo-node"); eNode.setAttribute("bar", QString::number(pNode->bar)); eNode.setAttribute("frame", QString::number(pNode->frame)); pDocument->saveTextElement("tempo", QString::number(pNode->tempo), &eNode); pDocument->saveTextElement("beat-type", QString::number(pNode->beatType), &eNode); pDocument->saveTextElement("beats-per-bar", QString::number(pNode->beatsPerBar), &eNode); pDocument->saveTextElement("beat-divisor", QString::number(pNode->beatDivisor), &eNode); eTempoMap.appendChild(eNode); pNode = pNode->next(); } pElement->appendChild(eTempoMap); } // Save location markers/key-signatures, if any... qtractorTimeScale::Marker *pMarker = qtractorSession::timeScale()->markers().first(); if (pMarker) { QDomElement eMarkers = pDocument->document()->createElement("markers"); while (pMarker) { QDomElement eMarker = pDocument->document()->createElement("marker"); eMarker.setAttribute("frame", QString::number(pMarker->frame)); if (!pMarker->text.isEmpty()) { pDocument->saveTextElement("text", pMarker->text, &eMarker); pDocument->saveTextElement("color", pMarker->color.name(), &eMarker); } if (qtractorTimeScale::isKeySignature( pMarker->accidentals, pMarker->mode)) { pDocument->saveTextElement("accidentals", QString::number(pMarker->accidentals), &eMarker); pDocument->saveTextElement("mode", QString::number(pMarker->mode), &eMarker); } eMarkers.appendChild(eMarker); pMarker = pMarker->next(); } pElement->appendChild(eMarkers); } // Save track view state... QDomElement eTracks = pDocument->document()->createElement("tracks"); QDomElement eView = pDocument->document()->createElement("view"); pDocument->saveTextElement("pixels-per-beat", QString::number(qtractorSession::pixelsPerBeat()), &eView); pDocument->saveTextElement("horizontal-zoom", QString::number(qtractorSession::horizontalZoom()), &eView); pDocument->saveTextElement("vertical-zoom", QString::number(qtractorSession::verticalZoom()), &eView); pDocument->saveTextElement("snap-per-beat", QString::number(qtractorSession::snapPerBeat()), &eView); if (!bTemplate) { pDocument->saveTextElement("edit-head", QString::number(qtractorSession::editHead()), &eView); pDocument->saveTextElement("edit-tail", QString::number(qtractorSession::editTail()), &eView); } eTracks.appendChild(eView); // Save session tracks... for (qtractorTrack *pTrack = qtractorSession::tracks().first(); pTrack; pTrack = pTrack->next()) { // Create the new track element... QDomElement eTrack = pDocument->document()->createElement("track"); if (!pTrack->saveElement(pDocument, &eTrack)) return false; // Add this slot... eTracks.appendChild(eTrack); } pElement->appendChild(eTracks); return true; } // Rename session files... void qtractorSession::renameSession ( const QString& sOldName, const QString& sNewName ) { // Do nothing if names are obviously the same... if (sOldName == sNewName) return; qtractorFiles *pFiles = nullptr; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pFiles = pMainForm->files(); if (pFiles == nullptr) return; // Lock it up... lock(); const QRegularExpression rx('^' + sOldName); // For each and every track and clip... QStringList paths; for (qtractorTrack *pTrack = m_tracks.first(); pTrack; pTrack = pTrack->next()) { // Refer to a proper files-view... qtractorFileListView *pFileListView = nullptr; qtractorFileList::Type iFileType = qtractorFileList::Audio; if (pTrack->trackType() == qtractorTrack::Midi) { pFileListView = pFiles->midiListView(); iFileType = qtractorFileList::Midi; } else { pFileListView = pFiles->audioListView(); // iFileType = qtractorFileList::Audio; } // For each and every elligible clip... for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { // Rename clip filename prefix... const QFileInfo info1(pClip->filename()); QString sFileName = info1.fileName(); if (!sFileName.contains(rx)) continue; sFileName.replace(rx, sNewName); QFileInfo info2(info1.dir(), sFileName); const QString& sOldFilePath = info1.absoluteFilePath(); if (!paths.contains(sOldFilePath)) { paths.append(sOldFilePath); // Increment filename suffix if exists already... if (info2.exists()) { int iFileNo = 0; sFileName = info2.completeBaseName(); QRegularExpression rxFileNo("([0-9]+)$"); QRegularExpressionMatch match = rxFileNo.match(sFileName); if (match.hasMatch()) { iFileNo = match.captured(1).toInt(); sFileName.remove(rxFileNo); } else sFileName += '-'; sFileName += "%1." + info1.suffix(); do { info2.setFile(info1.dir(), sFileName.arg(++iFileNo)); } while (info2.exists()); } } const QString& sNewFilePath = info2.absoluteFilePath(); if (!paths.contains(sNewFilePath)) { paths.append(sNewFilePath); if (!QFile::rename(sOldFilePath, sNewFilePath)) continue; #ifdef CONFIG_DEBUG qDebug("qtractorSession::renameSession: \"%s\" -> \"%s\"", info1.fileName().toUtf8().constData(), info2.fileName().toUtf8().constData()); #endif } // Re-hash clip filenames... if (iFileType == qtractorFileList::Midi) { qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) pMidiClip->setFilenameEx(sNewFilePath, true); } else { pClip->close(); pClip->setFilename(sNewFilePath); pClip->open(); } // Manage files-view item... if (pFileListView) { qtractorFileGroupItem *pGroupItem = nullptr; qtractorFileListItem *pFileItem = pFileListView->findFileItem(sOldFilePath); if (pFileItem) { pGroupItem = pFileItem->groupItem(); m_pFiles->removeFileItem(iFileType, pFileItem->path()); delete pFileItem; } pFileListView->addFileItem(sNewFilePath, pGroupItem); } } } // If session name has changed, we'll prompt // for correct filename when save is triggered... pMainForm->clearFilename(); // Done. unlock(); } //------------------------------------------------------------------------- // qtractorSession::Document -- Session file import/export helper class. // // Constructor. qtractorSession::Document::Document ( QDomDocument *pDocument, qtractorSession *pSession, qtractorFiles *pFiles ) : qtractorDocument(pDocument, "session") { m_pSession = pSession; m_pFiles = pFiles; } // Default destructor. qtractorSession::Document::~Document (void) { } // Session accessor. qtractorSession *qtractorSession::Document::session (void) const { return m_pSession; } // File list accessor. qtractorFiles *qtractorSession::Document::files (void) const { return m_pFiles; } // The elemental loader implementation. bool qtractorSession::Document::loadElement ( QDomElement *pElement ) { return m_pSession->loadElement(this, pElement); } // The elemental saver implementation. bool qtractorSession::Document::saveElement ( QDomElement *pElement ) { return m_pSession->saveElement(this, pElement); } // end of qtractorSession.cpp qtractor-1.5.11/src/PaxHeaders/qtractorInsertPlugin.cpp0000644000000000000000000000013215124701674020221 xustar0030 mtime=1767080892.785263487 30 atime=1767080892.785263487 30 ctime=1767080892.785263487 qtractor-1.5.11/src/qtractorInsertPlugin.cpp0000644000175000001440000013726015124701674020222 0ustar00rncbcusers// qtractorInsertPlugin.cpp // /**************************************************************************** Copyright (C) 2005-2025, rncbc aka Rui Nuno Capela. All rights reserved. Copyright (C) 2011, Holger Dehnhardt. 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 "qtractorAbout.h" #include "qtractorInsertPlugin.h" #include "qtractorSession.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorMidiManager.h" #include "qtractorPluginListView.h" #if defined(__SSE__) #include // SSE detection. static inline bool sse_enabled (void) { #if defined(__GNUC__) unsigned int eax, ebx, ecx, edx; #if defined(__x86_64__) || (!defined(PIC) && !defined(__PIC__)) __asm__ __volatile__ ( "cpuid\n\t" \ : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) \ : "a" (1) : "cc"); #else __asm__ __volatile__ ( "push %%ebx\n\t" \ "cpuid\n\t" \ "movl %%ebx,%1\n\t" \ "pop %%ebx\n\t" \ : "=a" (eax), "=r" (ebx), "=c" (ecx), "=d" (edx) \ : "a" (1) : "cc"); #endif return (edx & (1 << 25)); #else return false; #endif } // SSE enabled processor versions. static inline void sse_process_gain ( float **ppFrames, unsigned int iFrames, unsigned int iOffset, unsigned short iChannels, float fGain ) { __m128 v0 = _mm_load_ps1(&fGain); for (unsigned short i = 0; i < iChannels; ++i) { float *pFrames = ppFrames[i] + iOffset; unsigned int nframes = iFrames; for (; (long(pFrames) & 15) && (nframes > 0); --nframes) *pFrames++ *= fGain; for (; nframes >= 4; nframes -= 4) { _mm_store_ps(pFrames, _mm_mul_ps( _mm_loadu_ps(pFrames), v0 ) ); pFrames += 4; } for (; nframes > 0; --nframes) *pFrames++ *= fGain; } } static inline void sse_process_dry_wet ( float **ppBuffer, float **ppFrames, unsigned int iFrames, unsigned short iChannels, float fDry, float fWet ) { __m128 v0 = _mm_load_ps1(&fDry); __m128 v1 = _mm_load_ps1(&fWet); for (unsigned short i = 0; i < iChannels; ++i) { float *pBuffer = ppBuffer[i]; float *pFrames = ppFrames[i]; unsigned int nframes = iFrames; for (; (long(pBuffer) & 15) && (nframes > 0); --nframes) { *pBuffer *= fWet; *pBuffer++ += fDry * *pFrames++; } for (; nframes >= 4; nframes -= 4) { _mm_store_ps(pBuffer, _mm_mul_ps( _mm_loadu_ps(pBuffer), v1 ) ); _mm_store_ps(pBuffer, _mm_add_ps( _mm_loadu_ps(pBuffer), _mm_mul_ps( _mm_loadu_ps(pFrames), v0) ) ); pFrames += 4; pBuffer += 4; } for (; nframes > 0; --nframes) { *pBuffer *= fWet; *pBuffer++ += fDry * *pFrames++; } } } static inline void sse_process_add ( float **ppBuffer, float **ppFrames, unsigned int iFrames, unsigned int iOffset, unsigned short iChannels, float fGain ) { __m128 v0 = _mm_load_ps1(&fGain); for (unsigned short i = 0; i < iChannels; ++i) { float *pBuffer = ppBuffer[i]; if (pBuffer) { pBuffer += iOffset; float *pFrames = ppFrames[i]; unsigned int nframes = iFrames; for (; (long(pBuffer) & 15) && (nframes > 0); --nframes) *pBuffer++ += fGain * *pFrames++; for (; nframes >= 4; nframes -= 4) { _mm_store_ps(pBuffer, _mm_add_ps( _mm_loadu_ps(pBuffer), _mm_mul_ps( _mm_loadu_ps(pFrames), v0) ) ); pFrames += 4; pBuffer += 4; } for (; nframes > 0; --nframes) *pBuffer++ += fGain * *pFrames++; } } } #endif // __SSE__ #if defined(__ARM_NEON__) #include "arm_neon.h" // NEON enabled processor versions. static inline void neon_process_gain ( float **ppFrames, unsigned int iFrames, unsigned int iOffset, unsigned short iChannels, float fGain ) { float32x4_t vGain = vdupq_n_f32(fGain); for (unsigned short i = 0; i < iChannels; ++i) { float *pFrames = ppFrames[i] + iOffset; unsigned int nframes = iFrames; for (; (long(pFrames) & 15) && (nframes > 0); --nframes) *pFrames++ *= fGain; for (; nframes >= 4; nframes -= 4) { vst1q_f32(pFrames, vmulq_f32( vld1q_f32(pFrames), vGain ) ); pFrames += 4; } for (; nframes > 0; --nframes) *pFrames++ *= fGain; } } static inline void neon_process_dry_wet ( float **ppBuffer, float **ppFrames, unsigned int iFrames, unsigned short iChannels, float fDry, float fWet ) { float32x4_t vDry = vdupq_n_f32(fDry); float32x4_t vWet = vdupq_n_f32(fWet); for (unsigned short i = 0; i < iChannels; ++i) { float *pBuffer = ppBuffer[i]; float *pFrames = ppFrames[i]; unsigned int nframes = iFrames; for (; (long(pBuffer) & 15) && (nframes > 0); --nframes) { *pBuffer *= fWet; *pBuffer++ += fDry * *pFrames++; } for (; nframes >= 4; nframes -= 4) { float32x4_t vBuffer = vld1q_f32(pBuffer); vBuffer = vmulq_f32(vBuffer, vWet); float32x4_t vFrames = vld1q_f32(pFrames); // Vr[i] := Va[i] + Vb[i] * Vc[i] vBuffer = vmlaq_f32(vBuffer, vDry, vFrames); vst1q_f32(pBuffer, vBuffer); pFrames += 4; pBuffer += 4; } for (; nframes > 0; --nframes) { *pBuffer *= fWet; *pBuffer++ += fDry * *pFrames++; } } } static inline void neon_process_add ( float **ppBuffer, float **ppFrames, unsigned int iFrames, unsigned int iOffset, unsigned short iChannels, float fGain ) { float32x4_t vGain = vdupq_n_f32(fGain); for (unsigned short i = 0; i < iChannels; ++i) { float *pBuffer = ppBuffer[i]; if (pBuffer) { pBuffer += iOffset; float *pFrames = ppFrames[i]; unsigned int nframes = iFrames; for (; (long(pBuffer) & 15) && (nframes > 0); --nframes) *pBuffer++ += fGain * *pFrames++; for (; nframes >= 4; nframes -= 4) { float32x4_t vBuffer = vld1q_f32(pBuffer); float32x4_t vFrames = vld1q_f32(pFrames); //Vr[i] := Va[i] + Vb[i] * Vc[i] vBuffer = vmlaq_f32(vBuffer, vGain, vFrames); vst1q_f32(pBuffer, vBuffer); pFrames += 4; pBuffer += 4; } for (; nframes > 0; --nframes) *pBuffer++ += fGain * *pFrames++; } } } #endif // __ARM_NEON__ // Standard processor versions. static inline void std_process_gain ( float **ppFrames, unsigned int iFrames, unsigned int iOffset, unsigned short iChannels, float fGain ) { for (unsigned short i = 0; i < iChannels; ++i) { float *pFrames = ppFrames[i] + iOffset; for (unsigned int n = 0; n < iFrames; ++n) *pFrames++ *= fGain; } } static inline void std_process_dry_wet ( float **ppBuffer, float **ppFrames, unsigned int iFrames, unsigned short iChannels, float fDry, float fWet ) { for (unsigned short i = 0; i < iChannels; ++i) { float *pBuffer = ppBuffer[i]; float *pFrames = ppFrames[i]; for (unsigned int n = 0; n < iFrames; ++n) { *pBuffer *= fWet; *pBuffer++ += fDry * *pFrames++; } } } static inline void std_process_add ( float **ppBuffer, float **ppFrames, unsigned int iFrames, unsigned int iOffset, unsigned short iChannels, float fGain ) { for (unsigned short i = 0; i < iChannels; ++i) { float *pBuffer = ppBuffer[i]; if (pBuffer) { pBuffer += iOffset; float *pFrames = ppFrames[i]; for (unsigned int n = 0; n < iFrames; ++n) *pBuffer++ += fGain * *pFrames++; } } } //---------------------------------------------------------------------------- // qtractorInsertPluginType -- Insert pseudo-plugin type impl. // // Factory method (static) qtractorPlugin *qtractorInsertPluginType::createPlugin ( qtractorPluginList *pList, unsigned short iChannels ) { // Check whether it's a valid insert pseudo-plugin... qtractorPlugin *pPlugin = nullptr; qtractorInsertPluginType *pInsertType = nullptr; if (iChannels > 0) { pInsertType = new qtractorAudioInsertPluginType(iChannels); if (pInsertType->open()) pPlugin = new qtractorAudioInsertPlugin(pList, pInsertType); } else { pInsertType = new qtractorMidiInsertPluginType(); if (pInsertType->open()) pPlugin = new qtractorMidiInsertPlugin(pList, pInsertType); } if (pPlugin == nullptr && pInsertType) delete pInsertType; return pPlugin; } //---------------------------------------------------------------------------- // qtractorAudioInsertPluginType -- Audio-insert pseudo-plugin type instance. // // Derived methods. bool qtractorAudioInsertPluginType::open (void) { // Sanity check... const unsigned short iChannels = index(); if (iChannels < 1) return false; #ifdef CONFIG_DEBUG qDebug("qtractorAudioInsertPluginType[%p]::open() channels=%u", this, iChannels); #endif // Pseudo-plugin type names. m_sName = "Insert (Audio)"; m_sLabel = "AudioInsert"; // m_sLabel.remove(' '); // Pseudo-plugin unique identifier. m_iUniqueID = qHash(m_sLabel) ^ qHash(iChannels); // Pseudo-plugin port counts... m_iControlIns = 4; m_iControlOuts = 0; m_iAudioIns = iChannels; m_iAudioOuts = iChannels; m_iMidiIns = 0; m_iMidiOuts = 0; // Cache flags. m_bRealtime = true; m_bConfigure = true; // Done. return true; } void qtractorAudioInsertPluginType::close (void) { } // Instance cached-deferred accessors. const QString& qtractorAudioInsertPluginType::aboutText (void) { if (m_sAboutText.isEmpty()) { m_sAboutText += QObject::tr("Insert Send/Return pseudo-plugin (Audio)"); m_sAboutText += '\n'; m_sAboutText += QTRACTOR_WEBSITE; m_sAboutText += '\n'; m_sAboutText += QTRACTOR_COPYRIGHT; } return m_sAboutText; } //---------------------------------------------------------------------------- // qtractorMidiInsertPluginType -- MIDI-insert pseudo-plugin type instance. // // Derived methods. bool qtractorMidiInsertPluginType::open (void) { // Sanity check... const unsigned short iChannels = index(); if (iChannels > 0) return false; #ifdef CONFIG_DEBUG qDebug("qtractorMidiInsertPluginType[%p]::open() channels=%u", this, iChannels); #endif // Pseudo-plugin type names. m_sName = "Insert (MIDI)"; m_sLabel = "MidiInsert"; // m_sLabel.remove(' '); // Pseudo-plugin unique identifier. m_iUniqueID = qHash(m_sLabel);//^ qHash(iChannels); // Pseudo-plugin port counts... m_iControlIns = 3; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; m_iMidiIns = 1; m_iMidiOuts = 1; // Cache flags. m_bRealtime = true; m_bConfigure = true; // Done. return true; } void qtractorMidiInsertPluginType::close (void) { } // Instance cached-deferred accessors. const QString& qtractorMidiInsertPluginType::aboutText (void) { if (m_sAboutText.isEmpty()) { m_sAboutText += QObject::tr("Insert Send/Return pseudo-plugin (MIDI)"); m_sAboutText += '\n'; m_sAboutText += QTRACTOR_WEBSITE; m_sAboutText += '\n'; m_sAboutText += QTRACTOR_COPYRIGHT; } return m_sAboutText; } //---------------------------------------------------------------------------- // qtractorAudioInsertPlugin -- Audio-insert pseudo-plugin instance. // // Constructors. qtractorAudioInsertPlugin::qtractorAudioInsertPlugin ( qtractorPluginList *pList, qtractorInsertPluginType *pInsertType ) : qtractorInsertPlugin(pList, pInsertType), m_pAudioBus(nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractorAudioInsertPlugin[%p] channels=%u", this, pInsertType->channels()); #endif // Custom optimized processors. #if defined(__SSE__) if (sse_enabled()) { m_pfnProcessGain = sse_process_gain; m_pfnProcessDryWet = sse_process_dry_wet; } else #endif #if defined(__ARM_NEON__) m_pfnProcessGain = neon_process_gain; m_pfnProcessDryWet = neon_process_dry_wet; if (false) #endif { m_pfnProcessGain = std_process_gain; m_pfnProcessDryWet = std_process_dry_wet; } // Create and attach the custom parameters... m_pSendGainParam = new Param(this, 0); m_pSendGainParam->setName(QObject::tr("Send Gain")); m_pSendGainParam->setMinValue(0.0f); m_pSendGainParam->setMaxValue(4.0f); m_pSendGainParam->setDefaultValue(1.0f); m_pSendGainParam->setValue(1.0f, false); addParam(m_pSendGainParam); m_pDryGainParam = new Param(this, 1); m_pDryGainParam->setName(QObject::tr("Dry Gain")); m_pDryGainParam->setMinValue(0.0f); m_pDryGainParam->setMaxValue(4.0f); m_pDryGainParam->setDefaultValue(1.0f); m_pDryGainParam->setValue(1.0f, false); addParam(m_pDryGainParam); m_pWetGainParam = new Param(this, 2); m_pWetGainParam->setName(QObject::tr("Wet Gain")); m_pWetGainParam->setMinValue(0.0f); m_pWetGainParam->setMaxValue(4.0f); m_pWetGainParam->setDefaultValue(1.0f); m_pWetGainParam->setValue(1.0f, false); addParam(m_pWetGainParam); // Latency param applies to tracks only... m_pLatencyParam = nullptr; m_fLatencyValue = 0.0f; // Setup plugin instance... //setChannels(channels()); } // Destructor. qtractorAudioInsertPlugin::~qtractorAudioInsertPlugin (void) { // Cleanup plugin instance... setChannels(0); } // Channel/instance number accessors. void qtractorAudioInsertPlugin::setChannels ( unsigned short iChannels ) { // Check our type... qtractorPluginType *pType = type(); if (pType == nullptr) return; // We'll need this globals... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; // Estimate the (new) number of instances... const unsigned short iInstances = pType->instances(iChannels, list()->isMidi()); // Now see if instance and channel count changed anyhow... if (iInstances == instances() && iChannels == channels()) return; // Gotta go for a while... const bool bActivated = isActivated(); setChannelsActivated(iChannels, false); // Cleanup bus... if (m_pAudioBus) { pAudioEngine->removeBusEx(m_pAudioBus); m_pAudioBus->close(); delete m_pAudioBus; m_pAudioBus = nullptr; } // Latency param is recreated here, on tracks only... if (m_pLatencyParam) { m_fLatencyValue = m_pLatencyParam->value(); // Save! removeParam(m_pLatencyParam); delete m_pLatencyParam; m_pLatencyParam = nullptr; } // Set new instance number... setInstances(iInstances); if (iInstances < 1) { // setChannelsActivated(iChannels, bActivated); return; } #ifdef CONFIG_DEBUG qDebug("qtractorAudioInsertPlugin[%p]::setChannels(%u) instances=%u", this, iChannels, iInstances); #endif // Latency param is also recreated here, but only for tracks... const unsigned int iFlags = list()->flags(); if ((iFlags & qtractorPluginList::Bus) == 0) { m_pLatencyParam = new LatencyParam(this, 3); m_pLatencyParam->setName(QObject::tr("Latency (frames)")); m_pLatencyParam->setMinValue(0.0f); m_pLatencyParam->setMaxValue(float(pAudioEngine->sampleRate() << 1)); m_pLatencyParam->setDefaultValue(m_fLatencyValue); // Restore! m_pLatencyParam->setValue(m_fLatencyValue, false); // addParam(m_pLatencyParam); } // Audio bus name -- it must be unique... int iBusName = 1; const QString sBusNamePrefix("Insert_%1"); QString sBusName = label(); // HACK: take previously saved label. if (sBusName.isEmpty() || sBusName == pType->label()) sBusName = sBusNamePrefix.arg(iBusName); while (pAudioEngine->findBus(sBusName) || pAudioEngine->findBusEx(sBusName)) sBusName = sBusNamePrefix.arg(++iBusName); setLabel(sBusName); // HACK: remember this label. // Create the private audio bus... m_pAudioBus = new qtractorAudioBus(pAudioEngine, sBusName, qtractorBus::BusMode(qtractorBus::Duplex | qtractorBus::Ex), false, iChannels); // Add this one to the engine's exo-bus list, // for conection persistence purposes... pAudioEngine->addBusEx(m_pAudioBus); // (Re)issue all configuration as needed... realizeConfigs(); realizeValues(); // But won't need it anymore. releaseConfigs(); releaseValues(); // Open-up private bus... m_pAudioBus->open(); // (Re)activate instance if necessary... setChannelsActivated(iChannels, bActivated); } // Do the actual activation. void qtractorAudioInsertPlugin::activate (void) { list()->setAudioInsertActivated(true); } // Do the actual deactivation. void qtractorAudioInsertPlugin::deactivate (void) { list()->setAudioInsertActivated(false); } // The main plugin processing procedure. void qtractorAudioInsertPlugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { if (m_pAudioBus == nullptr) return; if (!m_pAudioBus->isEnabled()) return; // m_pAudioBus->process_prepare(nframes); qtractorAudioEngine *pAudioEngine = static_cast (m_pAudioBus->engine()); if (pAudioEngine == nullptr) return; const unsigned int iOffset = pAudioEngine->bufferOffset(); const unsigned int nbytes = nframes * sizeof(float); float **ppOut = m_pAudioBus->out(); // Sends. float **ppIn = m_pAudioBus->in(); // Returns. const unsigned short iChannels = channels(); for (unsigned short i = 0; i < iChannels; ++i) { ::memcpy(ppOut[i] + iOffset, ppIBuffer[i], nbytes); ::memcpy(ppOBuffer[i], ppIn[i] + iOffset, nbytes); } const float fGain = m_pSendGainParam->value(); (*m_pfnProcessGain)(ppOut, nframes, iOffset, iChannels, fGain); const float fDry = m_pDryGainParam->value(); const float fWet = m_pWetGainParam->value(); (*m_pfnProcessDryWet)(ppOBuffer, ppIBuffer, nframes, iChannels, fDry, fWet); // m_pAudioBus->process_commit(nframes); } // Pseudo-plugin configuration handlers. void qtractorAudioInsertPlugin::configure ( const QString& sKey, const QString& sValue ) { if (m_pAudioBus == nullptr) return; qtractorBus::ConnectItem *pItem = new qtractorBus::ConnectItem; pItem->index = sValue.section('|', 0, 0).toUShort(); const QString& sClient = sValue.section('|', 1, 1); const QString& sClientName = sClient.section(':', 1); if (sClientName.isEmpty()) { pItem->clientName = sClient; } else { // pItem->client = sClient.section(':', 0, 0).toInt(); pItem->clientName = sClientName; } const QString& sPort = sValue.section('|', 2, 2); const QString& sPortName = sPort.section(':', 1); if (sPortName.isEmpty()) { pItem->portName = sPort; } else { // pItem->port = sPort.section(':', 0, 0).toInt(); pItem->portName = sPortName; } const QString& sKeyPrefix = sKey.section('_', 0, 0); if (sKeyPrefix == "in") m_pAudioBus->inputs().append(pItem); else if (sKeyPrefix == "out") m_pAudioBus->outputs().append(pItem); else delete pItem; } // Pseudo-plugin configuration/state snapshot. void qtractorAudioInsertPlugin::freezeConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorAudioInsertPlugin[%p]::freezeConfigs()", this); #endif clearConfigs(); freezeConfigs(qtractorBus::Input); freezeConfigs(qtractorBus::Output); } void qtractorAudioInsertPlugin::releaseConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorAudioInsertPlugin[%p]::releaseConfigs()", this); #endif qtractorPlugin::clearConfigs(); } void qtractorAudioInsertPlugin::freezeConfigs ( int iBusMode ) { if (m_pAudioBus == nullptr) return; // Save connect items... qtractorBus::BusMode busMode = qtractorBus::BusMode(iBusMode); const QString sKeyPrefix(busMode & qtractorBus::Input ? "in" : "out"); int iKey = 0; qtractorBus::ConnectList connects; m_pAudioBus->updateConnects(busMode, connects); QListIterator iter(connects); while (iter.hasNext()) { qtractorBus::ConnectItem *pItem = iter.next(); QString sIndex = QString::number(pItem->index); QString sClient; if (pItem->client >= 0) sClient += QString::number(pItem->client) + ':'; sClient += pItem->clientName; QString sPort; if (pItem->port >= 0) sPort += QString::number(pItem->port) + ':'; sPort += pItem->portName; QString sKey = sKeyPrefix + '_' + QString::number(iKey++); setConfig(sKey, sIndex + '|' + sClient + '|' + sPort); } } // Audio specific accessor. qtractorAudioBus *qtractorAudioInsertPlugin::audioBus (void) const { return m_pAudioBus; } // Override title/name caption. QString qtractorAudioInsertPlugin::title (void) const { QString sTitle; if (m_pAudioBus && qtractorPlugin::alias().isEmpty()) { sTitle = QObject::tr("%1 (Audio)").arg(m_pAudioBus->busName()); sTitle.replace('_', ' '); } else { sTitle = qtractorPlugin::title(); } return sTitle; } // Report latency. unsigned long qtractorAudioInsertPlugin::latency (void) const { if (m_pLatencyParam) return (unsigned long) m_pLatencyParam->value(); else return 0; } // Display latency in milliseconds (ms) QString qtractorAudioInsertPlugin::LatencyParam::display (void) const { QString sDisplay; // Maximum latency is supposed to be settled to 2secs. const float fSampleRate = 0.5f * Param::maxValue(); if (fSampleRate > 0.0f) { // Latency in millisecs. const float fLatency = (1000.0f * Param::value()) / fSampleRate; if (fLatency > 0.0f) { const int iDecimals = (fLatency < 1.0f ? 3 : (fLatency < 100.0f ? 1 : 0)); sDisplay = QObject::tr("%1 ms") .arg(QString::number(fLatency, 'f', iDecimals)); } } return sDisplay; } //---------------------------------------------------------------------------- // qtractorMidiInsertPlugin -- MIDI-insert pseudo-plugin instance. // // Constructors. qtractorMidiInsertPlugin::qtractorMidiInsertPlugin ( qtractorPluginList *pList, qtractorInsertPluginType *pInsertType ) : qtractorInsertPlugin(pList, pInsertType), m_pMidiBus(nullptr), m_pMidiInputBuffer(nullptr), m_pMidiOutputBuffer(nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiInsertPlugin[%p] channels=%u", this, pInsertType->channels()); #endif // Create and attach the custom parameters... m_pSendGainParam = new Param(this, 0); m_pSendGainParam->setName(QObject::tr("Send Gain")); m_pSendGainParam->setMinValue(0.0f); m_pSendGainParam->setMaxValue(4.0f); m_pSendGainParam->setDefaultValue(1.0f); m_pSendGainParam->setValue(1.0f, false); addParam(m_pSendGainParam); m_pDryGainParam = new Param(this, 1); m_pDryGainParam->setName(QObject::tr("Dry Gain")); m_pDryGainParam->setMinValue(0.0f); m_pDryGainParam->setMaxValue(4.0f); m_pDryGainParam->setDefaultValue(1.0f); m_pDryGainParam->setValue(1.0f, false); addParam(m_pDryGainParam); m_pWetGainParam = new Param(this, 2); m_pWetGainParam->setName(QObject::tr("Wet Gain")); m_pWetGainParam->setMinValue(0.0f); m_pWetGainParam->setMaxValue(4.0f); m_pWetGainParam->setDefaultValue(1.0f); m_pWetGainParam->setValue(1.0f, false); addParam(m_pWetGainParam); // Setup plugin instance... //setChannels(channels()); } // Destructor. qtractorMidiInsertPlugin::~qtractorMidiInsertPlugin (void) { // Cleanup plugin instance... setChannels(0); } // Channel/instance number accessors. void qtractorMidiInsertPlugin::setChannels ( unsigned short iChannels ) { // Check our type... qtractorPluginType *pType = type(); if (pType == nullptr) return; // We'll need this globals... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; // Estimate the (new) number of instances... const unsigned short iInstances = pType->instances(iChannels, list()->isMidi()); // Now see if instance and channel count changed anyhow... if (iInstances == instances() && iChannels == channels()) return; // Gotta go for a while... const bool bActivated = isActivated(); setChannelsActivated(iChannels, false); // Cleanup buffers... if (m_pMidiInputBuffer) { if (m_pMidiBus) pMidiEngine->removeInputBuffer(m_pMidiBus->alsaPort()); delete m_pMidiInputBuffer; m_pMidiInputBuffer = nullptr; } if (m_pMidiOutputBuffer) { delete m_pMidiOutputBuffer; m_pMidiOutputBuffer = nullptr; } // Cleanup bus... if (m_pMidiBus) { pMidiEngine->removeBusEx(m_pMidiBus); m_pMidiBus->close(); delete m_pMidiBus; m_pMidiBus = nullptr; } // Set new instance number... setInstances(iInstances); if (iInstances < 1) { // setChannelsActivated(iChannels, bActivated); return; } #ifdef CONFIG_DEBUG qDebug("qtractorMidiInsertPlugin[%p]::setChannels(%u) instances=%u", this, iChannels, iInstances); #endif // MIDI bus name -- it must be unique... int iBusName = 1; const QString sBusNamePrefix("Insert_%1"); QString sBusName = label(); // HACK: take previously saved label. if (sBusName.isEmpty() || sBusName == pType->label()) sBusName = sBusNamePrefix.arg(iBusName); while (pMidiEngine->findBus(sBusName) || pMidiEngine->findBusEx(sBusName)) sBusName = sBusNamePrefix.arg(++iBusName); setLabel(sBusName); // HACK: remember this label. // Create the private audio bus... m_pMidiBus = new qtractorMidiBus(pMidiEngine, sBusName, qtractorBus::BusMode(qtractorBus::Duplex | qtractorBus::Ex), false); // Create the private MIDI buffers... m_pMidiInputBuffer = new qtractorMidiInputBuffer(); m_pMidiInputBuffer->setDryGainSubject(m_pDryGainParam->subject()); m_pMidiInputBuffer->setWetGainSubject(m_pWetGainParam->subject()); m_pMidiOutputBuffer = new qtractorMidiOutputBuffer(m_pMidiBus); m_pMidiOutputBuffer->setGainSubject(m_pSendGainParam->subject()); // Add this one to the engine's exo-bus list, // for conection persistence purposes... pMidiEngine->addBusEx(m_pMidiBus); // (Re)issue all configuration as needed... realizeConfigs(); realizeValues(); // But won't need it anymore. releaseConfigs(); releaseValues(); // Open-up private bus... if (m_pMidiBus->open()) pMidiEngine->addInputBuffer(m_pMidiBus->alsaPort(), m_pMidiInputBuffer); // (Re)activate instance if necessary... setChannelsActivated(iChannels, bActivated); } // Do the actual activation. void qtractorMidiInsertPlugin::activate (void) { } // Do the actual deactivation. void qtractorMidiInsertPlugin::deactivate (void) { if (m_pMidiInputBuffer) m_pMidiInputBuffer->clear(); if (m_pMidiOutputBuffer) m_pMidiOutputBuffer->clear(); } // The main plugin processing procedure. void qtractorMidiInsertPlugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { qtractorSession *pSession = qtractorSession::getInstance(); qtractorMidiManager *pMidiManager = list()->midiManager(); if (pMidiManager && pSession) { const unsigned long t0 = (pSession->isPlaying() ? pSession->playHead() : 0); // Enqueue input events into sends/output bus... if (m_pMidiOutputBuffer) { qtractorMidiBuffer *pEventBuffer = pMidiManager->buffer_in(); const unsigned int iEventCount = pEventBuffer->count(); for (unsigned int i = 0; i < iEventCount; ++i) { snd_seq_event_t *pEv = pEventBuffer->at(i); if (!m_pMidiOutputBuffer->enqueue(pEv, t0 + pEv->time.tick)) break; } // Wake the asynchronous working thread... if (iEventCount > 0) qtractorMidiSyncItem::syncItem(m_pMidiOutputBuffer); } // Merge events from returns/input events... if (m_pMidiInputBuffer) pMidiManager->processInputBuffer(m_pMidiInputBuffer, t0); } qtractorInsertPlugin::process(ppIBuffer, ppOBuffer, nframes); } // Pseudo-plugin configuration handlers. void qtractorMidiInsertPlugin::configure ( const QString& sKey, const QString& sValue ) { if (m_pMidiBus == nullptr) return; qtractorBus::ConnectItem *pItem = new qtractorBus::ConnectItem; pItem->index = sValue.section('|', 0, 0).toUShort(); const QString& sClient = sValue.section('|', 1, 1); const QString& sClientName = sClient.section(':', 1); if (sClientName.isEmpty()) { pItem->clientName = sClient; } else { pItem->client = sClient.section(':', 0, 0).toInt(); pItem->clientName = sClientName; } const QString& sPort = sValue.section('|', 2, 2); const QString& sPortName = sPort.section(':', 1); if (sPortName.isEmpty()) { pItem->portName = sPort; } else { pItem->port = sPort.section(':', 0, 0).toInt(); pItem->portName = sPortName; } const QString& sKeyPrefix = sKey.section('_', 0, 0); if (sKeyPrefix == "in") m_pMidiBus->inputs().append(pItem); else if (sKeyPrefix == "out") m_pMidiBus->outputs().append(pItem); else delete pItem; } // Pseudo-plugin configuration/state snapshot. void qtractorMidiInsertPlugin::freezeConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiInsertPlugin[%p]::freezeConfigs()", this); #endif clearConfigs(); freezeConfigs(qtractorBus::Input); freezeConfigs(qtractorBus::Output); } void qtractorMidiInsertPlugin::releaseConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiInsertPlugin[%p]::releaseConfigs()", this); #endif qtractorPlugin::clearConfigs(); } void qtractorMidiInsertPlugin::freezeConfigs ( int iBusMode ) { if (m_pMidiBus == nullptr) return; // Save connect items... qtractorBus::BusMode busMode = qtractorBus::BusMode(iBusMode); const QString sKeyPrefix(busMode & qtractorBus::Input ? "in" : "out"); int iKey = 0; qtractorBus::ConnectList connects; m_pMidiBus->updateConnects(busMode, connects); QListIterator iter(connects); while (iter.hasNext()) { qtractorBus::ConnectItem *pItem = iter.next(); QString sIndex = QString::number(pItem->index); QString sClient; if (pItem->client >= 0) sClient += QString::number(pItem->client) + ':'; sClient += pItem->clientName; QString sPort; if (pItem->port >= 0) sPort += QString::number(pItem->port) + ':'; sPort += pItem->portName; QString sKey = sKeyPrefix + '_' + QString::number(iKey++); setConfig(sKey, sIndex + '|' + sClient + '|' + sPort); } } // MIDI specific accessors. qtractorMidiBus *qtractorMidiInsertPlugin::midiBus (void) const { return m_pMidiBus; } // Override title/name caption. QString qtractorMidiInsertPlugin::title (void) const { QString sTitle; if (m_pMidiBus && qtractorPlugin::alias().isEmpty()) { sTitle = QObject::tr("%1 (MIDI)").arg(m_pMidiBus->busName()); sTitle.replace('_', ' '); } else { sTitle = qtractorPlugin::title(); } return sTitle; } //---------------------------------------------------------------------------- // qtractorAuxSendPluginType -- Aux-send pseudo-plugin impl. // // Factory method (static) qtractorPlugin *qtractorAuxSendPluginType::createPlugin ( qtractorPluginList *pList, unsigned short iChannels ) { // Check whether it's a valid insert pseudo-plugin... qtractorPlugin *pPlugin = nullptr; qtractorAuxSendPluginType *pAuxSendType = nullptr; if (iChannels > 0) { pAuxSendType = new qtractorAudioAuxSendPluginType(iChannels); if (pAuxSendType->open()) pPlugin = new qtractorAudioAuxSendPlugin(pList, pAuxSendType); } else { pAuxSendType = new qtractorMidiAuxSendPluginType(); if (pAuxSendType->open()) pPlugin = new qtractorMidiAuxSendPlugin(pList, pAuxSendType); } if (pPlugin == nullptr && pAuxSendType) delete pAuxSendType; return pPlugin; } //---------------------------------------------------------------------------- // qtractorAudioAuxSendPluginType -- Audio aux-send pseudo-plugin type. // // Derived methods. bool qtractorAudioAuxSendPluginType::open (void) { // Sanity check... const unsigned short iChannels = index(); if (iChannels < 1) return false; #ifdef CONFIG_DEBUG qDebug("qtractorAudioAuxSendPluginType[%p]::open() channels=%u", this, iChannels); #endif // Pseudo-plugin type names. m_sName = QObject::tr("Aux Send (Audio)"); m_sLabel = "AudioAuxSend"; // m_sLabel.remove(' '); // Pseudo-plugin unique identifier. m_iUniqueID = qHash(m_sLabel) ^ qHash(iChannels); // Pseudo-plugin port counts... m_iControlIns = 1; m_iControlOuts = 0; m_iAudioIns = iChannels; m_iAudioOuts = iChannels; m_iMidiIns = 0; m_iMidiOuts = 0; // Cache flags. m_bRealtime = true; m_bConfigure = true; // Done. return true; } void qtractorAudioAuxSendPluginType::close (void) { } // Instance cached-deferred accesors. const QString& qtractorAudioAuxSendPluginType::aboutText (void) { if (m_sAboutText.isEmpty()) { m_sAboutText += QObject::tr("Aux Send pseudo-plugin (Audio)"); m_sAboutText += '\n'; m_sAboutText += QTRACTOR_WEBSITE; m_sAboutText += '\n'; m_sAboutText += QTRACTOR_COPYRIGHT; } return m_sAboutText; } //---------------------------------------------------------------------------- // qtractorMidiAuxSendPluginType -- MIDI Aux-send pseudo-plugin type. // // Derived methods. bool qtractorMidiAuxSendPluginType::open (void) { // Sanity check... const unsigned short iChannels = index(); if (iChannels > 0) return false; #ifdef CONFIG_DEBUG qDebug("qtractorMidiAuxSendPluginType[%p]::open() channels=%u", this, iChannels); #endif // Pseudo-plugin type names. m_sName = "Aux Send (MIDI)"; m_sLabel = "MidiAuxSend"; // m_sLabel.remove(' '); // Pseudo-plugin unique identifier. m_iUniqueID = qHash(m_sLabel);//^ qHash(iChannels); // Pseudo-plugin port counts... m_iControlIns = 1; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; m_iMidiIns = 1; m_iMidiOuts = 1; // Cache flags. m_bRealtime = true; m_bConfigure = true; // Done. return true; } void qtractorMidiAuxSendPluginType::close (void) { } // Instance cached-deferred accesors. const QString& qtractorMidiAuxSendPluginType::aboutText (void) { if (m_sAboutText.isEmpty()) { m_sAboutText += QObject::tr("Aux Send pseudo-plugin (MIDI)"); m_sAboutText += '\n'; m_sAboutText += QTRACTOR_WEBSITE; m_sAboutText += '\n'; m_sAboutText += QTRACTOR_COPYRIGHT; } return m_sAboutText; } //---------------------------------------------------------------------------- // qtractorAudioAuxSendPlugin -- Audio aux-send pseudo-plugin instance. // // Constructors. qtractorAudioAuxSendPlugin::qtractorAudioAuxSendPlugin ( qtractorPluginList *pList, qtractorAuxSendPluginType *pAuxSendType ) : qtractorAuxSendPlugin(pList, pAuxSendType), m_pAudioBus(nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractorAudioAuxSendPlugin[%p] channels=%u", this, pAuxSendType->channels()); #endif // Custom optimized processors. #if defined(__SSE__) if (sse_enabled()) { m_pfnProcessAdd = sse_process_add; } else #endif #if defined(__ARM_NEON__) m_pfnProcessAdd = neon_process_add; if (false) #endif { m_pfnProcessAdd = std_process_add; } // Create and attach the custom parameters... m_pSendGainParam = new Param(this, 0); m_pSendGainParam->setName(QObject::tr("Send Gain")); m_pSendGainParam->setMinValue(0.0f); m_pSendGainParam->setMaxValue(4.0f); m_pSendGainParam->setDefaultValue(1.0f); m_pSendGainParam->setValue(1.0f, false); addParam(m_pSendGainParam); // Audio bus I/O matrix buffers. m_ppOBuffers = nullptr; m_piOBuffers = nullptr; } // Destructor. qtractorAudioAuxSendPlugin::~qtractorAudioAuxSendPlugin (void) { // Cleanup plugin instance... setChannels(0); } // Channel/instance number accessors. void qtractorAudioAuxSendPlugin::setChannels ( unsigned short iChannels ) { // Check our type... qtractorPluginType *pType = type(); if (pType == nullptr) return; // We'll need this globals... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; // Estimate the (new) number of instances... const unsigned short iInstances = pType->instances(iChannels, list()->isMidi()); // Now see if instance and channel count changed anyhow... if (iInstances == instances() && iChannels == channels()) return; // Gotta go for a while... const bool bActivated = isActivated(); setChannelsActivated(iChannels, false); // Cleanup bus and buffers... if (m_pAudioBus) m_pAudioBus = nullptr; updateAudioBusMatrix(0); // Set new instance number... setInstances(iInstances); if (iInstances < 1) { // setChannelsActivated(iChannels, bActivated); return; } #ifdef CONFIG_DEBUG qDebug("qtractorAudioAuxSendPlugin[%p]::setChannels(%u) instances=%u", this, iChannels, iInstances); #endif realizeConfigs(); realizeValues(); // But won't need it anymore. releaseConfigs(); releaseValues(); // Setup aux-send bus... setAudioBusName(m_sAudioBusName); // (Re)activate instance if necessary... setChannelsActivated(iChannels, bActivated); } // Audio bus specific accessors. void qtractorAudioAuxSendPlugin::setAudioBusName ( const QString& sAudioBusName, bool bReset ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; qtractorAudioBus *pAudioBus = nullptr; if (!sAudioBusName.isEmpty()) { pAudioBus = static_cast ( pAudioEngine->findOutputBus(sAudioBusName)); } if (pAudioBus) { qtractorPluginList *pPluginList = list(); if (pPluginList && (pPluginList->flags() & qtractorPluginList::AudioOutBus)) { for (qtractorBus *pBus = pAudioEngine->buses().first(); pBus; pBus = pBus->next()) { if ((pBus->busMode() & qtractorBus::Output) && (pBus->pluginList_out() == pPluginList) && (pBus->busName() == sAudioBusName)) { pAudioBus = nullptr; break; } } } } if (pAudioBus) { if (bReset && sAudioBusName != m_sAudioBusName) pAudioEngine->resetAudioOutBus(pAudioBus, list()); m_pAudioBus = pAudioBus; m_sAudioBusName = sAudioBusName; // setConfig("audioBusName", m_sAudioBusName); } else { m_pAudioBus = nullptr; m_sAudioBusName.clear(); // clearConfigs(); } updateAudioBusName(); updateAudioBusMatrix(channels()); } const QString& qtractorAudioAuxSendPlugin::audioBusName (void) const { return m_sAudioBusName; } qtractorAudioBus *qtractorAudioAuxSendPlugin::audioBus (void) const { return m_pAudioBus; } // Audio bus to appear on plugin lists. void qtractorAudioAuxSendPlugin::updateAudioBusName (void) const { const QString& sTitle = title(); QListIterator iter(items()); while (iter.hasNext()) iter.next()->setText(sTitle); } // Audio bus I/O matrix. void qtractorAudioAuxSendPlugin::setAudioBusMatrix ( const QList& matrix ) { m_matrix = matrix; updateAudioBusMatrix(channels()); } const QList& qtractorAudioAuxSendPlugin::audioBusMatrix (void) const { return m_matrix; } void qtractorAudioAuxSendPlugin::updateAudioBusMatrix ( unsigned short iChannels ) { if (m_ppOBuffers) { delete [] m_ppOBuffers; m_ppOBuffers = nullptr; } if (m_piOBuffers) { delete [] m_piOBuffers; m_piOBuffers = nullptr; } // Setup audio bus I/O matrix and buffers... if (iChannels > 0 && m_pAudioBus) { const unsigned short iOBuffers = m_pAudioBus->channels(); const unsigned short iIBuffers = iChannels; m_ppOBuffers = new float * [iIBuffers]; m_piOBuffers = new int [iIBuffers]; int j = 0; for (unsigned short i = 0; i < iIBuffers; ++i) { m_ppOBuffers[i] = nullptr; if (i < m_matrix.size()) m_piOBuffers[i] = m_matrix.at(i); else m_piOBuffers[i] = j; if (++j >= iOBuffers) j = 0; } } } // Override title/name caption. QString qtractorAudioAuxSendPlugin::title (void) const { QString sTitle; if (qtractorPlugin::alias().isEmpty()) { const QString sAudioBusName = (m_pAudioBus ? m_sAudioBusName : QObject::tr("(none)")); sTitle = QObject::tr("%1 (Audio)").arg(sAudioBusName); } else { sTitle = qtractorPlugin::title(); } return sTitle; } // The main plugin processing procedure. void qtractorAudioAuxSendPlugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { qtractorPlugin::process(ppIBuffer, ppOBuffer, nframes); if (m_pAudioBus == nullptr) return; if (!m_pAudioBus->isEnabled()) return; qtractorAudioEngine *pAudioEngine = static_cast (m_pAudioBus->engine()); if (pAudioEngine == nullptr) return; // m_pAudioBus->process_prepare(nframes); float **ppOut = m_pAudioBus->out(); const unsigned int iOffset = pAudioEngine->bufferOffset(); const unsigned short iOBuffers = m_pAudioBus->channels(); const unsigned short iIBuffers = channels(); const float fGain = m_pSendGainParam->value(); if (m_ppOBuffers && m_piOBuffers) { for (unsigned short i = 0; i < iIBuffers; ++i) { const int j = m_piOBuffers[i]; if (j >= 0 && j < iOBuffers) m_ppOBuffers[i] = ppOut[j]; } ppOut = m_ppOBuffers; } (*m_pfnProcessAdd)(ppOut, ppOBuffer, nframes, iOffset, iIBuffers, fGain); // m_pAudioBus->process_commit(nframes); } // Do the actual activation. void qtractorAudioAuxSendPlugin::activate (void) { } // Do the actual deactivation. void qtractorAudioAuxSendPlugin::deactivate (void) { } // Pseudo-plugin configuration handlers. void qtractorAudioAuxSendPlugin::configure ( const QString& sKey, const QString& sValue ) { #ifdef CONFIG_DEBUG qDebug("qtractorAudioAuxSendPlugin[%p]::configure()", this); #endif if (sKey == "audioBusName") setAudioBusName(sValue); else if (sKey == "audioBusMatrix") { m_matrix.clear(); QStringListIterator iter(sValue.split(';')); while (iter.hasNext()) m_matrix.append(iter.next().toInt()); } } // Pseudo-plugin configuration/state snapshot. void qtractorAudioAuxSendPlugin::freezeConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorAudioAuxSendPlugin[%p]::freezeConfigs()", this); #endif clearConfigs(); setConfig("audioBusName", m_sAudioBusName); if (!m_matrix.isEmpty()) { QStringList vlist; QListIterator iter(m_matrix); while (iter.hasNext()) vlist.append(QString::number(iter.next())); setConfig("audioBusMatrix", vlist.join(';')); } } void qtractorAudioAuxSendPlugin::releaseConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorAudioAuxSendPlugin[%p]::releaseConfigs()", this); #endif qtractorPlugin::clearConfigs(); } //---------------------------------------------------------------------------- // qtractorMidiAuxSendPlugin -- MIDI aux-send pseudo-plugin instance. // // Constructors. qtractorMidiAuxSendPlugin::qtractorMidiAuxSendPlugin ( qtractorPluginList *pList, qtractorAuxSendPluginType *pAuxSendType ) : qtractorAuxSendPlugin(pList, pAuxSendType), m_pMidiBus(nullptr), m_pMidiOutputBuffer(nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiAuxSendPlugin[%p] channels=%u", this, pAuxSendType->channels()); #endif // Create and attach the custom parameters... m_pSendGainParam = new Param(this, 0); m_pSendGainParam->setName(QObject::tr("Send Gain")); m_pSendGainParam->setMinValue(0.0f); m_pSendGainParam->setMaxValue(4.0f); m_pSendGainParam->setDefaultValue(1.0f); m_pSendGainParam->setValue(1.0f, false); addParam(m_pSendGainParam); } // Destructor. qtractorMidiAuxSendPlugin::~qtractorMidiAuxSendPlugin (void) { // Cleanup plugin instance... setChannels(0); } // Channel/instance number accessors. void qtractorMidiAuxSendPlugin::setChannels ( unsigned short iChannels ) { // Check our type... qtractorPluginType *pType = type(); if (pType == nullptr) return; // We'll need this globals... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; // Estimate the (new) number of instances... const unsigned short iInstances = pType->instances(iChannels, list()->isMidi()); // Now see if instance and channel count changed anyhow... if (iInstances == instances() && iChannels == channels()) return; // Gotta go for a while... const bool bActivated = isActivated(); setChannelsActivated(iChannels, false); // Cleanup buffer... if (m_pMidiOutputBuffer) { delete m_pMidiOutputBuffer; m_pMidiOutputBuffer = nullptr; } // Cleanup bus... if (m_pMidiBus) m_pMidiBus = nullptr; // Set new instance number... setInstances(iInstances); if (iInstances < 1) { // setChannelsActivated(iChannels, bActivated); return; } #ifdef CONFIG_DEBUG qDebug("qtractorMidiAuxSendPlugin[%p]::setChannels(%u) instances=%u", this, iChannels, iInstances); #endif realizeConfigs(); realizeValues(); // But won't need it anymore. releaseConfigs(); releaseValues(); // Setup aux-send bus... setMidiBusName(m_sMidiBusName); // (Re)activate instance if necessary... setChannelsActivated(iChannels, bActivated); } // MIDI bus specific accessors. void qtractorMidiAuxSendPlugin::setMidiBusName ( const QString& sMidiBusName ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; qtractorMidiBus *pMidiBus = nullptr; if (!sMidiBusName.isEmpty()) { pMidiBus = static_cast ( pMidiEngine->findOutputBus(sMidiBusName)); } if (pMidiBus) { qtractorPluginList *pPluginList = list(); if (pPluginList && (pPluginList->flags() & qtractorPluginList::MidiOutBus)) { for (qtractorBus *pBus = pMidiEngine->buses().first(); pBus; pBus = pBus->next()) { if ((pBus->busMode() & qtractorBus::Output) && (pBus->pluginList_out() == pPluginList) && (pBus->busName() == sMidiBusName)) { pMidiBus = nullptr; break; } } } } if (pMidiBus) { m_pMidiBus = pMidiBus; m_sMidiBusName = sMidiBusName; // setConfig("midiBusName", m_sMidiBusName); } else { m_pMidiBus = nullptr; m_sMidiBusName.clear(); // clearConfigs(); } // (Re)create private MIDI buffer... if (m_pMidiOutputBuffer) { delete m_pMidiOutputBuffer; m_pMidiOutputBuffer = nullptr; } if (m_pMidiBus && m_pMidiOutputBuffer == nullptr) { m_pMidiOutputBuffer = new qtractorMidiOutputBuffer(m_pMidiBus); m_pMidiOutputBuffer->setGainSubject(m_pSendGainParam->subject()); } updateMidiBusName(); } const QString& qtractorMidiAuxSendPlugin::midiBusName (void) const { return m_sMidiBusName; } qtractorMidiBus *qtractorMidiAuxSendPlugin::midiBus (void) const { return m_pMidiBus; } // Audio bus to appear on plugin lists. void qtractorMidiAuxSendPlugin::updateMidiBusName (void) const { const QString& sTitle = title(); QListIterator iter(items()); while (iter.hasNext()) iter.next()->setText(sTitle); } // Override title/name caption. QString qtractorMidiAuxSendPlugin::title (void) const { QString sTitle; if (qtractorPlugin::alias().isEmpty()) { const QString sMidiBusName = (m_pMidiBus ? m_sMidiBusName : QObject::tr("(none)")); sTitle = QObject::tr("%1 (MIDI)").arg(sMidiBusName); } else { sTitle = qtractorPlugin::title(); } return sTitle; } // The main plugin processing procedure. void qtractorMidiAuxSendPlugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { qtractorPlugin::process(ppIBuffer, ppOBuffer, nframes); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiManager *pMidiManager = nullptr; if (list()) pMidiManager = list()->midiManager(); if (pMidiManager == nullptr) return; if (m_pMidiBus && m_pMidiOutputBuffer) { // Enqueue events into sends/output bus... const unsigned long t0 = (pSession->isPlaying() ? pSession->playHead() : 0); qtractorMidiBuffer *pEventBuffer = pMidiManager->buffer_in(); const unsigned int iEventCount = pEventBuffer->count(); for (unsigned int i = 0; i < iEventCount; ++i) { snd_seq_event_t *pEv = pEventBuffer->at(i); if (!m_pMidiOutputBuffer->enqueue(pEv, t0 + pEv->time.tick)) break; } // Wake the asynchronous working thread... if (iEventCount > 0) qtractorMidiSyncItem::syncItem(m_pMidiOutputBuffer); } } // Do the actual activation. void qtractorMidiAuxSendPlugin::activate (void) { } // Do the actual deactivation. void qtractorMidiAuxSendPlugin::deactivate (void) { if (m_pMidiOutputBuffer) m_pMidiOutputBuffer->clear(); } // Pseudo-plugin configuration handlers. void qtractorMidiAuxSendPlugin::configure ( const QString& sKey, const QString& sValue ) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiAuxSendPlugin[%p]::configure()", this); #endif if (sKey == "midiBusName") setMidiBusName(sValue); } // Pseudo-plugin configuration/state snapshot. void qtractorMidiAuxSendPlugin::freezeConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiAuxSendPlugin[%p]::freezeConfigs()", this); #endif clearConfigs(); setConfig("midiBusName", m_sMidiBusName); } void qtractorMidiAuxSendPlugin::releaseConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiAuxSendPlugin[%p]::releaseConfigs()", this); #endif qtractorPlugin::clearConfigs(); } // end of qtractorInsertPlugin.cpp qtractor-1.5.11/src/PaxHeaders/qtractorLv2Plugin.h0000644000000000000000000000013215124701674017065 xustar0030 mtime=1767080892.787263479 30 atime=1767080892.787263479 30 ctime=1767080892.787263479 qtractor-1.5.11/src/qtractorLv2Plugin.h0000644000175000001440000005133415124701674017063 0ustar00rncbcusers// qtractorLv2Plugin.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorLv2Plugin_h #define __qtractorLv2Plugin_h #include "qtractorPlugin.h" #include #ifdef CONFIG_LV2_EVENT // LV2 Event/MIDI support. #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/event/event.h" #include "lv2/lv2plug.in/ns/ext/event/event-helpers.h" #else #include "lv2/event/event.h" #include "lv2/event/event-helpers.h" #endif #endif #ifdef CONFIG_LV2_ATOM // LV2 Atom/MIDI support. #include "lv2_atom_helpers.h" #endif #ifndef QTRACTOR_LV2_MIDI_EVENT_ID #define QTRACTOR_LV2_MIDI_EVENT_ID 1 #endif #ifdef CONFIG_LV2_WORKER // LV2 Worker/Schedule support. #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/worker/worker.h" #else #include "lv2/worker/worker.h" #endif // Forward declarations. class qtractorLv2Worker; #endif #ifdef CONFIG_LV2_UI // LV2 UI support. #ifdef CONFIG_LIBSUIL #include #endif #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #include "lv2/lv2plug.in/ns/ext/data-access/data-access.h" #include "lv2/lv2plug.in/ns/ext/instance-access/instance-access.h" #else #include "lv2/ui/ui.h" #include "lv2/data-access/data-access.h" #include "lv2/instance-access/instance-access.h" #endif #ifdef CONFIG_LV2_ATOM #include #endif #ifdef CONFIG_LV2_EXTERNAL_UI // LV2 External UI support. #include "lv2_external_ui.h" #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) class QWindow; #endif #ifdef CONFIG_LV2_UI_REQ_VALUE_FAKE // LV2 UI Request-value support (FAKE). #undef CONFIG_LV2_UI_REQ_VALUE #define CONFIG_LV2_UI_REQ_VALUE 1 #ifndef LV2_UI__requestValue #define LV2_UI__requestValue LV2_UI_PREFIX "requestValue" typedef enum { LV2UI_REQUEST_VALUE_SUCCESS, LV2UI_REQUEST_VALUE_BUSY, LV2UI_REQUEST_VALUE_ERR_UNKNOWN, LV2UI_REQUEST_VALUE_ERR_UNSUPPORTED } LV2UI_Request_Value_Status; typedef struct _LV2UI_Request_Value { LV2UI_Feature_Handle handle; LV2UI_Request_Value_Status (*request)( LV2UI_Feature_Handle handle, LV2_URID key, LV2_URID type, const LV2_Feature *const *features); } LV2UI_Request_Value; #endif // !LV2_UI__requestValue #endif // CONFIG_LV2_UI_REQ_VALUE_FAKE #ifdef CONFIG_LV2_PORT_CHANGE_REQUEST // LV2 Control Input Port change request support. #include "lv2_port_change_request.h" #endif // CONFIG_LV2_PORT_CHANGE_REQUEST #endif // CONFIG_LV2_UI #ifdef CONFIG_LV2_STATE // LV2 State support. #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/state/state.h" #else #include "lv2/state/state.h" #endif #endif #ifdef CONFIG_LV2_PROGRAMS // LV2 Programs support. #include "lv2_programs.h" #endif #ifdef CONFIG_LV2_MIDNAM // LV2 MIDNAM support. #include "lv2_midnam.h" #endif #ifdef CONFIG_LV2_PRESETS // LV2 Presets support. #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/presets/presets.h" #else #include "lv2/presets/presets.h" #endif #endif #ifdef CONFIG_LV2_TIME // LV2 Time support. #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/time/time.h" #else #include "lv2/time/time.h" #endif // Forward decl. class qtractorAudioEngine; #endif #ifdef CONFIG_LV2_OPTIONS // LV2 Options support. #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/options/options.h" #else #include "lv2/options/options.h" #endif #endif #ifdef CONFIG_LV2_PATCH // LV2 Patch/properties support. #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/patch/patch.h" #else #include "lv2/patch/patch.h" #endif #include #endif //---------------------------------------------------------------------------- // qtractorLv2PluginType -- LV2 plugin type instance. // class qtractorLv2PluginType : public qtractorPluginType { public: // Constructor. qtractorLv2PluginType(const QString& sUri, LilvPlugin *plugin = nullptr) : qtractorPluginType(nullptr, 0, qtractorPluginType::Lv2), m_sUri(sUri), m_lv2_plugin(plugin) {} // Destructor. ~qtractorLv2PluginType() { close(); } // Derived methods. bool open(); void close(); // Factory method (static) static qtractorLv2PluginType *createType(const QString& sUri); // LV2 plugin URI (virtual override). QString filename() const { return m_sUri; } // LV2 descriptor method (static) static LilvPlugin *lv2_plugin(const QString& sUri); // Specific accessors. LilvPlugin *lv2_plugin() const { return m_lv2_plugin; } // LV2 World stuff (ref. counted). static void lv2_open(); static void lv2_close(); // Plugin type (URI) listing (static). static QStringList lv2_plugins(); #ifdef CONFIG_LV2_EVENT unsigned short eventIns() const { return m_iEventIns; } unsigned short eventOuts() const { return m_iEventOuts; } #endif #ifdef CONFIG_LV2_ATOM unsigned short atomIns() const { return m_iAtomIns; } unsigned short atomOuts() const { return m_iAtomOuts; } #endif #ifdef CONFIG_LV2_CVPORT unsigned short cvportIns() const { return m_iCVPortIns; } unsigned short cvportOuts() const { return m_iCVPortOuts; } #endif #ifdef CONFIG_LV2_UI_SHOW // Check for LV2 UI Show interface. bool lv2_ui_show_interface(LilvUI *ui) const; #endif // Instance cached-deferred accesors. const QString& aboutText(); protected: // LV2 plugin URI. QString m_sUri; // LV2 descriptor itself. LilvPlugin *m_lv2_plugin; #ifdef CONFIG_LV2_EVENT unsigned short m_iEventIns; unsigned short m_iEventOuts; #endif #ifdef CONFIG_LV2_ATOM unsigned short m_iAtomIns; unsigned short m_iAtomOuts; #endif #ifdef CONFIG_LV2_CVPORT unsigned short m_iCVPortIns; unsigned short m_iCVPortOuts; #endif }; //---------------------------------------------------------------------------- // qtractorLv2Plugin -- LV2 plugin instance. // class qtractorLv2Plugin : public qtractorPlugin { public: // Constructors. qtractorLv2Plugin(qtractorPluginList *pList, qtractorLv2PluginType *pLv2Type); // Destructor. ~qtractorLv2Plugin(); // Forward decl. class Param; // Channel/intsance number accessors. void setChannels(unsigned short iChannels); // Do the actual (de)activation. void activate(); void deactivate(); // The main plugin processing procedure. void process(float **ppIBuffer, float **ppOBuffer, unsigned int nframes); // Specific accessors. LilvPlugin *lv2_plugin() const; LilvInstance *lv2_instance(unsigned short iInstance) const; LV2_Handle lv2_handle(unsigned short iInstance) const; // Audio port numbers. unsigned long audioIn(unsigned short i) { return m_piAudioIns[i]; } unsigned long audioOut(unsigned short i) { return m_piAudioOuts[i]; } #ifdef CONFIG_LV2_UI // GUI Editor stuff. void openEditor(QWidget *pParent = nullptr); void closeEditor(); void idleEditor(); // GUI editor visibility state. void setEditorVisible(bool bVisible); bool isEditorVisible() const; // GUI editor window title methods. void setEditorTitle(const QString& sTitle); void updateEditorTitleEx(); // GUI editor window (re)position methods. void saveEditorPos(); void loadEditorPos(); // Parameter update method. void updateParam(qtractorPlugin::Param *pParam, float fValue, bool bUpdate); // Idle editor (static). static void idleEditorAll(); // LV2 UI control change method. void lv2_ui_port_write(uint32_t port_index, uint32_t buffer_size, uint32_t protocol, const void *buffer); // LV2 UI portMap method. uint32_t lv2_ui_port_index(const char *port_symbol); #ifdef CONFIG_LV2_UI_TOUCH // LV2 UI Touch interface (ui->host). void lv2_ui_touch(uint32_t port_index, bool grabbed); #endif #ifdef CONFIG_LV2_UI_REQ_VALUE // LV2 UI Request-value interface (ui->host). LV2UI_Request_Value_Status lv2_ui_request_value( LV2_URID key, LV2_URID type, const LV2_Feature *const *features); #endif #ifdef CONFIG_LV2_PORT_CHANGE_REQUEST // LV2 Control Input Port change request. LV2_ControlInputPort_Change_Status lv2_port_change_request( unsigned long port_index, float port_value); #endif // CONFIG_LV2_PORT_CHANGE_REQUEST // LV2 UI resize control (host->ui). void lv2_ui_resize(const QSize& size); // GUI editor closed state. void setEditorClosed(bool bClosed) { m_bEditorClosed = bClosed; } bool isEditorClosed() const { return m_bEditorClosed; } void closeEditorEx(); #endif // Plugin configuration/state (save) snapshot. void freezeConfigs(); // Plugin configuration/state (load) realization. void realizeConfigs(); // Plugin configuration/state release. void releaseConfigs(); // Plugin current latency (in frames); unsigned long latency() const { return (m_pfLatency ? *m_pfLatency : 0.0f); } #ifdef CONFIG_LV2_WORKER // LV2 Worker/Schedule extension data interface accessor. const LV2_Worker_Interface *lv2_worker_interface(unsigned short iInstance) const; #endif #ifdef CONFIG_LV2_STATE // LV2 State extension data interface accessor. const LV2_State_Interface *lv2_state_interface(unsigned short iInstance) const; LV2_State_Status lv2_state_store( uint32_t key, const void *value, size_t size, uint32_t type, uint32_t flags); const void *lv2_state_retrieve( uint32_t key, size_t *size, uint32_t *type, uint32_t *flags); // Load default plugin state. void lv2_state_load_default(); #endif #ifdef CONFIG_LV2_STATE_FILES // LV2 State save directory (when not the default session one). const QString& lv2_state_save_dir() const; #endif // URID map/unmap helpers. static LV2_URID lv2_urid_map(const char *uri); static const char *lv2_urid_unmap(LV2_URID id); // Provisional program/patch accessor. bool getProgram(int iIndex, Program& program) const; // Provisional note name accessor. bool getNoteName(int iIndex, NoteName& note) const; #ifdef CONFIG_LV2_PROGRAMS // LV2 Programs extension data descriptor accessor. const LV2_Programs_Interface *lv2_programs_descriptor(unsigned short iInstance) const; // Bank/program selector override. void selectProgram(int iBank, int iProg); // Program/patch notification. void lv2_program_changed(int iIndex); #endif #ifdef CONFIG_LV2_MIDNAM // LV2 MIDNAM extension data descriptor accessor. const LV2_Midnam_Interface *lv2_midnam_descriptor(unsigned short iInstance) const; // LV2 MIDNAME update notification. void lv2_midnam_update(); #endif #ifdef CONFIG_LV2_PRESETS // Refresh and load preset labels listing. QStringList presetList() const; // Load/Save plugin state from/into a named preset. bool loadPreset(const QString& sPreset); bool savePreset(const QString& sPreset); // Delete plugin state preset (from file-system). bool deletePreset(const QString& sPreset); // Whether given preset is internal/read-only. bool isReadOnlyPreset(const QString& sPreset) const; #endif #ifdef CONFIG_LV2_PATCH // LV2 Patch/properties support... void lv2_property_changed(LV2_URID key, const LV2_Atom *value); void lv2_property_update(LV2_URID key); #endif // CONFIG_LV2_PATCH #ifdef CONFIG_LV2_TIME // Update LV2 Time from JACK transport position. static void updateTime(qtractorAudioEngine *pAudioEngine); static void updateTimePost(); #ifdef CONFIG_LV2_TIME_POSITION // Make ready LV2 Time position. void lv2_time_position_changed(); #endif #endif protected: // Update instrument/programs cache. void updateInstruments(); // Clear instrument/programs cache. void clearInstruments(); #ifdef CONFIG_LV2_UI // Alternate UI instantiation stuff... bool lv2_ui_instantiate( const char *ui_host_uri, const char *plugin_uri, const char *ui_uri, const char *ui_type_uri, const char *ui_bundle_path, const char *ui_binary_path, QWidget *pParent, Qt::WindowFlags wflags); void lv2_ui_port_event( uint32_t port_index, uint32_t buffer_size, uint32_t format, const void *buffer); const void *lv2_ui_extension_data(const char *uri); #endif // CONFIG_LV2_UI #ifdef CONFIG_LV2_PATCH // LV2 Patch/property decl. class Property; // LV2 Patch/properties inventory. void lv2_patch_properties(const char *pszPatch); #endif #ifdef CONFIG_LV2_STATE // Save/restore complete plugin state into/from a string. QString lv2_state_save(); bool lv2_state_restore(const QString& s); #endif // CONFIG_LV2_STATE private: // Instance variables. LilvInstance **m_ppInstances; // List of output control port indexes and data. unsigned long *m_piControlOuts; float *m_pfControlOuts; float *m_pfControlOutsLast; // List of audio port indexes. unsigned long *m_piAudioIns; unsigned long *m_piAudioOuts; // Dummy I/O buffers. float *m_pfIDummy; float *m_pfODummy; #ifdef CONFIG_LV2_EVENT // List of LV2 Event/MIDI port indexes. unsigned long *m_piEventIns; unsigned long *m_piEventOuts; #endif #ifdef CONFIG_LV2_ATOM // List of LV2 Atom/MIDI port indexes and buffers. unsigned long *m_piAtomIns; unsigned long *m_piAtomOuts; LV2_Atom_Buffer **m_lv2_atom_buffer_ins; LV2_Atom_Buffer **m_lv2_atom_buffer_outs; unsigned long m_lv2_atom_midi_port_in; unsigned long m_lv2_atom_midi_port_out; #endif #ifdef CONFIG_LV2_CVPORT // List of LV2 CVPort indexes. unsigned long *m_piCVPortIns; unsigned long *m_piCVPortOuts; #endif // Local copy of features array. LV2_Feature **m_lv2_features; #ifdef CONFIG_LV2_WORKER // LV2 Worker/Schedule support. qtractorLv2Worker *m_lv2_worker; #endif #ifdef CONFIG_LV2_UI int m_lv2_ui_type; QByteArray m_aEditorTitle; bool m_bEditorVisible; volatile bool m_bEditorClosed; LilvUIs *m_lv2_uis; LilvUI *m_lv2_ui; LV2_Extension_Data_Feature m_lv2_ui_data_access; LV2_Feature m_lv2_ui_data_access_feature; LV2_Feature m_lv2_ui_instance_access_feature; LV2_Feature **m_lv2_ui_features; // Alternate UI instantiation stuff. void *m_lv2_ui_module; const LV2UI_Descriptor *m_lv2_ui_descriptor; LV2UI_Port_Map m_lv2_ui_port_map; LV2_Feature m_lv2_ui_port_map_feature; // Common UI instantiation stuff. LV2UI_Handle m_lv2_ui_handle; LV2UI_Widget m_lv2_ui_widget; // Whether LV2 UI no-user-resize feature is being requested. bool m_lv2_ui_no_user_resize; #ifdef CONFIG_LIBSUIL SuilHost *m_suil_host; SuilInstance *m_suil_instance; bool m_suil_support; #endif #ifdef CONFIG_LV2_ATOM // LV2 Atom control (ring)buffers for UI updates. struct ControlEvent { uint32_t index; uint32_t protocol; uint32_t size; uint8_t body[]; }; jack_ringbuffer_t *m_ui_events; jack_ringbuffer_t *m_plugin_events; #endif #ifdef CONFIG_LV2_EXTERNAL_UI LV2_Feature m_lv2_ui_external_feature; LV2_External_UI_Host m_lv2_ui_external_host; #ifdef LV2_EXTERNAL_UI_DEPRECATED_URI LV2_Feature m_lv2_ui_external_deprecated_feature; #endif #endif // Our own Qt UI widget (native). class EventFilter; EventFilter *m_pQtFilter; QWidget *m_pQtWidget; bool m_bQtDelete; // Changed UI params hash-queue. QHash m_ui_params; #ifdef CONFIG_LV2_UI_TOUCH // LV2 UI Touch interface (ui->host). LV2UI_Touch m_lv2_ui_touch; LV2_Feature m_lv2_ui_touch_feature; QHash m_ui_params_touch; #endif // Changed control input port-events hash-queue. QHash m_port_events; #ifdef CONFIG_LV2_UI_REQ_VALUE // LV2 UI Request-value interface (ui->host). LV2UI_Request_Value m_lv2_ui_req_value; LV2_Feature m_lv2_ui_req_value_feature; volatile bool m_lv2_ui_req_value_busy; #endif #ifdef CONFIG_LV2_UI_IDLE // LV2 UI Idle extension data interface. const LV2UI_Idle_Interface *m_lv2_ui_idle_interface; #endif #ifdef CONFIG_LV2_UI_SHOW // LV2 UI Show extension data interface. const LV2UI_Show_Interface *m_lv2_ui_show_interface; #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef CONFIG_LV2_UI_GTK2 struct _GtkWidget *m_pGtkWindow; QWindow *m_pQtWindow; #endif // CONFIG_LV2_UI_GTK2 #ifdef CONFIG_LV2_UI_X11 LV2UI_Resize m_lv2_ui_resize; LV2_Feature m_lv2_ui_resize_feature; LV2_Feature m_lv2_ui_parent_feature; #endif // CONFIG_LV2_UI_X11 #endif #endif // CONFIG_LV2_UI #ifdef CONFIG_LV2_STATE QHash m_lv2_state_configs; QHash m_lv2_state_ctypes; LV2_Feature m_lv2_state_load_default_feature; #endif #ifdef CONFIG_LV2_STATE_FILES LV2_Feature m_lv2_state_map_path_feature; LV2_State_Map_Path m_lv2_state_map_path; #ifdef CONFIG_LV2_STATE_MAKE_PATH LV2_Feature m_lv2_state_make_path_feature; LV2_State_Make_Path m_lv2_state_make_path; #endif #ifdef CONFIG_LV2_STATE_FREE_PATH LV2_Feature m_lv2_state_free_path_feature; LV2_State_Free_Path m_lv2_state_free_path; #endif QString m_lv2_state_save_dir; #endif // Programs cache. QList m_programs; // Note-names cache. QList m_noteNames; #ifdef CONFIG_LV2_PROGRAMS LV2_Feature m_lv2_programs_host_feature; LV2_Programs_Host m_lv2_programs_host; #endif #ifdef CONFIG_LV2_MIDNAM LV2_Feature m_lv2_midnam_feature; LV2_Midnam m_lv2_midnam; unsigned int m_lv2_midnam_update; #endif #ifdef CONFIG_LV2_UI #ifdef CONFIG_LV2_PORT_CHANGE_REQUEST LV2_Feature m_lv2_port_change_request_feature; LV2_ControlInputPort_Change_Request m_lv2_port_change_request; #endif #endif #ifdef CONFIG_LV2_PRESETS // LV2 Presets label-to-uri map. QHash m_lv2_presets; #endif #ifdef CONFIG_LV2_TIME // LV2 Time designated ports map. QHash m_lv2_time_ports; #ifdef CONFIG_LV2_TIME_POSITION // LV2 Time position port enabled index. bool m_lv2_time_position_enabled; unsigned long m_lv2_time_position_port_in; unsigned int m_lv2_time_position_changed; #endif #endif #ifdef CONFIG_LV2_PATCH // LV2 Patch/properties support. unsigned long m_lv2_patch_port_in; unsigned int m_lv2_patch_changed; #endif #ifdef CONFIG_LV2_OPTIONS #ifdef CONFIG_LV2_BUF_SIZE LV2_Feature m_lv2_options_feature; LV2_Options_Option m_lv2_options[5]; uint32_t m_iMinBlockLength; uint32_t m_iMaxBlockLength; uint32_t m_iNominalBlockLength; uint32_t m_iSequenceSize; #endif #ifdef CONFIG_LV2_UI LV2_Feature m_lv2_ui_options_feature; LV2_Options_Option m_lv2_ui_options[5]; float m_fUpdateRate; float m_fSampleRate; double m_dSampleRate; #endif #endif // Plugin current latency output control port; float *m_pfLatency; }; //---------------------------------------------------------------------------- // qtractorLv2Plugin::Param -- LV2 plugin control input port instance. // class qtractorLv2Plugin::Param : public qtractorPlugin::Param { public: // Constructor. Param(qtractorLv2Plugin *pLv2Plugin, unsigned long iIndex); // Port range hints predicate methods. bool isBoundedBelow() const; bool isBoundedAbove() const; bool isDefaultValue() const; bool isLogarithmic() const; bool isSampleRate() const; bool isInteger() const; bool isToggled() const; bool isDisplay() const; // Current display value. QString display() const; private: // Port bit-wise hints. enum { None = 0, Toggled = 1, Integer = 2, SampleRate = 4, Logarithmic = 8, }; // Instance variables. unsigned int m_iPortHints; QHash m_display; }; #ifdef CONFIG_LV2_PATCH //---------------------------------------------------------------------------- // qtractorLv2Plugin::Property -- LV2 Patch/property registry item. // class qtractorLv2Plugin::Property : public qtractorPlugin::Property { public: // Constructor. Property(qtractorLv2Plugin *pLv2Plugin, unsigned long iProperty, const LilvNode *property); // Property accessors. LV2_URID type() const { return m_type; } // Property predicates. bool isToggled() const; bool isInteger() const; bool isString() const; bool isPath() const; protected: // Fake property predicates. bool isBoundedBelow() const; bool isBoundedAbove() const; bool isDefaultValue() const; bool isLogarithmic() const; bool isSampleRate() const; bool isDisplay() const; // Virtual observer updater. void update(float fValue, bool bUpdate); private: // Instance variables. LV2_URID m_type; }; #endif // CONFIG_LV2_PATCH #endif // __qtractorLv2Plugin_h // end of qtractorLv2Plugin.h qtractor-1.5.11/src/PaxHeaders/qtractorSessionCommand.h0000644000000000000000000000013215124701674020165 xustar0030 mtime=1767080892.800263424 30 atime=1767080892.800263424 30 ctime=1767080892.800263424 qtractor-1.5.11/src/qtractorSessionCommand.h0000644000175000001440000000650015124701674020156 0ustar00rncbcusers// qtractorSessionCommand.h // /**************************************************************************** Copyright (C) 2005-2022, 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 __qtractorSessionCommand_h #define __qtractorSessionCommand_h #include "qtractorPropertyCommand.h" #include "qtractorSession.h" // Forward declarations. class qtractorTimeScaleUpdateNodeCommand; //---------------------------------------------------------------------- // class qtractorSessionCommand - declaration. // class qtractorSessionCommand : public qtractorCommand { public: // Constructor. qtractorSessionCommand(const QString& sName, qtractorSession *pSession); // Destructor. virtual ~qtractorSessionCommand(); // Track accessor. qtractorSession *session() const { return m_pSession; } private: // Instance variables. qtractorSession *m_pSession; }; //---------------------------------------------------------------------- // class qtractorSessionLoopCommand - declaration. // class qtractorSessionLoopCommand : public qtractorSessionCommand { public: // Constructor. qtractorSessionLoopCommand(qtractorSession *pSession, unsigned long iLoopStart, unsigned long iLoopEnd); // Session-loop command methods. bool redo(); bool undo(); private: // Instance variables. unsigned long m_iLoopStart; unsigned long m_iLoopEnd; }; //---------------------------------------------------------------------- // class qtractorSessionPunchCommand - declaration. // class qtractorSessionPunchCommand : public qtractorSessionCommand { public: // Constructor. qtractorSessionPunchCommand(qtractorSession *pSession, unsigned long iPunchIn, unsigned long iPunchOut); // Session-punch command methods. bool redo(); bool undo(); private: // Instance variables. unsigned long m_iPunchIn; unsigned long m_iPunchOut; }; //---------------------------------------------------------------------- // class qtractorSessionEditCommand - declaration. // class qtractorSessionEditCommand : public qtractorSessionCommand { public: // Constructor. qtractorSessionEditCommand(qtractorSession *pSession, const qtractorSession::Properties& properties); // Destructor. ~qtractorSessionEditCommand(); // Session-tempo command methods. bool redo(); bool undo(); private: // Instance variables. qtractorPropertyCommand *m_pPropertiesCommand; qtractorTimeScaleUpdateNodeCommand *m_pTempoCommand; unsigned short m_iTicksPerBeat; QString m_sSessionName; }; #endif // __qtractorSessionCommand_h // end of qtractorSessionCommand.h qtractor-1.5.11/src/PaxHeaders/qtractorMixer.cpp0000644000000000000000000000013215124701674016662 xustar0030 mtime=1767080892.796263441 30 atime=1767080892.795263445 30 ctime=1767080892.796263441 qtractor-1.5.11/src/qtractorMixer.cpp0000644000175000001440000014364415124701674016666 0ustar00rncbcusers// qtractorMixer.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorMixer.h" #include "qtractorPluginListView.h" #include "qtractorAudioMeter.h" #include "qtractorMidiMeter.h" #include "qtractorAudioMonitor.h" #include "qtractorMidiMonitor.h" #include "qtractorMidiManager.h" #include "qtractorObserverWidget.h" #include "qtractorOptions.h" #include "qtractorSession.h" #include "qtractorTracks.h" #include "qtractorConnections.h" #include "qtractorTrackCommand.h" #include "qtractorEngineCommand.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorMidiControlObserver.h" #include "qtractorPlugin.h" #include "qtractorCurve.h" #include "qtractorMainForm.h" #include "qtractorBusForm.h" #include #include #include #include #include #include #ifdef CONFIG_GRADIENT #include #endif //---------------------------------------------------------------------------- // qtractorMonitorButton -- Monitor observer tool button. // Constructors. qtractorMonitorButton::qtractorMonitorButton ( qtractorTrack *pTrack, QWidget *pParent ) : qtractorMidiControlButton(pParent) { initMonitorButton(); setTrack(pTrack); } qtractorMonitorButton::qtractorMonitorButton ( qtractorBus *pBus, QWidget *pParent ) : qtractorMidiControlButton(pParent) { initMonitorButton(); setBus(pBus); } // Destructor. qtractorMonitorButton::~qtractorMonitorButton (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; qtractorMidiControlObserver *pMidiObserver = nullptr; if (m_pTrack) pMidiObserver = m_pTrack->monitorObserver(); else if (m_pBus) { pMidiObserver = m_pBus->monitorObserver(); } if (pMidiObserver) pMidiControl->unmapMidiObserverWidget(pMidiObserver, this); } // Common initializer. void qtractorMonitorButton::initMonitorButton (void) { QPushButton::setIconSize(QSize(8, 8)); QPushButton::setIcon(QIcon::fromTheme("itemLedOff")); QPushButton::setText(' ' + tr("monitor")); QPushButton::setCheckable(true); QObject::connect(this, SIGNAL(toggled(bool)), SLOT(toggledSlot(bool))); } // Specific track accessors. void qtractorMonitorButton::setTrack ( qtractorTrack *pTrack ) { m_pTrack = pTrack; m_pBus = nullptr; // QPushButton::setToolTip(tr("Monitor (rec)")); updateMonitor(); // Visitor setup. } // Specific bus accessors. void qtractorMonitorButton::setBus ( qtractorBus *pBus ) { m_pBus = pBus; m_pTrack = nullptr; // QPushButton::setToolTip(tr("Monitor (thru)")); updateMonitor(); // Visitor setup. } // Visitors overload. void qtractorMonitorButton::updateValue ( float fValue ) { // Avoid self-triggering... const bool bBlockSignals = QPushButton::blockSignals(true); if (fValue > 0.0f) { QPushButton::setIcon(QIcon::fromTheme("itemLedOn")); QPushButton::setChecked(true); } else { QPushButton::setIcon(QIcon::fromTheme("itemLedOff")); QPushButton::setChecked(false); } QPushButton::blockSignals(bBlockSignals); } // Special toggle slot. void qtractorMonitorButton::toggledSlot ( bool bOn ) { // Just emit proper signal... if (m_pTrack) m_pTrack->monitorChangeNotify(bOn); else if (m_pBus) m_pBus->monitorChangeNotify(bOn); } // Monitor state button setup. void qtractorMonitorButton::updateMonitor (void) { if (m_pTrack) { setSubject(m_pTrack->monitorSubject()); qtractorMidiControlObserver *pMidiObserver = m_pTrack->monitorObserver(); if (pMidiObserver) { pMidiObserver->setCurveList(m_pTrack->curveList()); addMidiControlAction(pMidiObserver); } } else if (m_pBus) { if ((m_pBus->busMode() & qtractorBus::Duplex) == qtractorBus::Duplex) { setSubject(m_pBus->monitorSubject()); addMidiControlAction(m_pBus->monitorObserver()); QPushButton::setEnabled(true); } else { QPushButton::setEnabled(false); } } observer()->update(true); } //---------------------------------------------------------------------------- // qtractorMixerStrip::IconLabel -- Custom mixer strip title widget. class qtractorMixerStrip::IconLabel : public QLabel { public: // Constructor. IconLabel(QWidget *pParent = nullptr) : QLabel(pParent) {} // Icon accessors. void setIcon(const QIcon& icon) { m_icon = icon; } const QIcon& icon() const { return m_icon; } protected: // Custom paint event. void paintEvent(QPaintEvent *) { QPainter painter(this); QRect rect(QLabel::rect()); const int x = rect.x() + 1; const int y = rect.y() + ((rect.height() - 16) >> 1) + 1; painter.drawPixmap(x, y, m_icon.pixmap(16)); rect.adjust(+16, +2, -1, 0); painter.drawText(rect, QLabel::alignment(), QLabel::text()); } private: // Instance variables. QIcon m_icon; }; //---------------------------------------------------------------------------- // qtractorMixerStrip -- Mixer strip widget. // Constructors. qtractorMixerStrip::qtractorMixerStrip ( qtractorMixerRack *pRack, qtractorBus *pBus, qtractorBus::BusMode busMode ) : QFrame(pRack->workspace()), m_pRack(pRack), m_pBus(pBus), m_busMode(busMode), m_pTrack(nullptr) { initMixerStrip(); } qtractorMixerStrip::qtractorMixerStrip ( qtractorMixerRack *pRack, qtractorTrack *pTrack ) : QFrame(pRack->workspace()), m_pRack(pRack), m_pBus(nullptr), m_busMode(qtractorBus::None), m_pTrack(pTrack) { initMixerStrip(); } // Default destructor. qtractorMixerStrip::~qtractorMixerStrip (void) { // Take special care to nullify the audio output monitor // on MIDI track or buses, avoid its removal at meter's dtor... qtractorTrack::TrackType meterType = qtractorTrack::None; if (m_pTrack) meterType = m_pTrack->trackType(); else if (m_pBus) meterType = m_pBus->busType(); if (meterType == qtractorTrack::Midi) { qtractorMidiMixerMeter *pMidiMixerMeter = static_cast (m_pMixerMeter); if (pMidiMixerMeter) pMidiMixerMeter->setAudioOutputMonitor(nullptr); } qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl&& m_pMixerMeter) { qtractorMidiControlObserver *pMidiObserver; pMidiObserver = m_pMixerMeter->monitor()->panningObserver(); pMidiControl->unmapMidiObserverWidget( pMidiObserver, m_pMixerMeter->panSlider()); pMidiObserver = m_pMixerMeter->monitor()->gainObserver(); pMidiControl->unmapMidiObserverWidget( pMidiObserver, m_pMixerMeter->gainSlider()); } // No need to delete child widgets, Qt does it all for us #if 0 if (m_pMidiLabel) delete m_pMidiLabel; if (m_pMixerMeter) delete m_pMixerMeter; if (m_pSoloButton) delete m_pSoloButton; if (m_pMuteButton) delete m_pMuteButton; if (m_pRecordButton) delete m_pRecordButton; if (m_pMonitorButton) delete m_pMonitorButton; if (m_pBusButton) delete m_pBusButton; delete m_pButtonLayout; delete m_pPluginListView; delete m_pLabel; delete m_pLayout; #endif } // Common mixer-strip initializer. void qtractorMixerStrip::initMixerStrip (void) { m_iMark = 0; const QFont& font = QFrame::font(); const QFont font2(font.family(), font.pointSize() - 2); const QFont font3(font.family(), font.pointSize() - 3); const int iFixedHeight = QFontMetrics(font2).lineSpacing() + 4; QFrame::setFont(font2); m_pLayout = new QVBoxLayout(this); m_pLayout->setContentsMargins(4, 4, 4, 4); m_pLayout->setSpacing(4); m_pLabel = new IconLabel(/*this*/); // m_pLabel->setFont(font2); m_pLabel->setFixedHeight(iFixedHeight); m_pLabel->setBackgroundRole(QPalette::Button); m_pLabel->setForegroundRole(QPalette::ButtonText); m_pLabel->setAutoFillBackground(true); m_pLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop); m_pLayout->addWidget(m_pLabel); m_pRibbon = new QFrame(/*this*/); m_pRibbon->setFixedHeight(4); m_pRibbon->setBackgroundRole(QPalette::Button); m_pRibbon->setForegroundRole(QPalette::ButtonText); m_pRibbon->setAutoFillBackground(true); m_pLayout->addWidget(m_pRibbon); m_pPluginListView = new qtractorPluginListView(/*this*/); m_pPluginListView->setFont(font3); m_pPluginListView->setMinimumHeight(iFixedHeight << 1); m_pPluginListView->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); m_pPluginListView->setTinyScrollBar(true); m_pLayout->addWidget(m_pPluginListView, 1); const QSizePolicy buttonPolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); m_pButtonLayout = new QHBoxLayout(/*this*/); m_pButtonLayout->setContentsMargins(0, 0, 0, 0); m_pButtonLayout->setSpacing(2); qtractorTrack::TrackType meterType = qtractorTrack::None; if (m_pTrack) { meterType = m_pTrack->trackType(); m_pMonitorButton = new qtractorMonitorButton(m_pTrack); m_pMonitorButton->setFixedHeight(iFixedHeight); m_pMonitorButton->setSizePolicy(buttonPolicy); m_pMonitorButton->setFont(font3); m_pRecordButton = new qtractorTrackButton(m_pTrack, qtractorTrack::Record); m_pRecordButton->setFixedHeight(iFixedHeight); m_pRecordButton->setSizePolicy(buttonPolicy); m_pRecordButton->setFont(font3); m_pMuteButton = new qtractorTrackButton(m_pTrack, qtractorTrack::Mute); m_pMuteButton->setFixedHeight(iFixedHeight); m_pMuteButton->setSizePolicy(buttonPolicy); m_pMuteButton->setFont(font3); m_pSoloButton = new qtractorTrackButton(m_pTrack, qtractorTrack::Solo); m_pSoloButton->setFixedHeight(iFixedHeight); m_pSoloButton->setSizePolicy(buttonPolicy); m_pSoloButton->setFont(font3); m_pButtonLayout->addWidget(m_pRecordButton); m_pButtonLayout->addWidget(m_pMuteButton); m_pButtonLayout->addWidget(m_pSoloButton); m_pBusButton = nullptr; } else if (m_pBus) { meterType = m_pBus->busType(); m_pMonitorButton = new qtractorMonitorButton(m_pBus); m_pMonitorButton->setFixedHeight(iFixedHeight); m_pMonitorButton->setSizePolicy(buttonPolicy); m_pMonitorButton->setFont(font3); m_pBusButton = new QPushButton(/*this*/); m_pBusButton->setFixedHeight(iFixedHeight); m_pBusButton->setFocusPolicy(Qt::NoFocus); m_pBusButton->setSizePolicy(buttonPolicy); // m_pBusButton->setToolButtonStyle(Qt::ToolButtonTextOnly); m_pBusButton->setFont(font3); m_pBusButton->setText( m_busMode & qtractorBus::Input ? tr("inputs") : tr("outputs")); m_pBusButton->setToolTip(tr("Connect %1").arg(m_pBusButton->text())); m_pButtonLayout->addWidget(m_pBusButton); QObject::connect(m_pBusButton, SIGNAL(clicked()), SLOT(busButtonSlot())); m_pRecordButton = nullptr; m_pMuteButton = nullptr; m_pSoloButton = nullptr; } m_pLayout->addWidget(m_pMonitorButton); m_pLayout->addLayout(m_pButtonLayout); // Now, there's whether we are Audio or MIDI related... m_pMixerMeter = nullptr; m_pMidiLabel = nullptr; int iFixedWidth = 64; QPalette pal(m_pRibbon->palette()); switch (meterType) { case qtractorTrack::Audio: { // Set header ribbon color (Audio)... pal.setColor(QPalette::Button, qtractorAudioMeter::color(qtractorAudioMeter::Color10dB)); // Type cast for proper audio monitor... qtractorAudioMonitor *pAudioMonitor = nullptr; if (m_pTrack) { pAudioMonitor = static_cast (m_pTrack->monitor()); m_pPluginListView->setPluginList(m_pTrack->pluginList()); } else { qtractorAudioBus *pAudioBus = static_cast (m_pBus); if (pAudioBus) { if (m_busMode & qtractorBus::Input) { m_pPluginListView->setPluginList( pAudioBus->pluginList_in()); pAudioMonitor = pAudioBus->audioMonitor_in(); } else { m_pPluginListView->setPluginList( pAudioBus->pluginList_out()); pAudioMonitor = pAudioBus->audioMonitor_out(); } } } // Have we an audio monitor/meter?... if (pAudioMonitor) { const int iAudioChannels = pAudioMonitor->channels(); iFixedWidth += (iAudioChannels < 3 ? 24 : 8 * iAudioChannels); m_pMixerMeter = new qtractorAudioMixerMeter(pAudioMonitor, this); } m_pPluginListView->setEnabled(true); break; } case qtractorTrack::Midi: { // Set header ribbon color (MIDI)... pal.setColor(QPalette::Button, qtractorMidiMeter::color(qtractorMidiMeter::ColorOver)); // Type cast for proper MIDI monitor... qtractorMidiMonitor *pMidiMonitor = nullptr; qtractorMidiBus *pMidiBus = nullptr; if (m_pTrack) { pMidiMonitor = static_cast (m_pTrack->monitor()); m_pPluginListView->setPluginList(m_pTrack->pluginList()); m_pPluginListView->setEnabled(true); } else { pMidiBus = static_cast (m_pBus); if (pMidiBus) { if (m_busMode & qtractorBus::Input) { m_pPluginListView->setPluginList( pMidiBus->pluginList_in()); pMidiMonitor = pMidiBus->midiMonitor_in(); } else { m_pPluginListView->setPluginList( pMidiBus->pluginList_out()); pMidiMonitor = pMidiBus->midiMonitor_out(); } } m_pPluginListView->setEnabled(true); } // Have we a MIDI monitor/meter?... if (pMidiMonitor) { iFixedWidth += 24; qtractorMidiMixerMeter *pMidiMixerMeter = new qtractorMidiMixerMeter(pMidiMonitor, this); // MIDI Tracks might need to show something, // like proper MIDI channel settings... if (m_pTrack) { m_pMidiLabel = new QLabel(/*m_pMeter->topWidget()*/); // m_pMidiLabel->setFont(font2); m_pMidiLabel->setAlignment( Qt::AlignHCenter | Qt::AlignVCenter); pMidiMixerMeter->topLayout()->insertWidget(1, m_pMidiLabel); updateMidiLabel(); } // No panning on MIDI bus monitors and on duplex ones // only on the output buses should be enabled... if (pMidiBus) { if ((m_busMode & qtractorBus::Input) && (m_pBus->busMode() & qtractorBus::Output)) { pMidiMixerMeter->panSlider()->setEnabled(false); pMidiMixerMeter->panSpinBox()->setEnabled(false); pMidiMixerMeter->gainSlider()->setEnabled(false); pMidiMixerMeter->gainSpinBox()->setEnabled(false); } } // Apply the combo-meter posssibility... qtractorMidiManager *pMidiManager = nullptr; qtractorPluginList *pPluginList = m_pPluginListView->pluginList(); if (pPluginList) pMidiManager = pPluginList->midiManager(); if (pMidiManager && pMidiManager->isAudioOutputMonitor()) { pMidiMixerMeter->setAudioOutputMonitor( pMidiManager->audioOutputMonitor()); } // Ready. m_pMixerMeter = pMidiMixerMeter; } break; } case qtractorTrack::None: default: break; } m_pRibbon->setPalette(pal); // Eventually the right one... if (m_pMixerMeter) { // Set MIDI controller & automation hooks... qtractorMidiControlObserver *pMidiObserver; pMidiObserver = m_pMixerMeter->monitor()->panningObserver(); if (m_pTrack) pMidiObserver->setCurveList(m_pTrack->curveList()); m_pMixerMeter->addMidiControlAction(m_pMixerMeter->panSlider(), pMidiObserver); pMidiObserver = m_pMixerMeter->monitor()->gainObserver(); if (m_pTrack) pMidiObserver->setCurveList(m_pTrack->curveList()); m_pMixerMeter->addMidiControlAction(m_pMixerMeter->gainSlider(), pMidiObserver); // Finally, add to layout... m_pLayout->addWidget(m_pMixerMeter, 4); QObject::connect(m_pMixerMeter->panSlider(), SIGNAL(valueChanged(float)), SLOT(panningChangedSlot(float))); QObject::connect(m_pMixerMeter->panSpinBox(), SIGNAL(valueChanged(float)), SLOT(panningChangedSlot(float))); QObject::connect(m_pMixerMeter->gainSlider(), SIGNAL(valueChanged(float)), SLOT(gainChangedSlot(float))); QObject::connect(m_pMixerMeter->gainSpinBox(), SIGNAL(valueChanged(float)), SLOT(gainChangedSlot(float))); } QFrame::setFrameShape(QFrame::Panel); QFrame::setFrameShadow(QFrame::Raised); QFrame::setFixedWidth(iFixedWidth); // QFrame::setSizePolicy( // QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); QFrame::setBackgroundRole(QPalette::Window); // QFrame::setForegroundRole(QPalette::WindowText); QFrame::setAutoFillBackground(true); updateName(); setSelected(false); } // Child properties accessors. void qtractorMixerStrip::setMonitor ( qtractorMonitor *pMonitor ) { qtractorTrack::TrackType meterType = qtractorTrack::None; if (m_pTrack) meterType = m_pTrack->trackType(); else if (m_pBus) meterType = m_pBus->busType(); if (meterType == qtractorTrack::Audio) { qtractorAudioMonitor *pAudioMonitor = static_cast (pMonitor); if (pAudioMonitor) { const int iOldWidth = QFrame::width(); const int iAudioChannels = pAudioMonitor->channels(); const int iFixedWidth = 64 + (iAudioChannels < 3 ? 24 : 8 * iAudioChannels); if (iFixedWidth != iOldWidth) { QFrame::setFixedWidth(iFixedWidth); m_pRack->updateWorkspace(); } } } else if (meterType == qtractorTrack::Midi) { qtractorMidiMixerMeter *pMidiMixerMeter = static_cast (m_pMixerMeter); if (pMidiMixerMeter) { qtractorPluginList *pPluginList = nullptr; if (m_pBus) { if (m_busMode & qtractorBus::Input) pPluginList = m_pBus->pluginList_in(); else pPluginList = m_pBus->pluginList_in(); } else if (m_pTrack) pPluginList = m_pTrack->pluginList(); if (pPluginList) { qtractorMidiManager *pMidiManager = pPluginList->midiManager(); if (pMidiManager && pMidiManager->isAudioOutputMonitor()) { pMidiMixerMeter->setAudioOutputMonitor( pMidiManager->audioOutputMonitor()); } else { pMidiMixerMeter->setAudioOutputMonitor(nullptr); } } } } if (m_pMixerMeter) m_pMixerMeter->setMonitor(pMonitor); } qtractorMonitor *qtractorMixerStrip::monitor (void) const { return (m_pMixerMeter ? m_pMixerMeter->monitor() : nullptr); } // Common mixer-strip caption title updater. void qtractorMixerStrip::updateName (void) { QIcon icon; QString sName; qtractorTrack::TrackType meterType = qtractorTrack::None; if (m_pTrack) { meterType = m_pTrack->trackType(); sName = m_pTrack->shortTrackName(); QPalette pal(m_pLabel->palette()); const QColor& bg = m_pTrack->foreground().lighter(); QColor fg = m_pTrack->background().lighter(); if (qAbs(bg.value() - fg.value()) < 0x33) fg.setHsv(fg.hue(), fg.saturation(), (255 - fg.value()), 200); pal.setColor(QPalette::Button, bg); pal.setColor(QPalette::ButtonText, fg); m_pLabel->setPalette(pal); const QString& sTrackIcon = m_pTrack->trackIcon(); icon = QIcon::fromTheme(sTrackIcon); if (icon.isNull()) icon = QIcon(sTrackIcon); } else if (m_pBus) { meterType = m_pBus->busType(); sName = m_pBus->busName(); } QString sType; switch (meterType) { case qtractorTrack::Audio: if (icon.isNull()) icon = QIcon::fromTheme("trackAudio"); sType = tr("(Audio)"); break; case qtractorTrack::Midi: if (icon.isNull()) icon = QIcon::fromTheme("trackMidi"); sType = tr("(MIDI)"); break; case qtractorTrack::None: default: sType = tr("(None)"); break; } m_pLabel->setIcon(icon); m_pLabel->setText(sName); m_pLabel->update(); // Make sure icon and text gets visibly updated! if (m_pTrack) QFrame::setToolTip(m_pTrack->trackName() + ' ' + sType); else if (m_pBus) { const QString& sMode = (m_busMode & qtractorBus::Input ? tr("In") : tr("Out")); QFrame::setToolTip(sName + ' ' + sMode + ' ' + sType); } } // MIDI (channel) label updater. void qtractorMixerStrip::updateMidiLabel (void) { if (m_pTrack == nullptr) return; if (m_pMidiLabel == nullptr) return; QString sOmni; if (m_pTrack->isMidiOmni()) sOmni += '*'; m_pMidiLabel->setText( sOmni + QString::number(m_pTrack->midiChannel() + 1)); } // Mixer strip clear/suspend delegates void qtractorMixerStrip::clear (void) { m_pPluginListView->setEnabled(false); m_pPluginListView->setPluginList(nullptr); m_pRack->updateStrip(this, nullptr); } // Bus property accessors. void qtractorMixerStrip::setBus ( qtractorBus *pBus ) { // Must be actual bus... if (m_pBus == nullptr || pBus == nullptr) return; m_pBus = pBus; if (m_busMode & qtractorBus::Input) { m_pRack->updateStrip(this, m_pBus->monitor_in()); m_pPluginListView->setPluginList(m_pBus->pluginList_in()); } else { m_pRack->updateStrip(this, m_pBus->monitor_out()); m_pPluginListView->setPluginList(m_pBus->pluginList_out()); } m_pPluginListView->setEnabled(true); m_pMonitorButton->setBus(m_pBus); updateName(); } // Track property accessors. void qtractorMixerStrip::setTrack ( qtractorTrack *pTrack ) { // Must be actual track... if (m_pTrack == nullptr || pTrack == nullptr) return; m_pTrack = pTrack; m_pRack->updateStrip(this, m_pTrack->monitor()); m_pPluginListView->setPluginList(m_pTrack->pluginList()); m_pPluginListView->setEnabled(true); m_pMonitorButton->setTrack(m_pTrack); m_pRecordButton->setTrack(m_pTrack); m_pMuteButton->setTrack(m_pTrack); m_pSoloButton->setTrack(m_pTrack); updateMidiLabel(); updateName(); } // Selection methods. void qtractorMixerStrip::setSelected ( bool bSelected ) { m_bSelected = bSelected; QStyle *pStyle = QFrame::style(); if (pStyle) pStyle->unpolish(this); QFrame::setProperty("selected", m_bSelected); QPalette pal; QColor rgbBase; if (m_bSelected) { rgbBase = pal.midlight().color(); pal.setColor(QPalette::WindowText, pal.highlightedText().color()); pal.setColor(QPalette::Window, rgbBase.darker(150)); } else { rgbBase = pal.window().color(); pal.setColor(QPalette::WindowText, pal.windowText().color()); pal.setColor(QPalette::Window, rgbBase); } #ifdef CONFIG_GRADIENT const QSize& hint = QFrame::sizeHint(); QLinearGradient grad(0, 0, hint.width() >> 1, hint.height()); if (m_bSelected) { grad.setColorAt(0.4, rgbBase.darker(150)); grad.setColorAt(0.6, rgbBase.darker(130)); grad.setColorAt(1.0, rgbBase.darker()); } else { grad.setColorAt(0.4, rgbBase); grad.setColorAt(0.6, rgbBase.lighter(105)); grad.setColorAt(1.0, rgbBase.darker(130)); } const QBrush brush = pal.brush(QPalette::Window); pal.setBrush(QPalette::Window, grad); #endif QFrame::setPalette(pal); #ifdef CONFIG_GRADIENT pal.setBrush(QPalette::Window, brush); #endif m_pPluginListView->setPalette(pal); m_pMonitorButton->setPalette(pal); if (m_pBusButton) m_pBusButton->setPalette(pal); if (m_pRecordButton) m_pRecordButton->setPalette(pal); if (m_pMuteButton) m_pMuteButton->setPalette(pal); if (m_pSoloButton) m_pSoloButton->setPalette(pal); if (m_pMixerMeter) m_pMixerMeter->setPalette(pal); #ifdef CONFIG_GRADIENT if (m_pRecordButton) m_pRecordButton->observer()->update(true); if (m_pMuteButton) m_pMuteButton->observer()->update(true); if (m_pSoloButton) m_pSoloButton->observer()->update(true); #endif if (pStyle) pStyle->polish(this); // QFrame::update(); } // Mouse selection event handlers. void qtractorMixerStrip::mousePressEvent ( QMouseEvent *pMouseEvent ) { QFrame::mousePressEvent(pMouseEvent); if (m_pTrack) m_pRack->setSelectedStrip(this); } // Mouse selection event handlers. void qtractorMixerStrip::mouseDoubleClickEvent ( QMouseEvent */*pMouseEvent*/ ) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; if (m_pTrack) { pMainForm->trackProperties(); } else { busPropertiesSlot(); } } // Bus connections dispatcher. void qtractorMixerStrip::busConnections ( qtractorBus::BusMode busMode ) { if (m_pBus == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; // Here we go... pMainForm->connections()->showBus(m_pBus, busMode); } // Bus pass-through dispatcher. void qtractorMixerStrip::busMonitor ( bool bMonitor ) { if (m_pBus == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Here we go... pSession->execute( new qtractorBusMonitorCommand(m_pBus, bMonitor)); } // Track monitor dispatcher. void qtractorMixerStrip::trackMonitor ( bool bMonitor ) { if (m_pTrack == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Here we go... pSession->execute( new qtractorTrackMonitorCommand(m_pTrack, bMonitor)); } // Show/edit bus input connections. void qtractorMixerStrip::busInputsSlot (void) { busConnections(qtractorBus::Input); } // Show/edit bus output connections. void qtractorMixerStrip::busOutputsSlot (void) { busConnections(qtractorBus::Output); } // Toggle bus passthru flag. void qtractorMixerStrip::busMonitorSlot (void) { if (m_pBus) busMonitor(!m_pBus->isMonitor()); } // Show/edit bus properties form. void qtractorMixerStrip::busPropertiesSlot (void) { if (m_pBus) { qtractorBusForm busForm(m_pRack); busForm.setBus(m_pBus); busForm.exec(); } } // Bus connections button slot void qtractorMixerStrip::busButtonSlot (void) { busConnections(m_busMode); } // Pan-meter slider value change slot. void qtractorMixerStrip::panningChangedSlot ( float fPanning ) { if (m_pMixerMeter == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMixerStrip[%p]::panningChangedSlot(%g)", this, fPanning); #endif qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Put it in the form of an undoable command... if (m_pTrack) { pSession->execute( new qtractorTrackPanningCommand(m_pTrack, fPanning)); } else if (m_pBus) { pSession->execute( new qtractorBusPanningCommand(m_pBus, m_busMode, fPanning)); } } // Gain-meter slider value change slot. void qtractorMixerStrip::gainChangedSlot ( float fGain ) { if (m_pMixerMeter == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMixerStrip[%p]::gainChangedSlot(%g)", this, fGain); #endif qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Put it in the form of an undoable command... if (m_pTrack) { pSession->execute( new qtractorTrackGainCommand(m_pTrack, fGain)); } else if (m_pBus) { pSession->execute( new qtractorBusGainCommand(m_pBus, m_busMode, fGain)); } } // Update a MIDI mixer strip, given its MIDI manager handle. void qtractorMixerStrip::updateMidiManager ( qtractorMidiManager *pMidiManager ) { qtractorMidiMixerMeter *pMidiMixerMeter = static_cast (m_pMixerMeter); if (pMidiMixerMeter == nullptr) return; // Apply the combo-meter posssibility... if (pMidiManager && pMidiManager->isAudioOutputMonitor()) { pMidiMixerMeter->setAudioOutputMonitor( pMidiManager->audioOutputMonitor()); } else { pMidiMixerMeter->setAudioOutputMonitor(nullptr); } } // Retrieve the MIDI manager from a mixer strip, if any.... qtractorMidiManager *qtractorMixerStrip::midiManager (void) const { qtractorPluginList *pPluginList = nullptr; if (m_pTrack && m_pTrack->trackType() == qtractorTrack::Midi) { pPluginList = m_pTrack->pluginList(); } else if (m_pBus && m_pBus->busType() == qtractorTrack::Midi) { if ((m_busMode & qtractorBus::Input) && (m_pBus->busMode() & qtractorBus::Input)) { pPluginList = m_pBus->pluginList_in(); } else if ((m_busMode & qtractorBus::Output) && (m_pBus->busMode() & qtractorBus::Output)) { pPluginList = m_pBus->pluginList_out(); } } return (pPluginList ? pPluginList->midiManager() : nullptr); } //---------------------------------------------------------------------------- // qtractorMixerRackWidget -- Meter bridge rack widget impl. // Constructor. qtractorMixerRackWidget::qtractorMixerRackWidget ( qtractorMixerRack *pRack ) : QScrollArea(pRack), m_pRack(pRack) { m_pWorkspaceLayout = new QGridLayout(); m_pWorkspaceLayout->setContentsMargins(0, 0, 0, 0); m_pWorkspaceLayout->setSpacing(0); m_pWorkspaceWidget = new QWidget(this); m_pWorkspaceWidget->setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); m_pWorkspaceWidget->setLayout(m_pWorkspaceLayout); QScrollArea::viewport()->setBackgroundRole(QPalette::Dark); // QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwayOn); QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QScrollArea::setWidget(m_pWorkspaceWidget); } // Default destructor. qtractorMixerRackWidget::~qtractorMixerRackWidget (void) { // No need to delete child widgets, Qt does it all for us } // Add/remove a mixer strip to/from rack workspace. void qtractorMixerRackWidget::addStrip ( qtractorMixerStrip *pStrip ) { m_pWorkspaceLayout->addWidget(pStrip, 0, m_pWorkspaceLayout->count()); } void qtractorMixerRackWidget::removeStrip ( qtractorMixerStrip *pStrip ) { m_pWorkspaceLayout->removeWidget(pStrip); } // Resize event handler. void qtractorMixerRackWidget::resizeEvent ( QResizeEvent *pResizeEvent ) { QScrollArea::resizeEvent(pResizeEvent); updateWorkspace(); } // Context menu request event handler. void qtractorMixerRackWidget::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { // Maybe it's a track strip if (m_pRack->isTrackRack()) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { pMainForm->stabilizeForm(); pMainForm->trackMenu()->exec(pContextMenuEvent->globalPos()); } // Bail out... return; } // Definitely a bus strip. qtractorBus *pBus = nullptr; qtractorMixerStrip *pStrip = m_pRack->stripAt(pContextMenuEvent->pos()); if (pStrip) pBus = pStrip->bus(); // Build the device context menu... QMenu menu(this); QAction *pAction; bool bEnabled; pAction = menu.addAction(tr("&Inputs")); bEnabled = (pStrip && pBus && (pBus->busMode() & qtractorBus::Input)); if (bEnabled) { QObject::connect( pAction, SIGNAL(triggered(bool)), pStrip, SLOT(busInputsSlot())); } pAction->setEnabled(bEnabled); pAction = menu.addAction(tr("&Outputs")); bEnabled = (pStrip && pBus && (pBus->busMode() & qtractorBus::Output)); if (bEnabled) { QObject::connect( pAction, SIGNAL(triggered(bool)), pStrip, SLOT(busOutputsSlot())); } pAction->setEnabled(bEnabled); menu.addSeparator(); pAction = menu.addAction(tr("&Monitor")); pAction->setCheckable(true); bEnabled = (pStrip && pBus && ( (pBus->busMode() & qtractorBus::Duplex) == qtractorBus::Duplex)); if (bEnabled) { QObject::connect( pAction, SIGNAL(triggered(bool)), pStrip, SLOT(busMonitorSlot())); pAction->setChecked(pBus->isMonitor()); } pAction->setEnabled(bEnabled); menu.addSeparator(); pAction = menu.addAction(tr("&Buses...")); if (pStrip && pBus) { QObject::connect( pAction, SIGNAL(triggered(bool)), pStrip, SLOT(busPropertiesSlot())); } else { QObject::connect( pAction, SIGNAL(triggered(bool)), m_pRack, SLOT(busPropertiesSlot())); } menu.addSeparator(); pAction = menu.addAction( QIcon::fromTheme("trackAudio"), tr("&Audio"), m_pRack, SLOT(busAudioStripsSlot())); pAction->setCheckable(true); pAction->setChecked(!m_pRack->isStripTypeHidden(qtractorTrack::Audio)); pAction = menu.addAction( QIcon::fromTheme("trackMidi"), tr("&MIDI"), m_pRack, SLOT(busMidiStripsSlot())); pAction->setCheckable(true); pAction->setChecked(!m_pRack->isStripTypeHidden(qtractorTrack::Midi)); menu.exec(pContextMenuEvent->globalPos()); } // Mouse click event handler. void qtractorMixerRackWidget::mousePressEvent ( QMouseEvent *pMouseEvent ) { if (!m_pWorkspaceWidget->rect().contains(pMouseEvent->pos())) m_pRack->mixer()->setCurrentTrack(nullptr); QScrollArea::mousePressEvent(pMouseEvent); } // Multi-row workspace layout method. void qtractorMixerRackWidget::updateWorkspace (void) { QWidget *pViewport = QScrollArea::viewport(); const int h = pViewport->height(); const int w = pViewport->width(); const int nitems = m_pWorkspaceLayout->count(); if (nitems > 0) { const int nrows = h / sizeHint().height(); const int ncols = nitems / (nrows > 0 ? nrows : 1); QLayoutItem *items[nitems]; for (int i = 0; i < nitems; ++i) items[i] = m_pWorkspaceLayout->takeAt(0); int row = 0; int col = 0; int wth = 0; for (int i = 0; i < nitems; ++i) { QLayoutItem *item = items[i]; m_pWorkspaceLayout->addItem(item, row, col++); // Auto-grid layout... const int wi = item->sizeHint().width(); wth += wi; if (wth > (w - wi) && row < nrows && col > ncols) { wth = 0; col = 0; ++row; } } } // m_pWorkspaceWidget->setMinimumWidth(w); m_pWorkspaceWidget->setFixedHeight(h); m_pWorkspaceWidget->adjustSize(); } //---------------------------------------------------------------------------- // qtractorMixerRackTitleBarWidget -- Mixer strip rack title-bar. class qtractorMixerRackTitleBarWidget : public QWidget { public: // Constructor. qtractorMixerRackTitleBarWidget ( qtractorMixerRack *pRack, const QString& sTitle ) : QWidget(pRack) { const QFont& font = QWidget::font(); QWidget::setFont(QFont(font.family(), font.pointSize() - 3)); QHBoxLayout *pHBoxLayout = new QHBoxLayout(); pHBoxLayout->setSpacing(4); pHBoxLayout->setContentsMargins(2, 2, 2, 2); #if 0 QFrame *pLeftFrame = new QFrame(); pLeftFrame->setFrameStyle(QFrame::HLine | QFrame::Sunken); pHBoxLayout->addWidget(pLeftFrame, 1); #endif pHBoxLayout->addWidget(new QLabel(sTitle)); #if 0 QFrame *pRightFrame = new QFrame(); pRightFrame->setFrameStyle(QFrame::HLine | QFrame::Sunken); pHBoxLayout->addWidget(pRightFrame, 1); #endif QWidget::setLayout(pHBoxLayout); } }; //---------------------------------------------------------------------------- // qtractorMixerRack -- Mixer strip rack. // Constructor. qtractorMixerRack::qtractorMixerRack ( qtractorMixer *pMixer, const QString& sTitle ) : QDockWidget(sTitle, pMixer), m_pMixer(pMixer), m_pSelectedStrip(nullptr), m_pSelectedStrip2(nullptr), m_pRackWidget(new qtractorMixerRackWidget(this)), m_hiddenStripType(qtractorTrack::None) { QDockWidget::setObjectName(sTitle); // TODO: make this an unique-id. QDockWidget::setTitleBarWidget( new qtractorMixerRackTitleBarWidget(this, sTitle)); QDockWidget::setToolTip(sTitle); QDockWidget::setWidget(m_pRackWidget); QDockWidget::setFeatures( QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); // FIXME: is floatable necessary? // QDockWidget::setAllowedAreas(Qt::AllDockWidgetAreas); } // Default destructor. qtractorMixerRack::~qtractorMixerRack (void) { // No need to delete child widgets, Qt does it all for us clear(); } // Add a mixer strip to rack list. void qtractorMixerRack::addStrip ( qtractorMixerStrip *pStrip ) { // Add this to the workspace layout... m_pRackWidget->addStrip(pStrip); qtractorMonitor *pMonitor = pStrip->monitor(); if (pMonitor) m_strips.insert(pMonitor, pStrip); pStrip->show(); } // Remove a mixer strip from rack list. void qtractorMixerRack::removeStrip ( qtractorMixerStrip *pStrip ) { // Don't let current selection hanging... if (m_pSelectedStrip == pStrip || m_pSelectedStrip2 == pStrip) { m_pSelectedStrip = nullptr; m_pSelectedStrip2 = nullptr; } // Remove this from the workspace layout... m_pRackWidget->removeStrip(pStrip); pStrip->hide(); qtractorMonitor *pMonitor = pStrip->monitor(); if (pMonitor) { m_strips.remove(pMonitor); delete pStrip; } m_pRackWidget->workspace()->adjustSize(); } // Find a mixer strip, given its rack workspace position. qtractorMixerStrip *qtractorMixerRack::stripAt ( const QPoint& pos ) const { Strips::ConstIterator iter = m_strips.constBegin(); const Strips::ConstIterator& iter_end = m_strips.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMixerStrip *pStrip = iter.value(); if (pStrip && pStrip->frameGeometry().contains(pos)) return pStrip; } return nullptr; } // Find a mixer strip, given its monitor handle. qtractorMixerStrip *qtractorMixerRack::findStrip ( qtractorMonitor *pMonitor ) const { return m_strips.value(pMonitor, nullptr); } // Update a mixer strip on rack list. void qtractorMixerRack::updateStrip ( qtractorMixerStrip *pStrip, qtractorMonitor *pMonitor ) { qtractorMonitor *pOldMonitor = pStrip->monitor(); if (pOldMonitor) m_strips.remove(pOldMonitor); pStrip->setMonitor(pMonitor); if (pMonitor) m_strips.insert(pMonitor, pStrip); } // Complete rack recycle. void qtractorMixerRack::clear (void) { m_pSelectedStrip = nullptr; m_pSelectedStrip2 = nullptr; qDeleteAll(m_strips); m_strips.clear(); } // Selection stuff. void qtractorMixerRack::setSelectedStrip ( qtractorMixerStrip *pStrip ) { if (m_pSelectedStrip) m_pSelectedStrip->setSelected(false); m_pSelectedStrip = pStrip; if (m_pSelectedStrip) { m_pSelectedStrip->setSelected(true); const int wm = (m_pSelectedStrip->width() >> 1); m_pRackWidget->ensureVisible( m_pSelectedStrip->pos().x() + wm, 0, wm, 0); } emit selectionChanged(); } void qtractorMixerRack::setSelectedStrip2 ( qtractorMixerStrip *pStrip ) { if (pStrip && pStrip == m_pSelectedStrip) return; if (m_pSelectedStrip2 && m_pSelectedStrip2 == m_pSelectedStrip) m_pSelectedStrip2 = nullptr; if (m_pSelectedStrip2) m_pSelectedStrip2->setSelected(false); m_pSelectedStrip2 = pStrip; if (m_pSelectedStrip2) { m_pSelectedStrip2->setSelected(true); const int wm = (m_pSelectedStrip2->width() >> 1); m_pRackWidget->ensureVisible( m_pSelectedStrip2->pos().x() + wm, 0, wm, 0); } } // Hacko-list-management marking... void qtractorMixerRack::markStrips ( int iMark ) { m_pRackWidget->workspace()->setUpdatesEnabled(false); Strips::ConstIterator strip = m_strips.constBegin(); const Strips::ConstIterator& strip_end = m_strips.constEnd(); for ( ; strip != strip_end; ++strip) strip.value()->setMark(iMark); } void qtractorMixerRack::cleanStrips ( int iMark ) { Strips::Iterator strip = m_strips.begin(); const Strips::Iterator& strip_end = m_strips.end(); while (strip != strip_end) { qtractorMixerStrip *pStrip = strip.value(); if (pStrip->mark() == iMark) { // Remove from the workspace layout... m_pRackWidget->removeStrip(pStrip); // Don't let current selection hanging... if (m_pSelectedStrip == pStrip) m_pSelectedStrip = nullptr; // Hide strip... pStrip->hide(); // Remove from list... strip = m_strips.erase(strip); // and finally get rid of it. delete pStrip; } else ++strip; } m_pRackWidget->updateWorkspace(); m_pRackWidget->workspace()->setUpdatesEnabled(true); } // Find a mixer strip, given its MIDI-manager handle. qtractorMixerStrip *qtractorMixerRack::findMidiManagerStrip ( qtractorMidiManager *pMidiManager ) const { if (pMidiManager == nullptr) return nullptr; Strips::ConstIterator strip = m_strips.constBegin(); const Strips::ConstIterator& strip_end = m_strips.constEnd(); for ( ; strip != strip_end; ++strip) { qtractorMixerStrip *pStrip = strip.value(); if (pStrip->midiManager() == pMidiManager) return pStrip; } return nullptr; } // Find all the MIDI mixer strip, given an audio output bus handle. QList qtractorMixerRack::findAudioOutputBusStrips ( qtractorAudioBus *pAudioOutputBus ) const { QList strips; if (pAudioOutputBus == nullptr) return strips; Strips::ConstIterator strip = m_strips.constBegin(); const Strips::ConstIterator& strip_end = m_strips.constEnd(); for ( ; strip != strip_end; ++strip) { qtractorMixerStrip *pStrip = strip.value(); qtractorMidiManager *pMidiManager = pStrip->midiManager(); if (pMidiManager && pMidiManager->audioOutputBus() == pAudioOutputBus) { strips.append(pStrip); } } return strips; } // Whether strip type is hidden. void qtractorMixerRack::setStripTypeHidden ( qtractorTrack::TrackType stripType ) { m_hiddenStripType = stripType; } bool qtractorMixerRack::isStripTypeHidden ( qtractorTrack::TrackType stripType ) const { return (m_hiddenStripType == stripType); } // Check for this being any special rack. bool qtractorMixerRack::isInputRack (void) const { return (m_pMixer->inputRack() == this); } bool qtractorMixerRack::isTrackRack (void) const { return (m_pMixer->trackRack() == this); } bool qtractorMixerRack::isOutputRack (void) const { return (m_pMixer->outputRack() == this); } // Show/edit bus properties form. void qtractorMixerRack::busPropertiesSlot (void) { qtractorBusForm(this).exec(); } void qtractorMixerRack::busAudioStripsSlot (void) { if (m_hiddenStripType != qtractorTrack::Audio) m_hiddenStripType = qtractorTrack::Audio; else m_hiddenStripType = qtractorTrack::None; m_pMixer->updateBuses(true); } void qtractorMixerRack::busMidiStripsSlot (void) { if (m_hiddenStripType != qtractorTrack::Midi) m_hiddenStripType = qtractorTrack::Midi; else m_hiddenStripType = qtractorTrack::None; m_pMixer->updateBuses(true); } //---------------------------------------------------------------------------- // qtractorMixer -- Mixer widget. // Constructor. qtractorMixer::qtractorMixer ( QWidget *pParent, Qt::WindowFlags wflags ) : QMainWindow(pParent, wflags) { // Surely a name is crucial (e.g. for storing geometry settings) QMainWindow::setObjectName("qtractorMixer"); m_pInputRack = new qtractorMixerRack(this, tr("Inputs")); m_pTrackRack = new qtractorMixerRack(this, tr("Tracks")); m_pOutputRack = new qtractorMixerRack(this, tr("Outputs")); // Some specialties to this kind of dock window... QMainWindow::setMinimumWidth(480); QMainWindow::setMinimumHeight(320); // Finally set the default caption and tooltip. const QString& sTitle = tr("Mixer"); QMainWindow::setWindowTitle(sTitle); QMainWindow::setWindowIcon(QIcon::fromTheme("qtractorMixer")); QMainWindow::setToolTip(sTitle); QMainWindow::addDockWidget(Qt::LeftDockWidgetArea, m_pInputRack); QMainWindow::addDockWidget(Qt::RightDockWidgetArea, m_pOutputRack); m_pTrackRack->setFeatures(QDockWidget::NoDockWidgetFeatures); m_pTrackRack->setAllowedAreas(Qt::NoDockWidgetArea); QMainWindow::setCentralWidget(m_pTrackRack); // Get previously saved splitter sizes... loadMixerState(); } // Default destructor. qtractorMixer::~qtractorMixer (void) { // Save splitter sizes... if (QMainWindow::isVisible()) saveMixerState(); // No need to delete child widgets, Qt does it all for us } // Just about to notify main-window that we're closing. void qtractorMixer::closeEvent ( QCloseEvent * /*pCloseEvent*/ ) { // Save splitter sizes... saveMixerState(); QMainWindow::hide(); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->stabilizeForm(); } // Get previously saved dockable state... void qtractorMixer::loadMixerState (void) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { QSettings& settings = pOptions->settings(); settings.beginGroup("Mixer"); const QByteArray& aDockables = pOptions->settings().value("/Layout/DockWindows").toByteArray(); if (!aDockables.isEmpty()) QMainWindow::restoreState(aDockables); settings.endGroup(); } } // Save dockables state... void qtractorMixer::saveMixerState (void) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { QSettings& settings = pOptions->settings(); settings.beginGroup("Mixer"); settings.setValue("/Layout/DockWindows", QMainWindow::saveState()); settings.endGroup(); } } // Update mixer rack, checking if given monitor already exists. void qtractorMixer::updateBusStrip ( qtractorMixerRack *pRack, qtractorBus *pBus, qtractorBus::BusMode busMode, bool bReset ) { qtractorMonitor *pMonitor = (busMode == qtractorBus::Input ? pBus->monitor_in() : pBus->monitor_out()); qtractorMixerStrip *pStrip = nullptr; if (!pRack->isStripTypeHidden(pBus->busType())) { pStrip = pRack->findStrip(pMonitor); if (pStrip == nullptr) { pStrip = new qtractorMixerStrip(pRack, pBus, busMode); pRack->addStrip(pStrip); } } if (pStrip) { pStrip->setMark(0); if (bReset) pStrip->setBus(pBus); } pBus->mapControllers(busMode); } void qtractorMixer::updateTrackStrip ( qtractorTrack *pTrack, bool bReset ) { qtractorMixerStrip *pStrip = m_pTrackRack->findStrip(pTrack->monitor()); if (pStrip == nullptr) { m_pTrackRack->addStrip(new qtractorMixerStrip(m_pTrackRack, pTrack)); } else { pStrip->setMark(0); if (bReset) pStrip->setTrack(pTrack); } qtractorCurveList *pCurveList = pTrack->curveList(); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pCurveList && pMainForm && pMainForm->tracks()) { pMainForm->tracks()->updateTrackList(pTrack); QObject::connect( pCurveList->proxy(), SIGNAL(update()), pMainForm->tracks(), SLOT(updateTrackView())); } } // Current selected track accessors. void qtractorMixer::setCurrentTrack ( qtractorTrack *pTrack ) { qtractorMixerStrip *pInputStrip = nullptr; qtractorMixerStrip *pTrackStrip = nullptr; qtractorMixerStrip *pOutputStrip = nullptr; qtractorMixerStrip *pOutputStrip2 = nullptr; if (pTrack) { qtractorBus *pInputBus = pTrack->inputBus(); if (pInputBus) pInputStrip = m_pInputRack->findStrip(pInputBus->monitor_in()); pTrackStrip = m_pTrackRack->findStrip(pTrack->monitor()); qtractorBus *pOutputBus = pTrack->outputBus(); if (pOutputBus) pOutputStrip = m_pOutputRack->findStrip(pOutputBus->monitor_out()); if (pTrack->trackType() == qtractorTrack::Midi && pTrack->pluginList()) { qtractorMidiManager *pMidiManager = (pTrack->pluginList())->midiManager(); if (pMidiManager && pMidiManager->isAudioOutputMonitor() && !pMidiManager->isAudioOutputBus()) { qtractorAudioBus *pAudioOutputBus = pMidiManager->audioOutputBus(); if (pAudioOutputBus) { pOutputStrip2 = m_pOutputRack->findStrip( pAudioOutputBus->monitor_out()); } } } } m_pInputRack->setSelectedStrip(pInputStrip); m_pTrackRack->setSelectedStrip(pTrackStrip); m_pOutputRack->setSelectedStrip(pOutputStrip); m_pOutputRack->setSelectedStrip2(pOutputStrip2); } qtractorTrack *qtractorMixer::currentTrack (void) const { qtractorMixerStrip *pTrackStrip = m_pTrackRack->selectedStrip(); if (pTrackStrip) return pTrackStrip->track(); else return nullptr; } // Update buses'racks. void qtractorMixer::updateBuses ( bool bReset ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTrack *pCurrentTrack = currentTrack(); if (bReset) { m_pInputRack->clear(); m_pOutputRack->clear(); } m_pInputRack->markStrips(1); m_pOutputRack->markStrips(1); // Audio buses first... QListIterator audio_iter(pSession->audioEngine()->buses2()); while (audio_iter.hasNext()) { qtractorBus *pBus = audio_iter.next(); if (pBus == nullptr) continue; if (pBus->busMode() & qtractorBus::Input) updateBusStrip(m_pInputRack, pBus, qtractorBus::Input); if (pBus->busMode() & qtractorBus::Output) updateBusStrip(m_pOutputRack, pBus, qtractorBus::Output); } // MIDI buses are next... QListIterator midi_iter(pSession->midiEngine()->buses2()); while (midi_iter.hasNext()) { qtractorBus *pBus = midi_iter.next(); if (pBus == nullptr) continue; if (pBus->busMode() & qtractorBus::Input) updateBusStrip(m_pInputRack, pBus, qtractorBus::Input); if (pBus->busMode() & qtractorBus::Output) updateBusStrip(m_pOutputRack, pBus, qtractorBus::Output); } m_pOutputRack->cleanStrips(1); m_pInputRack->cleanStrips(1); setCurrentTrack(pCurrentTrack); } // Update tracks'rack. void qtractorMixer::updateTracks ( bool bReset ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTrack *pCurrentTrack = currentTrack(); bool bCurrentTrack = false; if (bReset) m_pTrackRack->clear(); m_pTrackRack->markStrips(1); for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { updateTrackStrip(pTrack); if (pCurrentTrack == pTrack) bCurrentTrack = true; } m_pTrackRack->cleanStrips(1); setCurrentTrack(bCurrentTrack ? pCurrentTrack : nullptr); } // Update a MIDI mixer strip, given its MIDI manager handle. void qtractorMixer::updateMidiManagerStrip ( qtractorMidiManager *pMidiManager ) { qtractorTrack *pCurrentTrack = currentTrack(); qtractorMixerStrip * pStrip = m_pTrackRack->findMidiManagerStrip(pMidiManager); if (pStrip == nullptr) pStrip = m_pOutputRack->findMidiManagerStrip(pMidiManager); if (pStrip == nullptr) pStrip = m_pInputRack->findMidiManagerStrip(pMidiManager); if (pStrip) pStrip->updateMidiManager(pMidiManager); setCurrentTrack(pCurrentTrack); } // Find a MIDI mixer strip, given its MIDI manager handle. QList qtractorMixer::findAudioOutputBusStrips ( qtractorAudioBus *pAudioOutputBus ) const { QList strips; strips.append(m_pTrackRack->findAudioOutputBusStrips(pAudioOutputBus)); strips.append(m_pOutputRack->findAudioOutputBusStrips(pAudioOutputBus)); strips.append(m_pInputRack->findAudioOutputBusStrips(pAudioOutputBus)); return strips; } // Complete mixer recycle. void qtractorMixer::clear (void) { m_pInputRack->clear(); m_pTrackRack->clear(); m_pOutputRack->clear(); } // Update mixer automatic multi-row strip/grid layout. void qtractorMixer::updateWorkspaces (void) { m_pInputRack->updateWorkspace(); m_pTrackRack->updateWorkspace(); m_pOutputRack->updateWorkspace(); } // Keyboard event handler. void qtractorMixer::keyPressEvent ( QKeyEvent *pKeyEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMixer::keyPressEvent(%d)", pKeyEvent->key()); #endif const int iKey = pKeyEvent->key(); switch (iKey) { case Qt::Key_Escape: close(); break; default: QMainWindow::keyPressEvent(pKeyEvent); break; } } // Current selected output bus (usually an Aux-Send target) accessors. void qtractorMixer::setSelectedOutputBus ( qtractorBus *pOutputBus ) { qtractorMixerStrip *pOutputStrip2 = nullptr; if (pOutputBus && (pOutputBus->busMode() & qtractorBus::Output)) pOutputStrip2 = m_pOutputRack->findStrip(pOutputBus->monitor_out()); m_pOutputRack->setSelectedStrip2(pOutputStrip2); } qtractorBus *qtractorMixer::selectedOutputBus (void) const { qtractorBus *pOutputBus = nullptr; qtractorMixerStrip *pOutputStrip2 = m_pOutputRack->selectedStrip2(); if (pOutputStrip2) pOutputBus = pOutputStrip2->bus(); return pOutputBus; } // end of qtractorMixer.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiCursor.cpp0000644000000000000000000000013215124701674017656 xustar0030 mtime=1767080892.790263466 30 atime=1767080892.790263466 30 ctime=1767080892.790263466 qtractor-1.5.11/src/qtractorMidiCursor.cpp0000644000175000001440000000544415124701674017655 0ustar00rncbcusers// qtractorMidiCursor.cpp // /**************************************************************************** Copyright (C) 2005-2019, 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 "qtractorMidiCursor.h" #include "qtractorMidiSequence.h" //------------------------------------------------------------------------- // qtractorMidiCursor -- MIDI event cursor capsule. // Constructor. qtractorMidiCursor::qtractorMidiCursor (void) : m_pEvent(nullptr), m_iTime(0) { } // Intra-sequence tick/time positioning seek. qtractorMidiEvent *qtractorMidiCursor::seek ( qtractorMidiSequence *pSeq, unsigned long iTime ) { // Plain reset... if (iTime == 0) { m_pEvent = pSeq->events().first(); } else if (iTime > m_iTime) { // Seek forward... if (m_pEvent == nullptr) m_pEvent = pSeq->events().first(); while (m_pEvent && m_pEvent->next() && (m_pEvent->next())->time() < iTime) m_pEvent = m_pEvent->next(); if (m_pEvent == nullptr) m_pEvent = pSeq->events().last(); } else if (iTime < m_iTime) { // Seek backward... if (m_pEvent == nullptr) m_pEvent = pSeq->events().last(); while (m_pEvent && m_pEvent->time() >= iTime) m_pEvent = m_pEvent->prev(); if (m_pEvent == nullptr) m_pEvent = pSeq->events().first(); } // Done. m_iTime = iTime; return m_pEvent; } // Intra-sequence tick/time positioning reset (seek forward). qtractorMidiEvent *qtractorMidiCursor::reset ( qtractorMidiSequence *pSeq, unsigned long iTime ) { // Reset-seek forward... if (m_iTime >= iTime) m_pEvent = nullptr; if (m_pEvent == nullptr) m_pEvent = pSeq->events().first(); while (m_pEvent && m_pEvent->time() + m_pEvent->duration() < iTime) m_pEvent = m_pEvent->next(); while (m_pEvent && m_pEvent->time() > iTime) m_pEvent = m_pEvent->prev(); if (m_pEvent == nullptr) m_pEvent = pSeq->events().first(); // That was it... m_iTime = iTime; return m_pEvent; } // Complete/clear positioning reset. void qtractorMidiCursor::clear (void) { m_pEvent = nullptr; m_iTime = 0; } // end of qtractorMidiCursor.cpp qtractor-1.5.11/src/PaxHeaders/qtractorActionControl.cpp0000644000000000000000000000013215124701674020354 xustar0030 mtime=1767080892.777862586 30 atime=1767080892.777862586 30 ctime=1767080892.777862586 qtractor-1.5.11/src/qtractorActionControl.cpp0000644000175000001440000001254315124701674020351 0ustar00rncbcusers// qtractorActionControl.cpp // /**************************************************************************** Copyright (C) 2005-2022, 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 "qtractorAbout.h" #include "qtractorActionControl.h" #include #include //---------------------------------------------------------------------- // class qtractorActionControl::MidiObserver -- impl. // // MIDI observer ctor. qtractorActionControl::MidiObserver::MidiObserver ( QAction *pAction ) : qtractorMidiControlObserver(nullptr), m_pAction(pAction) { m_subject.setName(menuActionText(pAction, pAction->text()).remove('&')); m_subject.setInteger(true); m_subject.setToggled(pAction->isCheckable()); qtractorMidiControlObserver::setSubject(&m_subject); qtractorMidiControlObserver::setHook(true); qtractorMidiControlObserver::setLatch(false); qtractorMidiControlObserver::setTriggered(true); } // MIDI observer updater. void qtractorActionControl::MidiObserver::update ( bool bUpdate ) { #ifdef CONFIG_DEBUG qDebug("qtractorActionControl::MidiObserver[%p]::update(%d)", this, int(bUpdate)); #endif qtractorActionControl *pActionControl = qtractorActionControl::getInstance(); if (pActionControl && m_pAction->isEnabled()) { const bool bBlockSignals = pActionControl->blockSignals(true); m_pAction->activate(QAction::Trigger); pActionControl->blockSignals(bBlockSignals); } qtractorMidiControlObserver::update(bUpdate); } //---------------------------------------------------------------------- // class qtractorActionControl -- (QAction) MIDI observers map. // // Kind of singleton reference. qtractorActionControl *qtractorActionControl::g_pActionControl = nullptr; // ctor. qtractorActionControl::qtractorActionControl ( QObject *pParent ) : QObject(pParent) { // Pseudo-singleton reference setup. g_pActionControl = this; } // dtor. qtractorActionControl::~qtractorActionControl (void) { // Pseudo-singleton reference shut-down. g_pActionControl = nullptr; clear(); } // Kind of singleton reference. qtractorActionControl *qtractorActionControl::getInstance (void) { return g_pActionControl; } // MIDI observer map cleaner. void qtractorActionControl::clear (void) { qDeleteAll(m_midiObservers); m_midiObservers.clear(); }; // MIDI observer map methods. qtractorActionControl::MidiObserver *qtractorActionControl::getMidiObserver ( QAction *pAction ) { return m_midiObservers.value(pAction, nullptr); } qtractorActionControl::MidiObserver *qtractorActionControl::addMidiObserver ( QAction *pAction ) { MidiObserver *pMidiObserver = getMidiObserver(pAction); if (pMidiObserver == nullptr) { pMidiObserver = new MidiObserver(pAction); m_midiObservers.insert(pAction, pMidiObserver); } pMidiObserver->setValue(pAction->isChecked() ? pMidiObserver->maxValue() : pMidiObserver->minValue()); QObject::connect( pAction, SIGNAL(triggered(bool)), this, SLOT(triggeredSlot(bool))); return pMidiObserver; } void qtractorActionControl::removeMidiObserver ( QAction *pAction ) { MidiObserver *pMidiObserver = getMidiObserver(pAction); if (pMidiObserver) { QObject::disconnect( pAction, SIGNAL(triggered(bool)), this, SLOT(triggeredSlot(bool))); m_midiObservers.remove(pAction); delete pMidiObserver; } } // MIDI observer trigger slot. void qtractorActionControl::triggeredSlot ( bool bOn ) { QAction *pAction = qobject_cast (sender()); if (pAction) { MidiObserver *pMidiObserver = getMidiObserver(pAction); if (pMidiObserver) { #ifdef CONFIG_DEBUG qDebug("qtractorActionControl::triggeredSlot(%d)", int(bOn)); #endif const float v0 = pMidiObserver->value(); const float vmax = pMidiObserver->maxValue(); const float vmin = pMidiObserver->minValue(); const float vmid = 0.5f * (vmax + vmin); if (bOn && !pAction->isChecked()) pMidiObserver->setValue(v0 > vmid ? vmin : vmax); else pMidiObserver->setValue(v0 > vmid ? vmax : vmin); } } } // Complete action text, from associated menus. [static] QString qtractorActionControl::menuActionText ( QAction *pAction, const QString& sText ) { QString sActionText = sText; #if QT_VERSION >= QT_VERSION_CHECK(6, 3, 2) QListIterator iter(pAction->associatedObjects()); #else QListIterator iter(pAction->associatedWidgets()); #endif while (iter.hasNext()) { QMenu *pMenu = qobject_cast (iter.next()); if (pMenu) { sActionText = pMenu->title() + '/' + sActionText; pAction = pMenu->menuAction(); if (pAction) sActionText = menuActionText(pAction, sActionText); } } return sActionText; } // end of qtractorActionControl.cpp qtractor-1.5.11/src/PaxHeaders/qtractorClip.cpp0000644000000000000000000000013215124701674016465 xustar0030 mtime=1767080892.781263504 30 atime=1767080892.781263504 30 ctime=1767080892.781263504 qtractor-1.5.11/src/qtractorClip.cpp0000644000175000001440000010375415124701674016467 0ustar00rncbcusers// qtractorClip.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorClip.h" #include "qtractorSession.h" #include "qtractorDocument.h" #include "qtractorClipCommand.h" #include "qtractorMidiClip.h" #include "qtractorClipForm.h" #include #include #include #include #include #include //------------------------------------------------------------------------- // qtractorClip -- Track clip capsule. // Constructorz. qtractorClip::qtractorClip ( qtractorTrack *pTrack ) { m_pTrack = pTrack; m_fGain = 1.0f; m_fPanning = 0.0f; m_pTakeInfo = nullptr; m_pFadeInFunctor = nullptr; m_pFadeOutFunctor = nullptr; m_bActive = false; clear(); } // Default constructor. qtractorClip::~qtractorClip (void) { if (m_pTakeInfo) m_pTakeInfo->releaseRef(); if (m_pFadeInFunctor) delete m_pFadeInFunctor; if (m_pFadeOutFunctor) delete m_pFadeOutFunctor; } // Reset clip. void qtractorClip::clear (void) { m_sClipName.clear(); m_iClipStart = 0; m_iClipLength = 0; m_iClipOffset = 0; m_iClipStartTime = 0; m_iClipLengthTime = 0; m_iClipOffsetTime = 0; m_iSelectStart = 0; m_iSelectEnd = 0; m_bMute = false; m_iFadeInLength = 0; m_iFadeOutLength = 0; m_iFadeInTime = 0; m_iFadeOutTime = 0; setFadeInType(g_defaultFadeInType); setFadeOutType(g_defaultFadeOutType); m_bDirty = false; } // Clip filename properties accessors. void qtractorClip::setFilename ( const QString& sFilename ) { if (m_pTrack && m_pTrack->session()) m_sFilename = m_pTrack->session()->absoluteFilePath(sFilename); else m_sFilename = QDir::cleanPath(QDir().absoluteFilePath(sFilename)); } const QString& qtractorClip::filename (void) const { return m_sFilename; } QString qtractorClip::relativeFilename ( qtractorDocument *pDocument ) const { if (pDocument && (pDocument->isArchive() || pDocument->isSymLink())) return pDocument->addFile(m_sFilename); if (m_pTrack && m_pTrack->session()) return m_pTrack->session()->relativeFilePath(m_sFilename); else return QDir().relativeFilePath(m_sFilename); } QString qtractorClip::shortClipName ( const QString& sClipName ) const { QString sShortClipName(sClipName); if (m_pTrack && m_pTrack->session()) { const QString& sSessionName = m_pTrack->session()->sessionName(); if (!sSessionName.isEmpty()) { const QString sRegExp("^%1[\\-|_|\\s]+"); sShortClipName.remove(QRegularExpression(sRegExp.arg(sSessionName))); } } return sShortClipName; } QString qtractorClip::clipTitle (void) const { QString sClipTitle; if (m_bMute) sClipTitle += QObject::tr("[Mute] "); sClipTitle += m_sClipName; if (m_pTakeInfo && m_pTakeInfo->partClip(this) != TakeInfo::ClipHead) { const int iCurrentTake = m_pTakeInfo->currentTake(); const int iTakeCount = m_pTakeInfo->takeCount(); if (iCurrentTake >= 0 && iCurrentTake < iTakeCount) { sClipTitle += QObject::tr(" (take %1/%2)") .arg(iCurrentTake + 1).arg(iTakeCount); } } return sClipTitle; } // Clip start frame accessor. void qtractorClip::setClipStart ( unsigned long iClipStart ) { m_iClipStart = iClipStart; if (m_pTrack && m_pTrack->session()) m_iClipStartTime = m_pTrack->session()->tickFromFrame(iClipStart); } // Clip frame length accessor. void qtractorClip::setClipLength ( unsigned long iClipLength ) { m_iClipLength = iClipLength; if (m_pTrack && m_pTrack->session()) m_iClipLengthTime = m_pTrack->session()->tickFromFrameRange( m_iClipStart, m_iClipStart + m_iClipLength); } // Clip frame offset accessor. void qtractorClip::setClipOffset ( unsigned long iClipOffset ) { m_iClipOffset = iClipOffset; if (m_pTrack && m_pTrack->session()) m_iClipOffsetTime = m_pTrack->session()->tickFromFrameRange( m_iClipStart, m_iClipStart + m_iClipOffset, true); } // Clip selection accessors. void qtractorClip::setClipSelected ( bool bClipSelected ) { if (!bClipSelected) setClipSelect(0, 0); else if (!isClipSelected()) setClipSelect(m_iClipStart, m_iClipStart + m_iClipLength); } bool qtractorClip::isClipSelected (void) const { return (m_iSelectStart < m_iSelectEnd); } // Clip-selection points accessors. void qtractorClip::setClipSelect ( unsigned long iSelectStart, unsigned long iSelectEnd ) { if (iSelectStart < m_iClipStart) iSelectStart = m_iClipStart; if (iSelectEnd > m_iClipStart + m_iClipLength) iSelectEnd = m_iClipStart + m_iClipLength; if (iSelectStart < iSelectEnd) { m_iSelectStart = iSelectStart; m_iSelectEnd = iSelectEnd; } else { m_iSelectStart = 0; m_iSelectEnd = 0; } } // Clip fade-in accessors void qtractorClip::setFadeInType ( qtractorClip::FadeType fadeType ) { if (m_pFadeInFunctor) delete m_pFadeInFunctor; m_fadeInType = fadeType; m_pFadeInFunctor = createFadeFunctor(FadeIn, fadeType); } void qtractorClip::setFadeInLength ( unsigned long iFadeInLength ) { if (iFadeInLength > m_iClipLength) iFadeInLength = m_iClipLength; m_iFadeInLength = iFadeInLength; if (m_pTrack && m_pTrack->session()) m_iFadeInTime = m_pTrack->session()->tickFromFrameRange( m_iClipStart, m_iClipStart + m_iFadeInLength); } // Clip fade-out accessors void qtractorClip::setFadeOutType ( qtractorClip::FadeType fadeType ) { if (m_pFadeOutFunctor) delete m_pFadeOutFunctor; m_fadeOutType = fadeType; m_pFadeOutFunctor = createFadeFunctor(FadeOut, fadeType); } void qtractorClip::setFadeOutLength ( unsigned long iFadeOutLength ) { if (iFadeOutLength > m_iClipLength) iFadeOutLength = m_iClipLength; m_iFadeOutLength = iFadeOutLength; if (m_pTrack && m_pTrack->session()) m_iFadeOutTime = m_pTrack->session()->tickFromFrameRange( m_iClipStart + m_iClipLength - m_iFadeOutLength, m_iClipStart + m_iClipLength); } // Compute clip gain, given current fade-in/out slopes. float qtractorClip::fadeInOutGain ( unsigned long iOffset ) const { if (m_iFadeInLength > 0 && iOffset < m_iFadeInLength) { return (*m_pFadeInFunctor)( float(iOffset) / float(m_iFadeInLength)); } if (m_iFadeOutLength > 0 && iOffset > m_iClipLength - m_iFadeOutLength) { return (*m_pFadeOutFunctor)( float(iOffset - (m_iClipLength - m_iFadeOutLength)) / float(m_iFadeOutLength)); } return (iOffset < m_iClipLength ? 1.0f : 0.0f); } // Clip time reference settler method. void qtractorClip::updateClipTime (void) { if (m_pTrack == nullptr) return; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; m_iClipStart = pSession->frameFromTick(m_iClipStartTime); m_iClipLength = pSession->frameFromTickRange( m_iClipStartTime, m_iClipStartTime + m_iClipLengthTime); #if 0// FIXUP: Don't quantize to MIDI metronomic time-scale... m_iClipOffsetTime = pSession->tickFromFrameRange( m_iClipStart, m_iClipStart + m_iClipOffset, true); #else m_iClipOffset = pSession->frameFromTickRange( m_iClipStartTime, m_iClipStartTime + m_iClipOffsetTime, true); #endif m_iFadeInLength = pSession->frameFromTickRange( m_iClipStartTime, m_iClipStartTime + m_iFadeInTime); m_iFadeOutLength = pSession->frameFromTickRange( m_iClipStartTime + m_iClipLengthTime - m_iFadeOutTime, m_iClipStartTime + m_iClipLengthTime); } // Base clip drawing method. void qtractorClip::drawClip ( QPainter *pPainter, const QRect& clipRect, unsigned long iClipOffset ) { // Draw the framed rectangle and background... pPainter->drawRect(clipRect); // Draw clip contents (virtual) draw(pPainter, clipRect, iClipOffset); // Adjust the clip rectangle left origin... qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; QRect rect(clipRect); if (iClipOffset > 0) rect.setLeft(rect.left() - pSession->pixelFromFrame(iClipOffset) + 1); // Draw clip name label... const QColor& rgbFore = m_pTrack->foreground(); const QColor& rgbBack = m_pTrack->background(); QColor rgbText = (rgbBack.value() < 0xcc ? rgbFore.lighter(200) : rgbFore.darker(160)); if (qAbs(rgbFore.value() - rgbBack.value()) < 0x33) { rgbText.setHsv( rgbText.hue(), rgbText.saturation(), (255 - rgbText.value()), 200); } pPainter->setPen(rgbText); pPainter->drawText(rect, Qt::AlignLeft | Qt::AlignBottom | Qt::TextSingleLine, clipTitle()); // Avoid drawing fade in/out handles // on still empty clips (eg. while recording) if (m_iClipLength < 1) return; // Fade in/out handle color... QColor rgbFade(rgbText); rgbFade.setAlpha(80); pPainter->setPen(rgbFade); pPainter->setBrush(rgbFade); // Fade-in slope... const int y = rect.top(); const int h = rect.bottom(); int x = rect.left(); int w = pSession->pixelFromFrame(m_iFadeInLength); const QRect rectFadeIn(x + w, y, 8, 8); if (w > 0 && x + w > clipRect.left()) { #if 0 QPolygon polyg(3); polyg.setPoint(0, x, y); polyg.setPoint(1, x, h); polyg.setPoint(2, x + w, y); pPainter->drawPolygon(polyg); #else const int w2 = (w >> 1); const int w4 = (w >> 2); QPolygon polyg(5); polyg.setPoint(0, x, y); polyg.setPoint(1, x, h); switch (m_fadeInType) { case Linear: polyg.setPoint(2, x, h); polyg.setPoint(3, x, h); break; case InQuad: polyg.setPoint(2, x + w2, h); polyg.setPoint(3, x + w, y); break; case OutQuad: polyg.setPoint(2, x + w2, y); polyg.setPoint(3, x + w, y); break; case InOutQuad: polyg.setPoint(2, x + w2, h); polyg.setPoint(3, x + w2, y); break; case InCubic: polyg.setPoint(2, x + w - w4, h); polyg.setPoint(3, x + w, y); break; case OutCubic: polyg.setPoint(2, x + w2, y); polyg.setPoint(3, x + w - w4, y); break; case InOutCubic: polyg.setPoint(2, x + w - w4, h); polyg.setPoint(3, x + w4, y); break; } polyg.setPoint(4, x + w, y); QPainterPath path; path.moveTo(polyg.at(0)); path.lineTo(polyg.at(1)); path.cubicTo(polyg.at(2), polyg.at(3), polyg.at(4)); path.lineTo(polyg.at(0)); pPainter->drawPath(path); #endif } // Fade-out slope... x = rect.left() + pSession->pixelFromFrame(m_iClipLength); w = pSession->pixelFromFrame(m_iFadeOutLength); const QRect rectFadeOut(x - w - 8, y, 8, 8); if (w > 0 && x - w < clipRect.right()) { #if 0 QPolygon polyg(3); polyg.setPoint(0, x, y); polyg.setPoint(1, x, h); polyg.setPoint(2, x - w, y); pPainter->drawPolygon(polyg); #else const int w2 = (w >> 1); const int w4 = (w >> 2); QPolygon polyg(5); polyg.setPoint(0, x, y); polyg.setPoint(1, x, h); switch (m_fadeOutType) { case Linear: polyg.setPoint(2, x, h); polyg.setPoint(3, x, h); break; case InQuad: polyg.setPoint(2, x - w2, y); polyg.setPoint(3, x - w, y); break; case OutQuad: polyg.setPoint(2, x - w2, h); polyg.setPoint(3, x - w, y); break; case InOutQuad: polyg.setPoint(2, x - w2, h); polyg.setPoint(3, x - w2, y); break; case InCubic: polyg.setPoint(2, x - w2, y); polyg.setPoint(3, x - w + w4, y); break; case OutCubic: polyg.setPoint(2, x - w + w4, h); polyg.setPoint(3, x - w, y); break; case InOutCubic: polyg.setPoint(2, x - w + w4, h); polyg.setPoint(3, x - w4, y); break; } polyg.setPoint(4, x - w, y); QPainterPath path; path.moveTo(polyg.at(0)); path.lineTo(polyg.at(1)); path.cubicTo(polyg.at(2), polyg.at(3), polyg.at(4)); path.lineTo(polyg.at(0)); pPainter->drawPath(path); #endif } // Fade in/out handles... if (rectFadeIn.intersects(clipRect)) pPainter->fillRect(rectFadeIn, rgbFade.darker(120)); if (rectFadeOut.intersects(clipRect)) pPainter->fillRect(rectFadeOut, rgbFade.darker(120)); } // Recording clip drawing method. void qtractorClip::drawClipRecord ( QPainter *pPainter, const QRect& clipRect, unsigned long iClipOffset ) { // Draw the framed rectangle and background... pPainter->drawRect(clipRect); // Update clip rolling stats, if any... update(); // Draw clip contents (virtual)... draw(pPainter, clipRect, iClipOffset); // Draw red shade overlay... pPainter->fillRect(clipRect, QColor(255, 0, 0, 120)); } // Clip editor method. bool qtractorClip::startEditor ( QWidget *pParent ) { // Make sure the regular clip-form is started modal... qtractorClipForm clipForm(pParent); clipForm.setClip(this); return clipForm.exec(); } // Clip editor update. void qtractorClip::updateEditor ( bool /*bSelectClear*/ ) { // Do nothing here. } // Clip editor contents update. void qtractorClip::updateEditorContents (void) { // Do nothing here. } // Clip query-close method (return true if editing is done). bool qtractorClip::queryEditor (void) { return !isDirty(); } // Clip tool-tip. QString qtractorClip::toolTip (void) const { QString sToolTip = QObject::tr("Name:\t%1").arg(clipTitle()); qtractorSession *pSession = nullptr; if (m_pTrack) pSession = m_pTrack->session(); if (pSession) { sToolTip += '\n'; qtractorTimeScale *pTimeScale = pSession->timeScale(); sToolTip += QObject::tr("Start:\t%1\tOffset:\t%2\nEnd:\t%3\tLength:\t%4") .arg(pTimeScale->textFromFrame(m_iClipStart)) .arg(pTimeScale->textFromFrame(m_iClipStart, true, m_iClipOffset)) .arg(pTimeScale->textFromFrame(m_iClipStart + m_iClipLength)) .arg(pTimeScale->textFromFrame(m_iClipStart, true, m_iClipLength)); } sToolTip += '\n'; sToolTip += QObject::tr("File:\t%1").arg(QFileInfo(m_sFilename).fileName()); return sToolTip; } // Document element methods. bool qtractorClip::loadElement ( qtractorDocument *pDocument, QDomElement *pElement ) { qtractorClip::setClipName(pElement->attribute("name")); // Load clip children... for (QDomNode nChild = pElement->firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert node to element... QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; // Load clip properties... if (eChild.tagName() == "properties") { for (QDomNode nProp = eChild.firstChild(); !nProp.isNull(); nProp = nProp.nextSibling()) { // Convert property node to element... QDomElement eProp = nProp.toElement(); if (eProp.isNull()) continue; if (eProp.tagName() == "name") qtractorClip::setClipName(eProp.text()); else if (eProp.tagName() == "start") qtractorClip::setClipStart(eProp.text().toULong()); else if (eProp.tagName() == "offset") qtractorClip::setClipOffset(eProp.text().toULong()); else if (eProp.tagName() == "length") qtractorClip::setClipLength(eProp.text().toULong()); else if (eProp.tagName() == "gain") qtractorClip::setClipGain(eProp.text().toFloat()); else if (eProp.tagName() == "panning") qtractorClip::setClipPanning(eProp.text().toFloat()); else if (eProp.tagName() == "mute") qtractorClip::setClipMute(qtractorDocument::boolFromText(eProp.text())); else if (eProp.tagName() == "fade-in") { qtractorClip::setFadeInType( qtractorClip::fadeInTypeFromText(eProp.attribute("type"))); qtractorClip::setFadeInLength(eProp.text().toULong()); } else if (eProp.tagName() == "fade-out") { qtractorClip::setFadeOutType( qtractorClip::fadeOutTypeFromText(eProp.attribute("type"))); qtractorClip::setFadeOutLength(eProp.text().toULong()); } } } else // Load clip derivative properties... if (eChild.tagName() == "audio-clip" || eChild.tagName() == "midi-clip") { if (!loadClipElement(pDocument, &eChild)) return false; } else if (eChild.tagName() == "take-info") { int iTakeID = eChild.attribute("id").toInt(); qtractorClip::TakeInfo::ClipPart cpart = qtractorClip::TakeInfo::ClipPart( eChild.attribute("part").toInt()); // Load take(record) descriptor children, if any... unsigned long iClipStart = 0; unsigned long iClipOffset = 0; unsigned long iClipLength = 0; unsigned long iTakeStart = 0; unsigned long iTakeEnd = 0; unsigned long iTakeGap = 0; int iCurrentTake = -1; for (QDomNode nProp = eChild.firstChild(); !nProp.isNull(); nProp = nProp.nextSibling()) { // Convert node to element... QDomElement eProp = nProp.toElement(); if (eProp.isNull()) continue; // Load take-info properties... if (eProp.tagName() == "clip-start") iClipStart = eProp.text().toULong(); else if (eProp.tagName() == "clip-offset") iClipOffset = eProp.text().toULong(); else if (eProp.tagName() == "clip-length") iClipLength = eProp.text().toULong(); else if (eProp.tagName() == "take-start") iTakeStart = eProp.text().toULong(); else if (eProp.tagName() == "take-end") iTakeEnd = eProp.text().toULong(); else if (eProp.tagName() == "take-gap") iTakeGap = eProp.text().toULong(); else if (eProp.tagName() == "current-take") iCurrentTake = eProp.text().toInt(); } qtractorTrack::TakeInfo *pTakeInfo = nullptr; qtractorTrack *pTrack = qtractorClip::track(); if (pTrack && iTakeID >= 0) pTakeInfo = pTrack->takeInfo(iTakeID); if (pTakeInfo == nullptr && iTakeStart < iTakeEnd) { pTakeInfo = static_cast ( new qtractorClip::TakeInfo( iClipStart, iClipOffset, iClipLength, iTakeStart, iTakeEnd, iTakeGap)); if (pTrack && iTakeID >= 0) pTrack->takeInfoAdd(iTakeID, pTakeInfo); } if (pTakeInfo) { qtractorClip::setTakeInfo(pTakeInfo); pTakeInfo->setClipPart(cpart, this); if (iCurrentTake >= 0) pTakeInfo->setCurrentTake(iCurrentTake); } } } return true; } bool qtractorClip::saveElement ( qtractorDocument *pDocument, QDomElement *pElement ) { pElement->setAttribute("name", qtractorClip::clipName()); // Save clip properties... QDomElement eProps = pDocument->document()->createElement("properties"); pDocument->saveTextElement("name", qtractorClip::clipName(), &eProps); pDocument->saveTextElement("start", QString::number(qtractorClip::clipStart()), &eProps); pDocument->saveTextElement("offset", QString::number(qtractorClip::clipOffset()), &eProps); pDocument->saveTextElement("length", QString::number(qtractorClip::clipLength()), &eProps); pDocument->saveTextElement("gain", QString::number(qtractorClip::clipGain()), &eProps); pDocument->saveTextElement("panning", QString::number(qtractorClip::clipPanning()), &eProps); pDocument->saveTextElement("mute", qtractorDocument::textFromBool(qtractorClip::isClipMute()), &eProps); QDomElement eFadeIn = pDocument->document()->createElement("fade-in"); eFadeIn.setAttribute("type", qtractorClip::textFromFadeType(qtractorClip::fadeInType())); eFadeIn.appendChild(pDocument->document()->createTextNode( QString::number(qtractorClip::fadeInLength()))); eProps.appendChild(eFadeIn); QDomElement eFadeOut = pDocument->document()->createElement("fade-out"); eFadeOut.setAttribute("type", qtractorClip::textFromFadeType(qtractorClip::fadeOutType())); eFadeOut.appendChild(pDocument->document()->createTextNode( QString::number(qtractorClip::fadeOutLength()))); eProps.appendChild(eFadeOut); pElement->appendChild(eProps); // Save clip derivative properties... if (!saveClipElement(pDocument, pElement)) return false; // At last, save clip take(record) properties, if any... qtractorTrack::TakeInfo *pTakeInfo = static_cast (qtractorClip::takeInfo()); if (pTakeInfo) { qtractorTrack *pTrack = qtractorClip::track(); if (pTrack) { QDomElement eTakeInfo = pDocument->document()->createElement("take-info"); int iTakeID = pTrack->takeInfoId(pTakeInfo); if (iTakeID < 0) { iTakeID = pTrack->takeInfoNew(pTakeInfo); pDocument->saveTextElement("clip-start", QString::number(pTakeInfo->clipStart()), &eTakeInfo); pDocument->saveTextElement("clip-offset", QString::number(pTakeInfo->clipOffset()), &eTakeInfo); pDocument->saveTextElement("clip-length", QString::number(pTakeInfo->clipLength()), &eTakeInfo); pDocument->saveTextElement("take-start", QString::number(pTakeInfo->takeStart()), &eTakeInfo); pDocument->saveTextElement("take-end", QString::number(pTakeInfo->takeEnd()), &eTakeInfo); pDocument->saveTextElement("take-gap", QString::number(pTakeInfo->takeGap()), &eTakeInfo); pDocument->saveTextElement("current-take", QString::number(pTakeInfo->currentTake()), &eTakeInfo); } eTakeInfo.setAttribute("id", QString::number(iTakeID)); eTakeInfo.setAttribute("part", QString::number(int(pTakeInfo->partClip(this)))); pElement->appendChild(eTakeInfo); } } // Successs. return true; } // Clip fade type textual helper methods. qtractorClip::FadeType qtractorClip::fadeInTypeFromText ( const QString& sText ) { FadeType fadeType = InQuad; if (sText == "Linear" || sText == "linear") fadeType = Linear; else if (sText == "InCubic" || sText == "cubic") fadeType = InCubic; else if (sText == "OutQuad") fadeType = OutQuad; else if (sText == "InOutQuad") fadeType = InOutQuad; else if (sText == "OutCubic") fadeType = OutCubic; else if (sText == "InOutCubic") fadeType = InOutCubic; return fadeType; } qtractorClip::FadeType qtractorClip::fadeOutTypeFromText ( const QString& sText ) { FadeType fadeType = OutQuad; if (sText == "Linear" || sText == "linear") fadeType = Linear; else if (sText == "OutCubic" || sText == "cubic") fadeType = OutCubic; else if (sText == "InQuad") fadeType = InQuad; else if (sText == "InOutQuad") fadeType = InOutQuad; else if (sText == "InCubic") fadeType = InCubic; else if (sText == "InOutCubic") fadeType = InOutCubic; return fadeType; } QString qtractorClip::textFromFadeType ( FadeType fadeType ) { QString sText; switch (fadeType) { case Linear: sText = "Linear"; break; case InQuad: sText = "InQuad"; break; case OutQuad: sText = "OutQuad"; break; case InOutQuad: sText = "InOutQuad"; break; case InCubic: sText = "InCubic"; break; case OutCubic: sText = "OutCubic"; break; case InOutCubic: sText = "InOutCubic"; break; } return sText; } // Estimate total number of takes. int qtractorClip::TakeInfo::takeCount (void) const { int iTakeCount = -1; const unsigned long iClipEnd = m_iClipStart + m_iClipLength; const unsigned long iTakeLength = m_iTakeEnd - m_iTakeStart + m_iTakeGap; if (iTakeLength > 0 && m_iClipStart < m_iTakeEnd && m_iTakeEnd < iClipEnd) iTakeCount = (iClipEnd - m_iTakeStart) / iTakeLength; return iTakeCount + 1; } // Select current take set. int qtractorClip::TakeInfo::select ( qtractorClipCommand *pClipCommand, qtractorTrack *pTrack, int iTake ) { unsigned long iClipStart = m_iClipStart; unsigned long iClipOffset = m_iClipOffset; unsigned long iClipLength = m_iClipLength; const unsigned long iClipEnd = iClipStart + iClipLength; const unsigned long iTakeStart = m_iTakeStart; const unsigned long iTakeEnd = m_iTakeEnd; const unsigned long iTakeGap = m_iTakeGap; const unsigned long iTakeLength = iTakeEnd - iTakeStart + iTakeGap; #ifdef CONFIG_DEBUG qDebug("qtractorClip::TakeInfo[%p]::select(%d, %lu, %lu, %lu, %lu, %lu)", this, iTake, iClipStart, iClipOffset, iClipLength, iTakeStart, iTakeEnd); #endif if (iTakeStart < iClipEnd) { int iTakeCount = 0; if (iClipEnd > iTakeEnd) iTakeCount += (iClipEnd - iTakeStart) / iTakeLength + 1; if (iTake < 0 || iTake >= iTakeCount) iTake = iTakeCount - 1; // Clip-head for sure... if (iClipStart < iTakeStart) { iClipLength = iTakeStart - iClipStart; selectClipPart(pClipCommand, pTrack, ClipHead, iClipStart, iClipOffset, iClipLength); iClipOffset += iClipLength; iClipStart = iTakeStart; } // Clip-take from now on... iClipLength = (iClipEnd > iTakeEnd ? iTakeEnd : iClipEnd) - iClipStart; if (iTake > 0) { iClipOffset += (iTakeEnd - iClipStart) + iTakeGap; iClipOffset += (iTake - 1) * iTakeLength; if (iTake < iTakeCount - 1) iClipLength = iTakeEnd - iTakeStart; else iClipLength = (iClipEnd - iTakeStart) % iTakeLength; iClipStart = iTakeStart; } } else iTake = -1; selectClipPart(pClipCommand, pTrack, ClipTake, iClipStart, iClipOffset, iClipLength); // m_iCurrentTake = iTake; return iTake; } // Select current take sub-clip part. void qtractorClip::TakeInfo::selectClipPart ( qtractorClipCommand *pClipCommand, qtractorTrack *pTrack, ClipPart cpart, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength ) { #ifdef CONFIG_DEBUG qDebug("qtractorClip::TakeInfo[%p]::selectClipPart(%d, %lu, %lu, %lu)", this, int(cpart), iClipStart, iClipOffset, iClipLength); #endif // Priority to recording clip... qtractorClip *pClip = (pTrack ? pTrack->clipRecord() : nullptr); // Clip already taken?... if (pClip == nullptr) { pClip = clipPart(cpart); if (pClip) { // Don't change what hasn't change... if (iClipStart != pClip->clipStart() || iClipOffset != pClip->clipOffset() || iClipLength != pClip->clipLength()) { pClipCommand->resizeClip(pClip, iClipStart, iClipOffset, iClipLength); } return; // Done. } // Take clip-take clone... if (cpart == ClipHead) pClip = clipPart(ClipTake); } // Clip about to be taken (as new)?... if (pClip) { TakePart part(this, cpart); pClipCommand->addClipRecordTake(pTrack, pClip, iClipStart, iClipOffset, iClipLength, &part); } } // Reset(unfold) whole take set. void qtractorClip::TakeInfo::reset ( qtractorClipCommand *pClipCommand, bool bClear ) { const unsigned long iClipStart = m_iClipStart; const unsigned long iClipOffset = m_iClipOffset; const unsigned long iClipLength = m_iClipLength; #ifdef CONFIG_DEBUG qDebug("qtractorClip::TakeInfo[%p]::reset(%lu, %lu, %lu, %d)", this, iClipStart, iClipOffset, iClipLength, int(bClear)); #endif qtractorClip *pClip = clipPart(ClipHead); if (pClip) { pClipCommand->removeClip(pClip); pClipCommand->takeInfoClip(pClip, nullptr); setClipPart(ClipHead, nullptr); } pClip = clipPart(ClipTake); if (pClip) { pClipCommand->resizeClip(pClip, iClipStart, iClipOffset, iClipLength); if (bClear) { pClipCommand->takeInfoClip(pClip, nullptr); // setClipPart(ClipTake, nullptr); } } // m_iCurrentTake = -1; } // Take(record) descriptor accessors. void qtractorClip::setTakeInfo ( qtractorClip::TakeInfo *pTakeInfo ) { #ifdef CONFIG_DEBUG qDebug("qtractorClip[%p]::setTakeInfo(%p)", this, pTakeInfo); #endif if (m_pTakeInfo) m_pTakeInfo->releaseRef(); m_pTakeInfo = pTakeInfo; if (m_pTakeInfo) m_pTakeInfo->addRef(); } qtractorClip::TakeInfo *qtractorClip::takeInfo (void) const { return m_pTakeInfo; } //--------------------------------------------------------------------------- /* TERMS OF USE - EASING EQUATIONS Open source under the BSD License. Copyright (C) 2001 Robert Penner All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //--------------------------------------------------------------------------- // Functor argument normalization. // // t = (x - x0) / (x1 - x0) // a = y1 - y0 // b = y0 // // Linear fade. // struct FadeLinear { float operator() (float t, float a, float b) const { return a * t + b; } }; // Quadratic (t^2) fade in: accelerating from zero velocity. // struct FadeInQuad { float operator() (float t, float a, float b) const { return a * (t * t) + b; } }; // Quadratic (t^2) fade out: decelerating to zero velocity. // struct FadeOutQuad { float operator() (float t, float a, float b) const { return a * (t * (2.0f - t)) + b; } }; // Quadratic (t^2) fade in-out: acceleration until halfway, then deceleration. // struct FadeInOutQuad { float operator() (float t, float a, float b) const { t *= 2.0f; if (t < 1.0f) { return 0.5f * a * (t * t) + b; } else { t -= 1.0f; return 0.5f * a * (1.0f - (t * (t - 2.0f))) + b; } } }; // Cubic (t^3) fade in: accelerating from zero velocity. // struct FadeInCubic { float operator() (float t, float a, float b) const { return a * (t * t * t) + b; } }; // Cubic (t^3) fade out: decelerating from zero velocity. // struct FadeOutCubic { float operator() (float t, float a, float b) const { t -= 1.0f; return a * ((t * t * t) + 1.0f) + b; } }; // Cubic (t^3) fade in-out: acceleration until halfway, then deceleration. // struct FadeInOutCubic { float operator() (float t, float a, float b) const { t *= 2.0f; if (t < 1.0f) { return 0.5f * a * (t * t * t) + b; } else { t -= 2.0f; return 0.5f * a * ((t * t * t) + 2.0f) + b; } } }; // Fade model class. // struct FadeMode { FadeMode(float y0, float y1) : a(y1 - y0), b(y0) {} float a, b; }; // Fade-in mode. // struct FadeInMode : public FadeMode { FadeInMode() : FadeMode(0.0f, 1.0f) {} }; // Fade-out mode. // struct FadeOutMode : public FadeMode { FadeOutMode() : FadeMode(1.0f, 0.0f) {} }; // Fade functor template class. // template class FadeCurve : public qtractorClip::FadeFunctor { public: FadeCurve() : m_mode(M()), m_func(F()) {} float operator() (float t) const { return m_func(t, m_mode.a, m_mode.b); } private: M m_mode; F m_func; }; // Fade functor factory class (static). // qtractorClip::FadeFunctor *qtractorClip::createFadeFunctor ( FadeMode fadeMode, FadeType fadeType ) { switch (fadeMode) { case FadeIn: switch (fadeType) { case Linear: return new FadeCurve (); case InQuad: return new FadeCurve (); case OutQuad: return new FadeCurve (); case InOutQuad: return new FadeCurve (); case InCubic: return new FadeCurve (); case OutCubic: return new FadeCurve (); case InOutCubic: return new FadeCurve (); default: break; } break; case FadeOut: switch (fadeType) { case Linear: return new FadeCurve (); case InQuad: return new FadeCurve (); case OutQuad: return new FadeCurve (); case InOutQuad: return new FadeCurve (); case InCubic: return new FadeCurve (); case OutCubic: return new FadeCurve (); case InOutCubic: return new FadeCurve (); default: break; } break; default: break; } return nullptr; } //---------------------------------------------------------------------------- // Clip fade-in/out types. qtractorClip::FadeType qtractorClip::g_defaultFadeInType = qtractorClip::InQuad; qtractorClip::FadeType qtractorClip::g_defaultFadeOutType = qtractorClip::OutQuad; void qtractorClip::setDefaultFadeInType(FadeType fadeType) { g_defaultFadeInType = fadeType; } qtractorClip::FadeType qtractorClip::defaultFadeInType (void) { return g_defaultFadeInType; } void qtractorClip::setDefaultFadeOutType ( FadeType fadeType ) { g_defaultFadeOutType = fadeType; } qtractorClip::FadeType qtractorClip::defaultFadeOutType (void) { return g_defaultFadeOutType; } qtractorClip::FadeTypes& qtractorClip::fadeTypes (void) { static const char *s_aFadeTypeNames[] = { QT_TR_NOOP("Linear"), // Linear (obvious:) QT_TR_NOOP("Quadratic 1"), // InQuad QT_TR_NOOP("Quadratic 2"), // OutQuad QT_TR_NOOP("Quadratic 3"), // InOutQuad QT_TR_NOOP("Cubic 1"), // InCubic QT_TR_NOOP("Cubic 2"), // OutCubic QT_TR_NOOP("Cubic 3"), // InOutCubic nullptr }; static FadeTypes s_fadeTypes; if (s_fadeTypes.isEmpty()) { const QPixmap& pmFadeIn = QIcon::fromTheme("fadeIn").pixmap(7 * 16, 16); const QPixmap& pmFadeOut = QIcon::fromTheme("fadeOut").pixmap(7 * 16, 16); for (int i = 0; s_aFadeTypeNames[i]; ++i) { FadeTypeInfo& info = s_fadeTypes[i]; info.name = QObject::tr(s_aFadeTypeNames[i]); info.iconFadeIn = pmFadeIn.copy(i << 4, 0, 16, 16); info.iconFadeOut = pmFadeOut.copy(i << 4, 0, 16, 16); } } return s_fadeTypes; } // Fade type index converters. qtractorClip::FadeType qtractorClip::fadeTypeFromIndex ( int iIndex ) { return qtractorClip::FadeType(iIndex); } int qtractorClip::indexFromFadeType ( FadeType fadeType ) { return int(fadeType); } // end of qtractorClip.cpp qtractor-1.5.11/src/PaxHeaders/qtractorAudioMadFile.h0000644000000000000000000000013215124701674017526 xustar0030 mtime=1767080892.779263512 30 atime=1767080892.779263512 30 ctime=1767080892.779263512 qtractor-1.5.11/src/qtractorAudioMadFile.h0000644000175000001440000000717615124701674017531 0ustar00rncbcusers// qtractorAudioMadFile.h // /**************************************************************************** Copyright (C) 2005-2019, 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 __qtractorAudioMadFile_h #define __qtractorAudioMadFile_h #include "qtractorAudioFile.h" #include #include #include #ifdef CONFIG_LIBMAD // libmad API #include #endif //---------------------------------------------------------------------- // class qtractorAudioMadFile -- Buffered audio file declaration. // class qtractorAudioMadFile : public qtractorAudioFile { public: // Constructor. qtractorAudioMadFile(unsigned int iBufferSize = 0); // Destructor. virtual ~qtractorAudioMadFile(); // Virtual method mockups. bool open (const QString& sFilename, int iMode = Read); int read (float **ppFrames, unsigned int iFrames); int write (float **ppFrames, unsigned int iFrames); bool seek (unsigned long iOffset); void close (); // Virtual accessor mockups. int mode() const; unsigned short channels() const; unsigned long frames() const; // Specialty methods. unsigned int sampleRate() const; protected: // Special decode method. bool input(); bool decode(); // Internal ring-buffer helper methods. unsigned int readable() const; unsigned int writable() const; private: // Instance variables. int m_iMode; FILE *m_pFile; unsigned int m_iBitRate; unsigned short m_iChannels; unsigned int m_iSampleRate; unsigned long m_iFramesEst; bool m_bEndOfStream; #ifdef CONFIG_LIBMAD struct mad_stream m_madStream; struct mad_frame m_madFrame; struct mad_synth m_madSynth; #endif // Input buffer stuff. unsigned int m_iInputBufferSize; unsigned char *m_pInputBuffer; // Output ring-buffer stuff. unsigned int m_iRingBufferSize; unsigned int m_iRingBufferMask; unsigned int m_iRingBufferRead; unsigned int m_iRingBufferWrite; float **m_ppRingBuffer; // Decoding frame mapping for sample-accurate seeking. unsigned long m_iSeekOffset; // Decoded frame node. struct FrameNode { // Member constructor. FrameNode(unsigned long i = 0, unsigned long o = 0, unsigned int c = 0) : iInputOffset(i), iOutputOffset(o), iDecodeCount(c) {} // Member fields. unsigned long iInputOffset; // Bytes from input file. unsigned long iOutputOffset; // Sample frames on output. unsigned int iDecodeCount; // Decoder iteration count. }; // Decoded frame list type. typedef QList FrameList; // Frame list factory method. static FrameList *createFrameList(const QString& sFilename); // Frame list instance; FrameList *m_pFrameList; // Current decoded frame node. FrameNode m_curr; // Frame list mutex. static QMutex g_mutex; }; #endif // __qtractorAudioMadFile_h // end of qtractorAudioMadFile.h qtractor-1.5.11/src/PaxHeaders/qtractorConnect.h0000644000000000000000000000012415124701674016635 xustar0028 mtime=1767080892.7822635 28 atime=1767080892.7822635 28 ctime=1767080892.7822635 qtractor-1.5.11/src/qtractorConnect.h0000644000175000001440000002577715124701674016646 0ustar00rncbcusers// qtractorConnect.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorConnect_h #define __qtractorConnect_h #include #include #include // Forward declarations. class qtractorPortListItem; class qtractorClientListItem; class qtractorClientListView; class qtractorConnectorView; class qtractorConnect; class QPainter; class QTimer; class QPaintEvent; class QResizeEvent; class QMouseEvent; class QDragMoveEvent; class QDragEnterEvent; class QDragLeaveEvent; class QDropEvent; class QContextMenuEvent; //---------------------------------------------------------------------- // qtractorPortListItem -- Port list item. // class qtractorPortListItem : public QTreeWidgetItem { public: // Constructor. qtractorPortListItem(qtractorClientListItem *pClientItem); // Default destructor. virtual ~qtractorPortListItem(); // Instance accessors. void setPortName(const QString& sPortName); const QString& clientName() const; const QString& portName() const; // Proto-pretty/display name accessors. virtual void updatePortName(); // Complete client:port name helper. QString clientPortName(); // Connections client item accessor. qtractorClientListItem *clientItem() const; // Client port cleanup marker. void markPort(int iMark); void markClientPort(int iMark); int portMark() const; // Connected port list primitives. void addConnect(qtractorPortListItem *pPortItem); void removeConnect(qtractorPortListItem *pPortItem); void cleanConnects(); // Connected port finders. qtractorPortListItem *findConnect(qtractorPortListItem *pPortItem); // Connection list accessor. const QList& connects() const; // To virtually distinguish between list view items. int rtti() const; // Connectiopn highlight methods. void setHilite (bool bHilite); bool isHilite() const; // Proxy sort override method. // - Natural decimal sorting comparator. bool operator< (const QTreeWidgetItem& other) const; protected: // Port name display name accessors. void setPortText(const QString& sPortText); QString portText() const; private: // Instance variables. qtractorClientListItem *m_pClientItem; QString m_sPortName; int m_iPortMark; bool m_bHilite; // Connection cache list. QList m_connects; }; //---------------------------------------------------------------------------- // qtractorClientListItem -- Client list item. // class qtractorClientListItem : public QTreeWidgetItem { public: // Constructor. qtractorClientListItem(qtractorClientListView *pClientListView); // Default destructor. virtual ~qtractorClientListItem(); // Port finder. qtractorPortListItem *findPortItem(const QString& sPortName); // Instance accessors. void setClientName(const QString& sClientName); const QString& clientName() const; // Proto-pretty/display name accessors. virtual void updateClientName(); // Readable flag accessor. bool isReadable() const; // Client port cleanup marker. void markClient(int iMark); void markClientPorts(int iMark); void cleanClientPorts(int iMark); int clientMark() const; // Connection highlight methods. void setHilite (bool bHilite); bool isHilite() const; // Client item openness status. void setOpen(bool bOpen); bool isOpen() const; // Proxy sort override method. // - Natural decimal sorting comparator. bool operator< (const QTreeWidgetItem& other) const; protected: // Client name display name accessors. void setClientText(const QString& sClientText); QString clientText() const; private: // Instance variables. QString m_sClientName; int m_iClientMark; int m_iHilite; }; //---------------------------------------------------------------------------- // qtractorClientListView -- Client list view, supporting drag-n-drop. // class qtractorClientListView : public QTreeWidget { Q_OBJECT public: // Constructor. qtractorClientListView(QWidget *pParent = nullptr); // Default destructor. virtual ~qtractorClientListView(); // Client finder. qtractorClientListItem *findClientItem(const QString& sClientName); // Client:port finder. qtractorPortListItem *findClientPortItem(const QString& sClientPort); // Main controller accessors. void setBinding(qtractorConnect *pConnect); qtractorConnect *binding() const; // Readable flag accessors. void setReadable(bool bReadable); bool isReadable() const; // Client name filter helpers. void setClientName(const QString& sClientName); QString clientName() const; // Port name filter helpers. void setPortName(const QString& sPortName); QString portName() const; // Maintained current client name list. const QStringList& clientNames() const; // Override clear method. void clear(); // Whether items are all open (expanded) or closed (collapsed). void setOpenAll(bool bOpen); // Client ports cleanup marker. void markClientPorts(int iMark); void cleanClientPorts(int iMark); // Client:port refreshner (return newest item count). virtual int updateClientPorts() = 0; // Client:port hilite update stabilization. void hiliteClientPorts(); // Redirect this one as public. QTreeWidgetItem *itemFromIndex(const QModelIndex& index) const { return QTreeWidget::itemFromIndex(index); } // Auto-open timer methods. void setAutoOpenTimeout(int iAutoOpenTimeout); int autoOpenTimeout() const; // Do proper contents refresh/update. void refresh(); // Natural decimal sorting comparator. static bool lessThan( const QTreeWidgetItem& item1, const QTreeWidgetItem& item2, int col = 0); protected slots: // Auto-open timeout slot. void timeoutSlot(); protected: // Client:port filter function via regular expression. bool isClientName(const QString& sClientName); bool isPortName(const QString& sPortName); // Trap for help/tool-tip events. bool eventFilter(QObject *pObject, QEvent *pEvent); // Drag-n-drop stuff. QTreeWidgetItem *dragDropItem(const QPoint& pos); // Drag-n-drop stuff -- reimplemented virtual methods. void dragEnterEvent(QDragEnterEvent *pDragEnterEvent); void dragMoveEvent(QDragMoveEvent *pDragMoveEvent); void dragLeaveEvent(QDragLeaveEvent *); void dropEvent(QDropEvent *pDropEvent); // Handle mouse events for drag-and-drop stuff. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseMoveEvent(QMouseEvent *pMouseEvent); // Context menu request event handler. void contextMenuEvent(QContextMenuEvent *); private: // Local instance variables. qtractorConnect *m_pConnect; bool m_bReadable; // Auto-open timer. int m_iAutoOpenTimeout; QTimer *m_pAutoOpenTimer; // Item we'll eventually drag-and-dropping something. QTreeWidgetItem *m_pDragItem; QTreeWidgetItem *m_pDropItem; // The point from where drag started. QPoint m_posDrag; // The current highlighted item. QTreeWidgetItem *m_pHiliteItem; // Client:port regular expression filters. QRegularExpression m_rxClientName; QRegularExpression m_rxPortName; // Maintained list of client names. QStringList m_clientNames; }; //---------------------------------------------------------------------- // qtractorConnectorView -- Connector view widget. // class qtractorConnectorView : public QWidget { Q_OBJECT public: // Constructor. qtractorConnectorView(QWidget *pParent = nullptr); // Default destructor. ~qtractorConnectorView(); // Main controller accessors. void setBinding(qtractorConnect *pConnect); qtractorConnect *binding() const; protected slots: // Useful slots (should this be protected?). void contentsChanged(); protected: // Specific event handlers. void paintEvent(QPaintEvent *); // Context menu request event handler. void contextMenuEvent(QContextMenuEvent *); private: // Legal client/port item position helper. int itemY(QTreeWidgetItem *pListItem) const; // Drawing methods. void drawConnectionLine(QPainter *pPainter, int x1, int y1, int x2, int y2, int h1, int h2, const QPen& pen); // Local instance variables. qtractorConnect *m_pConnect; // Connector line color map/persistence. QHash m_colorMap; }; //---------------------------------------------------------------------------- // qtractorConnect -- Connections controller. // class qtractorConnect : public QObject { Q_OBJECT public: // Constructor. qtractorConnect( qtractorClientListView *pOListView, qtractorClientListView *pIListView, qtractorConnectorView *pConnectorView); // Default destructor. virtual ~qtractorConnect(); // QTreeWidgetItem types. enum { ClientItem = 1001, PortItem = 1002 }; // Widget accesors. qtractorClientListView *OListView() const { return m_pOListView; } qtractorClientListView *IListView() const { return m_pIListView; } qtractorConnectorView *ConnectorView() const { return m_pConnectorView; } // Explicit connection tests. bool canConnectSelected(); bool canDisconnectSelected(); bool canDisconnectAll(); public slots: // Explicit connection slots. bool connectSelected(); bool disconnectSelected(); bool disconnectAll(); // Complete/incremental contents rebuilder; // check dirty status if incremental. void updateContents(bool bClear); // Incremental contents refreshner; check dirty status. void refresh(); // Context menu helper. void contextMenu(const QPoint& gpos); signals: // Contents change signal. void contentsChanged(); // Connection change signal. void connectChanged(); protected: // Connect/Disconnection primitives. virtual bool connectPorts(qtractorPortListItem *pOPort, qtractorPortListItem *pIPort) = 0; virtual bool disconnectPorts(qtractorPortListItem *pOPort, qtractorPortListItem *pIPort) = 0; // Update port connection references. virtual void updateConnections() = 0; private: // Connect/Disconnection local primitives. bool connectPortsEx(qtractorPortListItem *pOPort, qtractorPortListItem *pIPort); bool disconnectPortsEx(qtractorPortListItem *pOPort, qtractorPortListItem *pIPort); // Connection methods (unguarded). bool connectSelectedEx(); bool disconnectSelectedEx(); bool disconnectAllEx(); // Controlled widgets. qtractorClientListView *m_pOListView; qtractorClientListView *m_pIListView; qtractorConnectorView *m_pConnectorView; }; #endif // __qtractorConnect_h // end of qtractorConnect.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiEditSelect.h0000644000000000000000000000013215124701674020073 xustar0030 mtime=1767080892.791263462 30 atime=1767080892.791263462 30 ctime=1767080892.791263462 qtractor-1.5.11/src/qtractorMidiEditSelect.h0000644000175000001440000000600315124701674020062 0ustar00rncbcusers// qtractorMidiEditSelect.h // /**************************************************************************** Copyright (C) 2005-2019, 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 __qtractorMidiEditSelect_h #define __qtractorMidiEditSelect_h #include #include // Forward declarations. class qtractorMidiEvent; //------------------------------------------------------------------------- // qtractorMidiEditSelect -- MIDI event selection capsule. class qtractorMidiEditSelect { public: // Constructor. qtractorMidiEditSelect(); // Default destructor. ~qtractorMidiEditSelect(); // Selection item struct. struct Item { // Item constructor. Item(const QRect& re, const QRect& rv, unsigned long dt = 0) : rectEvent(re), rectView(rv), delta(dt), flags(1) {} // Item members. QRect rectEvent; QRect rectView; unsigned long delta; unsigned int flags; }; typedef QHash ItemList; // Event selection item lookup. Item *findItem(qtractorMidiEvent *pEvent); // Event insertion method. void addItem(qtractorMidiEvent *pEvent, const QRect& rectEvent, const QRect& rectView, unsigned long iDeltaTime = 0); // Event selection method. void selectItem(qtractorMidiEvent *pEvent, const QRect& rectEvent, const QRect& rectView, bool bSelect = true, bool bToggle = false); // Item update method (visual rects). void updateItem ( Item *pItem ) { m_rectEvent = m_rectEvent.united(pItem->rectEvent); m_rectView = m_rectView.united(pItem->rectView); } // The united selection rectangle. const QRect& rectEvent() const { return m_rectEvent; } const QRect& rectView() const { return m_rectView; } // Selection list accessor. const ItemList& items() const { return m_items; } // Selection update method. void update(bool bCommit); // Selection commit method. void commit(); // Reset event selection. void clear(); qtractorMidiEvent *anchorEvent() const { return m_pAnchorEvent; } private: // The clip selection list. ItemList m_items; // The united selection rectangle. QRect m_rectEvent; QRect m_rectView; // The most probable anchor event. qtractorMidiEvent *m_pAnchorEvent; }; #endif // __qtractorMidiEditSelect_h // end of qtractorMidiEditSelect.h qtractor-1.5.11/src/PaxHeaders/qtractorInsertPlugin.h0000644000000000000000000000013215124701674017666 xustar0030 mtime=1767080892.785263487 30 atime=1767080892.785263487 30 ctime=1767080892.785263487 qtractor-1.5.11/src/qtractorInsertPlugin.h0000644000175000001440000003336515124701674017670 0ustar00rncbcusers// qtractorInsertPlugin.h // /**************************************************************************** Copyright (C) 2005-2025, rncbc aka Rui Nuno Capela. All rights reserved. Copyright (C) 2011, Holger Dehnhardt. 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 __qtractorInsertPlugin_h #define __qtractorInsertPlugin_h #include "qtractorPlugin.h" // Forward declarations. class qtractorAudioBus; class qtractorMidiBus; class qtractorMidiInputBuffer; class qtractorMidiOutputBuffer; //---------------------------------------------------------------------------- // qtractorInsertPluginType -- Insert pseudo-plugin type instance. // class qtractorInsertPluginType : public qtractorPluginType { public: // Constructor. qtractorInsertPluginType(unsigned short iChannels) : qtractorPluginType(nullptr, iChannels, qtractorPluginType::Insert) {} // Factory method (static) static qtractorPlugin *createPlugin( qtractorPluginList *pList, unsigned short iChannels); // Specific named accessors. unsigned short channels() const { return index(); } }; //---------------------------------------------------------------------------- // qtractorAudioInsertPluginType -- Audio-insert pseudo-plugin type instance. // class qtractorAudioInsertPluginType : public qtractorInsertPluginType { public: // Constructor. qtractorAudioInsertPluginType(unsigned short iChannels) : qtractorInsertPluginType(iChannels) {} // Derived methods. bool open(); void close(); // Compute the number of instances needed // for the given input/output audio channels. unsigned short instances(unsigned short iChannels, bool /*bMidi*/) const { return (iChannels > 0 && iChannels == audioOuts() ? 1 : 0); } // Instance cached-deferred accessors. const QString& aboutText(); }; //---------------------------------------------------------------------------- // qtractorMidiInsertPluginType -- MIDI-insert pseudo-plugin type instance. // class qtractorMidiInsertPluginType : public qtractorInsertPluginType { public: // Constructor. qtractorMidiInsertPluginType() : qtractorInsertPluginType(0) {} // Derived methods. bool open(); void close(); // Compute the number of instances needed. unsigned short instances(unsigned short iChannels, bool bMidi) const { return (iChannels > 0 && bMidi ? 1 : 0); } // Instance cached-deferred accessors. const QString& aboutText(); }; //---------------------------------------------------------------------------- // qtractorInsertPlugin -- Insert pseudo-plugin decl. // class qtractorInsertPlugin : public qtractorPlugin { public: // Constructors. qtractorInsertPlugin(qtractorPluginList *pList, qtractorInsertPluginType *pInsertType) : qtractorPlugin(pList, pInsertType) {} // Forward decls. class Param; }; //---------------------------------------------------------------------------- // qtractorAudioInsertPlugin -- Audio-insert pseudo-plugin instance. // class qtractorAudioInsertPlugin : public qtractorInsertPlugin { public: // Constructors. qtractorAudioInsertPlugin(qtractorPluginList *pList, qtractorInsertPluginType *pInsertType); // Destructor. ~qtractorAudioInsertPlugin(); // Channel/intsance number accessors. void setChannels(unsigned short iChannels); // Do the actual (de)activation. void activate(); void deactivate(); // The main plugin processing procedure. void process(float **ppIBuffer, float **ppOBuffer, unsigned int nframes); // Plugin configuration handlers. void configure(const QString& sKey, const QString& sValue); // Plugin configuration/state snapshot. void freezeConfigs(); void releaseConfigs(); // Audio specific accessor. qtractorAudioBus *audioBus() const; // Override title/name caption. QString title() const; // Report latency. unsigned long latency() const; protected: // Plugin configuration (connections). void freezeConfigs(int iBusMode); // Forward decls. class LatencyParam; private: // Instance variables. qtractorAudioBus *m_pAudioBus; Param *m_pSendGainParam; Param *m_pDryGainParam; Param *m_pWetGainParam; LatencyParam *m_pLatencyParam; float m_fLatencyValue; // Custom optimized processors. void (*m_pfnProcessGain)(float **, unsigned int, unsigned int, unsigned short, float); void (*m_pfnProcessDryWet)(float **, float **, unsigned int, unsigned short, float, float); }; //---------------------------------------------------------------------------- // qtractorMidiInsertPlugin -- MIDI-insert pseudo-plugin instance. // class qtractorMidiInsertPlugin : public qtractorInsertPlugin { public: // Constructors. qtractorMidiInsertPlugin(qtractorPluginList *pList, qtractorInsertPluginType *pInsertType); // Destructor. ~qtractorMidiInsertPlugin(); // Channel/intsance number accessors. void setChannels(unsigned short iChannels); // Do the actual (de)activation. void activate(); void deactivate(); // The main plugin processing procedure. void process(float **ppIBuffer, float **ppOBuffer, unsigned int nframes); // Plugin configuration handlers. void configure(const QString& sKey, const QString& sValue); // Plugin configuration/state snapshot. void freezeConfigs(); void releaseConfigs(); // MIDI specific accessor. qtractorMidiBus *midiBus() const; // Override title/name caption. QString title() const; protected: // Plugin configuration (connections). void freezeConfigs(int iBusMode); private: // Instance variables. qtractorMidiBus *m_pMidiBus; qtractorMidiInputBuffer *m_pMidiInputBuffer; qtractorMidiOutputBuffer *m_pMidiOutputBuffer; Param *m_pSendGainParam; Param *m_pDryGainParam; Param *m_pWetGainParam; }; //---------------------------------------------------------------------------- // qtractorAuxSendPluginType -- Aux-send pseudo-plugin type instance. // class qtractorAuxSendPluginType : public qtractorPluginType { public: // Constructor. qtractorAuxSendPluginType(unsigned short iChannels) : qtractorPluginType(nullptr, iChannels, qtractorPluginType::AuxSend) {} // Factory method (static) static qtractorPlugin *createPlugin( qtractorPluginList *pList, unsigned short iChannels); // Specific named accessors. unsigned short channels() const { return index(); } }; //---------------------------------------------------------------------------- // qtractorAudioAuxSendPluginType -- Audio aux-send pseudo-plugin type. // class qtractorAudioAuxSendPluginType : public qtractorAuxSendPluginType { public: // Constructor. qtractorAudioAuxSendPluginType(unsigned short iChannels) : qtractorAuxSendPluginType(iChannels) {} // Derived methods. bool open(); void close(); // Compute the number of instances needed // for the given input/output audio channels. unsigned short instances(unsigned short iChannels, bool /*bMidi*/) const { return (iChannels > 0 ? 1 : 0); } // Instance cached-deferred accesors. const QString& aboutText(); }; //---------------------------------------------------------------------------- // qtractorMidiAuxSendPluginType -- MIDI Aux-send pseudo-plugin type. // class qtractorMidiAuxSendPluginType : public qtractorAuxSendPluginType { public: // Constructor. qtractorMidiAuxSendPluginType() : qtractorAuxSendPluginType(0) {} // Derived methods. bool open(); void close(); // Compute the number of instances needed // for the given input/output audio channels. unsigned short instances(unsigned short iChannels, bool bMidi) const { return (iChannels > 0 && bMidi ? 1 : 0); } // Instance cached-deferred accesors. const QString& aboutText(); }; //---------------------------------------------------------------------------- // qtractorAuxSendPlugin -- Aux-send pseudo-plugin decl. // class qtractorAuxSendPlugin : public qtractorPlugin { public: // Constructors. qtractorAuxSendPlugin(qtractorPluginList *pList, qtractorAuxSendPluginType *pAuxSendType) : qtractorPlugin(pList, pAuxSendType) {} // Forward decls. class Param; }; //---------------------------------------------------------------------------- // qtractorAudioAuxSendPlugin -- Audio aux-send pseudo-plugin instance. // class qtractorAudioAuxSendPlugin : public qtractorAuxSendPlugin { public: // Constructors. qtractorAudioAuxSendPlugin(qtractorPluginList *pList, qtractorAuxSendPluginType *pAuxSendType); // Destructor. ~qtractorAudioAuxSendPlugin(); // Channel/intsance number accessors. void setChannels(unsigned short iChannels); // The main plugin processing procedure. void process(float **ppIBuffer, float **ppOBuffer, unsigned int nframes); // Plugin configuration handlers. void configure(const QString& sKey, const QString& sValue); // Plugin configuration/state snapshot. void freezeConfigs(); void releaseConfigs(); // Audio bus specific accessors. void setAudioBusName(const QString& sAudioBusName, bool bReset = false); const QString& audioBusName() const; qtractorAudioBus *audioBus() const; // Audio bus to appear on plugin lists. void updateAudioBusName() const; // Audio bus I/O matrix. void setAudioBusMatrix(const QList& matrix); const QList& audioBusMatrix() const; void updateAudioBusMatrix(unsigned short iChannels); // Override title/name caption. QString title() const; protected: // Do the actual (de)activation. void activate(); void deactivate(); private: // Instance variables. qtractorAudioBus *m_pAudioBus; QString m_sAudioBusName; Param *m_pSendGainParam; // Custom optimized processors. void (*m_pfnProcessAdd)(float **, float **, unsigned int, unsigned int, unsigned short, float); float **m_ppOBuffers; int *m_piOBuffers; QList m_matrix; }; //---------------------------------------------------------------------------- // qtractorMidiAuxSendPlugin -- MIDI aux-send pseudo-plugin instance. // class qtractorMidiAuxSendPlugin : public qtractorAuxSendPlugin { public: // Constructors. qtractorMidiAuxSendPlugin(qtractorPluginList *pList, qtractorAuxSendPluginType *pAuxSendType); // Destructor. ~qtractorMidiAuxSendPlugin(); // Channel/intsance number accessors. void setChannels(unsigned short iChannels); // The main plugin processing procedure. void process(float **ppIBuffer, float **ppOBuffer, unsigned int nframes); // Plugin configuration handlers. void configure(const QString& sKey, const QString& sValue); // Plugin configuration/state snapshot. void freezeConfigs(); void releaseConfigs(); // Audio bus specific accessors. void setMidiBusName(const QString& sMidiBusName); const QString& midiBusName() const; qtractorMidiBus *midiBus() const; // Audio bus to appear on plugin lists. void updateMidiBusName() const; // Override title/name caption. QString title() const; protected: // Do the actual (de)activation. void activate(); void deactivate(); private: // Instance variables. qtractorMidiBus *m_pMidiBus; QString m_sMidiBusName; qtractorMidiOutputBuffer *m_pMidiOutputBuffer; Param *m_pSendGainParam; }; //---------------------------------------------------------------------------- // qtractorInsertPlugin::Param -- Common insert plugin control input port. // class qtractorInsertPlugin::Param : public qtractorPlugin::Param { public: // Constructors. Param(qtractorInsertPlugin *pInsertPlugin, unsigned long iIndex) : qtractorPlugin::Param(pInsertPlugin, iIndex) {} // Port range hints predicate methods. bool isBoundedBelow() const { return true; } bool isBoundedAbove() const { return true; } bool isDefaultValue() const { return true; } bool isLogarithmic() const { return true; } bool isSampleRate() const { return false; } bool isInteger() const { return false; } bool isToggled() const { return false; } bool isDisplay() const { return false; } }; //---------------------------------------------------------------------------- // qtractorAudioInsertPlugin::Latency -- Audio insert plugin control input port. // class qtractorAudioInsertPlugin::LatencyParam : public qtractorInsertPlugin::Param { public: // Constructors. LatencyParam(qtractorAudioInsertPlugin *pAudioInsertPlugin, unsigned long iIndex) : qtractorInsertPlugin::Param(pAudioInsertPlugin, iIndex) {} // Port range hints predicate methods. bool isLogarithmic() const { return false; } bool isInteger() const { return true; } bool isDisplay() const { return true; } // Display latency in milliseconds (ms) QString display() const; }; //---------------------------------------------------------------------------- // qtractorAuxSendPlugin::Param -- Common aux-send plugin control input port. // class qtractorAuxSendPlugin::Param : public qtractorPlugin::Param { public: // Constructors. Param(qtractorAuxSendPlugin *pAuxSendPlugin, unsigned long iIndex) : qtractorPlugin::Param(pAuxSendPlugin, iIndex) {} // Port range hints predicate methods. bool isBoundedBelow() const { return true; } bool isBoundedAbove() const { return true; } bool isDefaultValue() const { return true; } bool isLogarithmic() const { return true; } bool isSampleRate() const { return false; } bool isInteger() const { return false; } bool isToggled() const { return false; } bool isDisplay() const { return false; } }; #endif // __qtractorInsertPlugin_h // end of qtractorInsertPlugin.h qtractor-1.5.11/src/PaxHeaders/qtractorExportForm.h0000644000000000000000000000013215124701674017350 xustar0030 mtime=1767080892.784263491 30 atime=1767080892.784263491 30 ctime=1767080892.784263491 qtractor-1.5.11/src/qtractorExportForm.h0000644000175000001440000000770315124701674017347 0ustar00rncbcusers// qtractorExportForm.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorExportForm_h #define __qtractorExportForm_h #include "ui_qtractorExportForm.h" #include "qtractorTrack.h" //---------------------------------------------------------------------------- // qtractorExportForm -- UI wrapper form. class qtractorExportForm : public QDialog { Q_OBJECT public: // Constructor. qtractorExportForm(QWidget *pParent = nullptr); // Destructor. virtual ~qtractorExportForm(); // Default window title (prefix). void setExportTitle(const QString& sExportTitle); const QString& exportTitle() const; // Populate (setup) dialog controls from settings descriptors. void setExportType(qtractorTrack::TrackType exportType); qtractorTrack::TrackType exportType() const; // Retrieve current audio file suffix. const QString& exportExt() const; // Retrieve current aliased file format index. int audioExportFormat() const; int midiExportFormat() const; protected slots: void exportPathChanged(const QString&); void exportPathClicked(); void audioExportTypeChanged(int); void rangeChanged(); void formatChanged(int); void valueChanged(); virtual void stabilizeForm(); protected: // Make up window/dialog title (pure virtual). virtual QString windowTitleEx( const QString& sExportTitle, const QString& sExportType) const = 0; // Audio file type changed aftermath. void audioExportTypeUpdate(int iIndex); // Save export options (settings). void saveExportOptions(); // Range types. enum RangeType { Session = 0, Loop, Punch, Edit, Custom }; // The Qt-designer UI struct... Ui::qtractorExportForm m_ui; // Instance variables... qtractorTrack::TrackType m_exportType; QString m_sExportTitle; QString m_sExportType; QString m_sExportExt; qtractorTimeScale *m_pTimeScale; }; //---------------------------------------------------------------------------- // qtractorExportTrackForm -- UI wrapper form. class qtractorExportTrackForm : public qtractorExportForm { Q_OBJECT public: // Constructor. qtractorExportTrackForm(QWidget *pParent = nullptr); // Destructor. ~qtractorExportTrackForm(); protected slots: // Executive slots. void accept(); void reject(); void stabilizeForm(); protected: // Make up window/dialog title (pure virtual). QString windowTitleEx( const QString& sExportTitle, const QString& sExportType) const; }; //---------------------------------------------------------------------------- // qtractorExportClipForm -- UI wrapper form. class qtractorExportClipForm : public qtractorExportForm { Q_OBJECT public: // Constructor. qtractorExportClipForm(QWidget *pParent = nullptr); // Destructor. ~qtractorExportClipForm(); // Settle/retrieve the export path. void setExportPath(const QString& sExportPath); QString exportPath() const; protected slots: // Executive slots. void accept(); protected: // Make up window/dialog title (pure virtual). QString windowTitleEx( const QString& sExportTitle, const QString& sExportType) const; }; #endif // __qtractorExportForm_h // end of qtractorExportForm.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiThumbView.cpp0000644000000000000000000000013215124701674020313 xustar0030 mtime=1767080892.795263445 30 atime=1767080892.795263445 30 ctime=1767080892.795263445 qtractor-1.5.11/src/qtractorMidiThumbView.cpp0000644000175000001440000003430615124701674020311 0ustar00rncbcusers// qtractorMidiThumbView.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorMidiThumbView.h" #include "qtractorMidiEditView.h" #include "qtractorMidiEditor.h" #include "qtractorMidiClip.h" #include "qtractorSession.h" #include "qtractorTrack.h" #include "qtractorRubberBand.h" #include #include #include #include #include #include //------------------------------------------------------------------------- // qtractorMidiThumbView -- Session track line thumb view. // Constructor. qtractorMidiThumbView::qtractorMidiThumbView( qtractorMidiEditor *pEditor, QWidget *pParent ) : QFrame(pParent) { m_pEditor = pEditor; // Avoid intensively annoying repaints... QFrame::setAttribute(Qt::WA_StaticContents); QFrame::setAttribute(Qt::WA_OpaquePaintEvent); QFrame::setFrameShape(QFrame::Panel); QFrame::setFrameShadow(QFrame::Sunken); QFrame::setMinimumSize(QSize(120, 32)); QFrame::setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); QFrame::setFocusPolicy(Qt::ClickFocus); // Local play-head positioning. m_iPlayHeadX = 0; m_dragState = DragNone; m_pRubberBand = new qtractorRubberBand(QRubberBand::Rectangle, this, 2); #if 0 QPalette pal(m_pRubberBand->palette()); pal.setColor(m_pRubberBand->foregroundRole(), pal.highlight().color()); m_pRubberBand->setPalette(pal); m_pRubberBand->setBackgroundRole(QPalette::NoRole); #endif m_pRubberBand->show(); QFrame::setToolTip(tr("MIDI Thumb view")); } // (Re)create the complete view pixmap. void qtractorMidiThumbView::updateContents (void) { const int w = QFrame::width(); const int h = QFrame::height(); if (w < 1 || h < 1) return; const QPalette& pal = QFrame::palette(); m_pixmap = QPixmap(w, h); m_pixmap.fill(pal.base().color()); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return; qtractorMidiClip *pMidiClip = m_pEditor->midiClip(); if (pMidiClip == nullptr) return; qtractorTrack *pTrack = pMidiClip->track(); if (pTrack == nullptr) return; qtractorMidiSequence *pSeq = pMidiClip->sequence(); if (pSeq == nullptr) return; QPainter painter(&m_pixmap); // painter.initFrom(this); // painter.setFont(QFrame::font()); // Local contents length (in ticks). const int cw = m_pEditor->editView()->contentsWidth() + 1; const unsigned long iClipStart = pMidiClip->clipStart(); const unsigned long iClipEnd = iClipStart + pMidiClip->clipLength(); qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(iClipStart); const unsigned long t0 = pNode->tickFromFrame(iClipStart); const int x0 = pNode->pixelFromTick(t0); int x2; // Check maximum note span... int iNoteSpan = (pSeq->noteMax() - pSeq->noteMin() + 1); if (iNoteSpan < 6) iNoteSpan = 6; const int h2 = 1 + (h / iNoteSpan); const bool bDrumMode = m_pEditor->isDrumMode(); QVector diamond; if (bDrumMode) { const int h4 = (h2 >> 1); diamond.append(QPoint( 0, -h4)); diamond.append(QPoint(-h2, h4)); diamond.append(QPoint( 0, h4 + h2)); diamond.append(QPoint( h2, h4)); } const QColor& fg = pTrack->foreground(); painter.setPen(fg); painter.setBrush(fg.lighter()); qtractorMidiEvent *pEvent = pSeq->events().first(); while (pEvent) { if (pEvent->type() == qtractorMidiEvent::NOTEON) { const unsigned long t1 = t0 + pEvent->time(); pNode = cursor.seekTick(t1); x2 = (w * (pNode->pixelFromTick(t1) - x0)) / cw; const int y2 = h - h2 - (h * (pEvent->note() - pSeq->noteMin())) / iNoteSpan; if (bDrumMode) { const QPolygon& polyg = QPolygon(diamond).translated(x2, y2); painter.drawPolygon(polyg); // diamond } else { const unsigned long t2 = t1 + pEvent->duration(); const int w2 = (w * (pNode->pixelFromTick(t2) - x0)) / cw - x2; // painter.fillRect(x2, y2, w2, h2, fg); painter.drawRect(x2, y2, w2, h2); } } pEvent = pEvent->next(); } // Draw the location marker lines, if any... qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekFrame(iClipStart); while (pMarker) { x2 = (w * (pTimeScale->pixelFromFrame(pMarker->frame) - x0)) / cw; if (x2 < 0 || x2 > w) break; painter.setPen(pMarker->color); painter.drawLine(x2, 0, x2, h); pMarker = pMarker->next(); } // Shade the beyond-end-of-clip zone... const QBrush shade(QColor(0, 0, 0, 60)); pNode = cursor.seekFrame(iClipEnd); x2 = (w * (pTimeScale->pixelFromFrame(iClipEnd) - x0)) / cw; painter.fillRect(QRect(x2, 0, w - x2, h), shade); // Draw the loop-bound lines, if any... if (pSession->isLooping()) { painter.setPen(Qt::darkCyan); x2 = (w * (pTimeScale->pixelFromFrame(pSession->loopStart()) - x0)) / cw; if (x2 > 0 && x2 < w) { painter.fillRect(QRect(0, 0, x2, h), shade); painter.drawLine(x2, 0, x2, h); } x2 = (w * (pTimeScale->pixelFromFrame(pSession->loopEnd()) - x0)) / cw; if (x2 > 0 && x2 < w) { painter.fillRect(QRect(x2, 0, w - x2, h), shade); painter.drawLine(x2, 0, x2, h); } } // Don't forget the punch-in/out ones too... if (pSession->isPunching()) { painter.setPen(Qt::darkMagenta); x2 = (w * (pTimeScale->pixelFromFrame(pSession->punchIn()) - x0)) / cw; if (x2 > 0 && x2 < w) { painter.fillRect(QRect(0, 0, x2, h), shade); painter.drawLine(x2, 0, x2, h); } x2 = (w * (pTimeScale->pixelFromFrame(pSession->punchOut()) - x0)) / cw; if (x2 < w) { painter.fillRect(QRect(x2, 0, w - x2, h), shade); painter.drawLine(x2, 0, x2, h); } } // Update relative play-head position... const unsigned long iPlayHead = pSession->playHead(); m_iPlayHeadX = (w * (pTimeScale->pixelFromFrame(iPlayHead) - x0)) / cw; // May trigger an update now... update(); updateThumb(); } // Update thumb-position. void qtractorMidiThumbView::updateThumb ( int dx ) { const int w = QFrame::width(); const int h = QFrame::height(); if (w < 1 || h < 1) return; qtractorMidiEditView *pEditView = m_pEditor->editView(); const int cw = pEditView->contentsWidth() + 1; int x2 = dx + (w * pEditView->contentsX()) / cw; int w2 = (w * pEditView->viewport()->width()) / cw; if (w2 < 8) w2 = 8; else if (w2 > w) w2 = w; if (x2 < 0) x2 = 0; else if (x2 > w - w2) x2 = w - w2; m_pRubberBand->setGeometry(x2, 0, w2, h); } // Update playhead-position. void qtractorMidiThumbView::updatePlayHead ( unsigned long iPlayHead ) { const int w = QFrame::width(); const int h = QFrame::height(); if (w < 1 || h < 1) return; qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return; qtractorMidiClip *pMidiClip = m_pEditor->midiClip(); if (pMidiClip == nullptr) return; // Extra: update current playhead position... const int cw = m_pEditor->editView()->contentsWidth() + 1; const unsigned long iClipStart = pMidiClip->clipStart(); const int x0 = pTimeScale->pixelFromFrame(iClipStart); const int x2 = (w * (pTimeScale->pixelFromFrame(iPlayHead) - x0)) / cw; if (m_iPlayHeadX != x2) { // Override old playhead line... update(QRect(m_iPlayHeadX, 0, 1, h)); // New position is in... m_iPlayHeadX = x2; // And draw it... update(QRect(m_iPlayHeadX, 0, 1, h)); } } // Update view-position. void qtractorMidiThumbView::updateView ( int dx ) { const int w = QFrame::width(); if (w < 1) return; qtractorMidiEditView *pEditView = m_pEditor->editView(); const int cw = pEditView->contentsWidth() + 1; const int cy = pEditView->contentsY(); int cx = pEditView->contentsX() + (dx * cw) / w; if (cx < 0) cx = 0; m_pEditor->setSyncViewHoldOn(true); pEditView->setContentsPos(cx, cy); } // Set playhead-position (indirect). void qtractorMidiThumbView::setPlayHeadX ( int iPlayHeadX ) { const int w = QFrame::width(); if (w < 1) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return; qtractorMidiClip *pMidiClip = m_pEditor->midiClip(); if (pMidiClip == nullptr) return; const int cw = m_pEditor->editView()->contentsWidth();// + 1; const int x0 = pTimeScale->pixelFromFrame(pMidiClip->clipStart()); pSession->setPlayHead( pTimeScale->frameFromPixel(x0 + ((cw * iPlayHeadX) / w))); m_pEditor->setSyncViewHoldOn(false); } // Session track-line paint method. void qtractorMidiThumbView::paintEvent ( QPaintEvent *pPaintEvent ) { QPainter painter(this); // Render the famous pixmap region... const QRect& rect = pPaintEvent->rect(); painter.drawPixmap(rect, m_pixmap, rect); const int w = QFrame::width(); const int h = QFrame::height(); if (w < 1 || h < 1) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return; qtractorMidiClip *pMidiClip = m_pEditor->midiClip(); if (pMidiClip == nullptr) return; int x2; const int cw = m_pEditor->editView()->contentsWidth() + 1; const unsigned long iClipStart = pMidiClip->clipStart(); const unsigned long x0 = pTimeScale->pixelFromFrame(iClipStart); // Extra: update edit-bound positions... painter.setPen(Qt::blue); x2 = (w * (pTimeScale->pixelFromFrame(pSession->editHead()) - x0)) / cw; if (x2 >= rect.left() && x2 <= rect.right()) painter.drawLine(x2, 0, x2, h); x2 = (w * (pTimeScale->pixelFromFrame(pSession->editTail()) - x0)) / cw; if (x2 >= rect.left() && x2 <= rect.right()) painter.drawLine(x2, 0, x2, h); x2 = (w * (pTimeScale->pixelFromFrame(pSession->playHeadAutoBackward()) - x0)) / cw; if (x2 >= rect.left() && x2 <= rect.right()) { painter.setPen(QColor(240, 0, 0, 60)); painter.drawLine(x2, 0, x2, h); } // Draw current play-head as well... x2 = m_iPlayHeadX; if (x2 >= rect.left() && x2 <= rect.right()) { painter.setPen(Qt::red); painter.drawLine(x2, 0, x2, h); } // Shade-out what's not in view... if (m_pRubberBand) { const QColor rgba(0, 0, 0, 96); const QRect& rect2 = m_pRubberBand->geometry(); if (rect2.left() > rect.left()) painter.fillRect( rect.left(), rect.top(), rect2.left() - rect.left(), rect.height(), rgba); if (rect2.right() < rect.right() + 1) painter.fillRect( rect2.right() + 1, rect.top(), rect.right() - rect2.right(), rect.height(), rgba); } } // Session track-line paint method. void qtractorMidiThumbView::resizeEvent ( QResizeEvent *pResizeEvent ) { QFrame::resizeEvent(pResizeEvent); updateContents(); } // Handle selection with mouse. void qtractorMidiThumbView::mousePressEvent ( QMouseEvent *pMouseEvent ) { // Force null state. m_dragState = DragNone; // Only expected behavior with left-button pressed... if (pMouseEvent->button() == Qt::LeftButton) { QFrame::setCursor(QCursor(Qt::PointingHandCursor)); m_posDrag = pMouseEvent->pos(); const QRect& rect = m_pRubberBand->geometry(); if (rect.contains(pMouseEvent->pos())) { m_dragState = DragStart; } else { m_dragState = DragClick; } } else if (pMouseEvent->button() == Qt::MiddleButton) { // Make it change playhead?... if (pMouseEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) setPlayHeadX(pMouseEvent->pos().x()); } QFrame::mousePressEvent(pMouseEvent); } void qtractorMidiThumbView::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { // Only expected behavior with left-button pressed... if (pMouseEvent->buttons() & Qt::LeftButton) { const QPoint& pos = pMouseEvent->pos(); if ((m_dragState == DragStart || m_dragState == DragClick) && (pos - m_posDrag).manhattanLength() > QApplication::startDragDistance()) { m_dragState = DragMove; QFrame::setCursor(QCursor(Qt::SizeHorCursor)); } if (m_dragState == DragMove && rect().contains(pos)) { updateView(pos.x() - m_posDrag.x()); m_posDrag.setX(pos.x()); } } QFrame::mouseMoveEvent(pMouseEvent); } void qtractorMidiThumbView::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { QFrame::mouseReleaseEvent(pMouseEvent); // Only expected behavior with left-button pressed... if (pMouseEvent->button() == Qt::LeftButton) { const QPoint& pos = pMouseEvent->pos(); if (m_dragState == DragMove) updateView(pos.x() - m_posDrag.x()); else { if (m_dragState == DragStart || m_dragState == DragClick) { const QRect& rect = m_pRubberBand->geometry(); m_posDrag.setX(((rect.left() + rect.right()) >> 1)); updateView(pos.x() - m_posDrag.x()); } // Make it change playhead?... if (pMouseEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) setPlayHeadX(pos.x()); } } // Clean up. resetDragState(); } // Reset drag/select state. void qtractorMidiThumbView::resetDragState (void) { // Restore uncommitted thumb position?... if (m_dragState == DragMove) updateThumb(); // Cancel any dragging out there... if (m_dragState != DragNone) QFrame::unsetCursor(); // Force null state. m_dragState = DragNone; // HACK: give focus to edit-view... m_pEditor->editView()->setFocus(); } // Keyboard event handler. void qtractorMidiThumbView::keyPressEvent ( QKeyEvent *pKeyEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiThumbView::keyPressEvent(%d)", pKeyEvent->key()); #endif switch (pKeyEvent->key()) { case Qt::Key_Escape: resetDragState(); break; default: QFrame::keyPressEvent(pKeyEvent); break; } } // end of qtractorMidiThumbView.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiControlForm.h0000644000000000000000000000013215124701674020312 xustar0030 mtime=1767080892.790263466 30 atime=1767080892.790263466 30 ctime=1767080892.790263466 qtractor-1.5.11/src/qtractorMidiControlForm.h0000644000175000001440000000510015124701674020276 0ustar00rncbcusers// qtractorMidiControlForm.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorMidiControlForm_h #define __qtractorMidiControlForm_h #include "ui_qtractorMidiControlForm.h" #include "qtractorMidiControl.h" // forward decls. class qtractorMidiControlTypeGroup; //---------------------------------------------------------------------------- // qtractorMidiControlForm -- UI wrapper form. class qtractorMidiControlForm : public QDialog { Q_OBJECT public: // Constructor. qtractorMidiControlForm(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()); // Destructor. ~qtractorMidiControlForm(); protected slots: void reject(); void importSlot(); void removeSlot(); void moveUpSlot(); void moveDownSlot(); void mapSlot(); void unmapSlot(); void syncSlot(bool); void reloadSlot(); void exportSlot(); void typeChangedSlot(); void keyChangedSlot(); void valueChangedSlot(); void stabilizeForm(); protected: void stabilizeTypeChange(); void stabilizeKeyChange(); void stabilizeValueChange(); void refreshFiles(); void refreshControlMap(); unsigned short channelFromText(const QString& sText) const; QString textFromChannel(unsigned short iChannel) const; unsigned short paramFromText( qtractorMidiControl::ControlType ctype, const QString& sText) const; QString textFromParam( qtractorMidiControl::ControlType ctype, unsigned short iParam) const; private: // The Qt-designer UI struct... Ui::qtractorMidiControlForm m_ui; // Instance variables... int m_iDirtyCount; int m_iDirtyMap; int m_iUpdating; qtractorMidiControlTypeGroup *m_pControlTypeGroup; }; #endif // __qtractorMidiControlForm_h // end of qtractorMidiControlForm.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiListView.h0000644000000000000000000000013215124701674017614 xustar0030 mtime=1767080892.794263449 30 atime=1767080892.794263449 30 ctime=1767080892.794263449 qtractor-1.5.11/src/qtractorMidiListView.h0000644000175000001440000000527515124701674017615 0ustar00rncbcusers// qtractorMidiListView.h // /**************************************************************************** Copyright (C) 2005-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 __qtractorMidiListView_h #define __qtractorMidiListView_h #include "qtractorFileListView.h" // Forward declarations. class qtractorMidiFile; class qtractorMidiListView; //---------------------------------------------------------------------- // class qtractorMidiFileItem -- MIDI file list view item. // class qtractorMidiFileItem : public qtractorFileListItem { public: // Constructor. qtractorMidiFileItem(const QString& sPath, qtractorMidiFile *pFile); protected: // Virtual tooltip renderer. QString toolTip() const; }; //---------------------------------------------------------------------- // class qtractorMidiChannelItem -- MIDI channel list view item. // class qtractorMidiChannelItem : public qtractorFileChannelItem { public: // Constructors. qtractorMidiChannelItem(qtractorMidiFileItem *pFileItem, const QString& sName, unsigned short iChannel); protected: // Virtual tooltip renderer. QString toolTip() const; }; //---------------------------------------------------------------------------- // qtractorMidiListView -- Group/File list view, supporting drag-n-drop. // class qtractorMidiListView : public qtractorFileListView { Q_OBJECT public: // Constructor. qtractorMidiListView(QWidget *pParent = nullptr); // QListView::addColumn() ids. enum ItemColumn { Name = 0, Format = 1, Tracks = 2, Resolution = 3, Path = 4, LastColumn = 5 }; protected: // Which column is the complete file path? int pathColumn() const { return Path; } // File item factory method. qtractorFileListItem *createFileItem(const QString& sPath); // Prompt for proper file list open. QStringList getOpenFileNames(); }; #endif // __qtractorMidiListView_h // end of qtractorMidiListView.h qtractor-1.5.11/src/PaxHeaders/qtractorTrack.cpp0000644000000000000000000000013115124701674016641 xustar0030 mtime=1767080892.802263416 29 atime=1767080892.80126342 30 ctime=1767080892.802263416 qtractor-1.5.11/src/qtractorTrack.cpp0000644000175000001440000021447715124701674016651 0ustar00rncbcusers// qtractorTrack.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorTrack.h" #include "qtractorSession.h" #include "qtractorAudioClip.h" #include "qtractorMidiClip.h" #include "qtractorDocument.h" #include "qtractorAudioEngine.h" #include "qtractorAudioMonitor.h" #include "qtractorAudioBuffer.h" #include "qtractorMidiEngine.h" #include "qtractorMidiMonitor.h" #include "qtractorMidiManager.h" #include "qtractorInstrument.h" #include "qtractorPlugin.h" #include "qtractorMixer.h" #include "qtractorMeter.h" #include "qtractorCurveFile.h" #include "qtractorFileList.h" #include "qtractorTrackCommand.h" #include "qtractorMainForm.h" #include "qtractorTracks.h" #include "qtractorTrackList.h" #include #include #include // MIDI specific controllers. #define MIDI_CHANNEL_VOLUME 0x07 #define MIDI_CHANNEL_PANNING 0x0a //------------------------------------------------------------------------ // qtractorTrack::StateObserver -- Local track state observer. class qtractorTrack::StateObserver : public qtractorMidiControlObserver { public: // Constructor. StateObserver(qtractorTrack *pTrack, ToolType toolType, qtractorSubject *pSubject) : qtractorMidiControlObserver(pSubject), m_pTrack(pTrack), m_toolType(toolType) {} protected: // Update feedback. void update(bool bUpdate) { const bool bOn = (value() > 0.0f); switch (m_toolType) { case qtractorTrack::Record: m_pTrack->setRecord(bOn); break; case qtractorTrack::Mute: m_pTrack->setMute(bOn); break; case qtractorTrack::Solo: m_pTrack->setSolo(bOn); break; } qtractorMidiControlObserver::update(bUpdate); if (bUpdate) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->tracks()->updateContents(true); } } private: // Members. qtractorTrack *m_pTrack; ToolType m_toolType; }; //---------------------------------------------------------------------------- // qtractorTrack::MidiVolumeObserver -- Local dedicated observer. class qtractorTrack::MidiVolumeObserver : public qtractorObserver { public: // Constructor. MidiVolumeObserver(qtractorTrack *pTrack, qtractorSubject *pSubject) : qtractorObserver(pSubject), m_pTrack(pTrack) {} protected: // Update feedback. void update(bool bUpdate) { m_pTrack->setMidiVolume(int(127.0f * value()) & 0x7f, bUpdate); } private: // Members. qtractorTrack *m_pTrack; }; //---------------------------------------------------------------------------- // qtractorTrack::MidiPanningObserver -- Local dedicated observer. class qtractorTrack::MidiPanningObserver : public qtractorObserver { public: // Constructor. MidiPanningObserver(qtractorTrack *pTrack, qtractorSubject *pSubject) : qtractorObserver(pSubject), m_pTrack(pTrack) {} protected: // Update feedback. void update(bool bUpdate) { m_pTrack->setMidiPanning((0x40 + int(63.0f * value())) & 0x7f, bUpdate); } private: // Members. qtractorTrack *m_pTrack; }; //---------------------------------------------------------------------------- // qtractorTrack::MidiProgramObserver -- Local dedicated observer. class qtractorTrack::MidiProgramObserver : public qtractorObserver { public: // Constructor. MidiProgramObserver(qtractorTrack *pTrack, qtractorSubject *pSubject) : qtractorObserver(pSubject), m_pTrack(pTrack) {} protected: // Update feedback. void update(bool bUpdate) { const int iValue = int(value()); const int iBank = (iValue >> 7) & 0x3fff; const int iProg = (iValue & 0x7f); m_pTrack->setMidiBank(iBank); m_pTrack->setMidiProg(iProg); // Refresh track item, at least the names... if (bUpdate) m_pTrack->updateTrack(); } private: // Members. qtractorTrack *m_pTrack; }; //------------------------------------------------------------------------- // qtractorTrack::Properties -- Track properties structure. // Helper clear/reset method. void qtractorTrack::Properties::clear (void) { trackName.clear(); trackIcon.clear(); trackType = None; monitor = false; record = false; mute = false; solo = false; gain = 1.0f; panning = 0.0f; inputBusName.clear(); outputBusName.clear(); pluginListLatency = false; midiOmni = false; midiChannel = 0; midiBankSelMethod = -1; midiBank = -1; midiProg = -1; midiDrums = false; foreground = Qt::yellow; background = Qt::darkBlue; } // Helper copy method. qtractorTrack::Properties& qtractorTrack::Properties::copy ( const Properties& props ) { if (&props != this) { trackName = props.trackName; trackIcon = props.trackIcon; trackType = props.trackType; monitor = props.monitor; record = props.record; mute = props.mute; solo = props.solo; gain = props.gain; panning = props.panning; inputBusName = props.inputBusName; outputBusName = props.outputBusName; pluginListLatency = props.pluginListLatency; midiOmni = props.midiOmni; midiChannel = props.midiChannel; midiBankSelMethod = props.midiBankSelMethod; midiBank = props.midiBank; midiProg = props.midiProg; midiDrums = props.midiDrums; foreground = props.foreground; background = props.background; } return *this; } // Take(record) descriptor/id registry methods. void qtractorTrack::clearTakeInfo (void) const { m_idtakes.clear(); m_takeids.clear(); } // Retrieve take(record) descriptor/id from registry. qtractorTrack::TakeInfo *qtractorTrack::takeInfo ( int iTakeID ) const { return m_idtakes.value(iTakeID, nullptr); } int qtractorTrack::takeInfoId ( qtractorTrack::TakeInfo *pTakeInfo ) const { return m_takeids.value(pTakeInfo, -1); } // Add/new take(record) descriptor/id to registry. int qtractorTrack::takeInfoNew ( qtractorTrack::TakeInfo *pTakeInfo ) const { QHash::ConstIterator iter = m_takeids.constFind(pTakeInfo); if (iter != m_takeids.constEnd()) { return iter.value(); } else { const int iTakeID = m_takeids.count(); takeInfoAdd(iTakeID, pTakeInfo); return iTakeID; } } void qtractorTrack::takeInfoAdd ( int iTakeID, qtractorTrack::TakeInfo *pTakeInfo ) const { m_idtakes.insert(iTakeID, pTakeInfo); m_takeids.insert(pTakeInfo, iTakeID); } //------------------------------------------------------------------------- // qtractorTrack -- Track container. // Constructor. qtractorTrack::qtractorTrack ( qtractorSession *pSession, TrackType trackType ) { m_pSession = pSession; m_props.trackType = trackType; m_pInputBus = nullptr; m_pOutputBus = nullptr; m_pMonitor = nullptr; m_iMidiTag = 0; m_midiNoteMin = 0; m_midiNoteMax = 0; m_midiVolume = 0; m_midiPanning = 0x40; m_pClipRecord = nullptr; m_iClipRecordStart = 0; m_bClipRecordEx = false; m_clips.setAutoDelete(true); m_pSyncThread = nullptr; m_pMidiVolumeObserver = nullptr; m_pMidiPanningObserver = nullptr; m_pMonitorSubject = new qtractorSubject(); m_pMonitorSubject->setToggled(true); m_pRecordSubject = new qtractorSubject(); m_pMuteSubject = new qtractorSubject(); m_pSoloSubject = new qtractorSubject(); m_pRecordSubject->setToggled(true); m_pMuteSubject->setToggled(true); m_pSoloSubject->setToggled(true); m_pMonitorObserver = new qtractorMidiControlObserver(m_pMonitorSubject); m_pRecordObserver = new StateObserver(this, Record, m_pRecordSubject); m_pMuteObserver = new StateObserver(this, Mute, m_pMuteSubject); m_pSoloObserver = new StateObserver(this, Solo, m_pSoloSubject); unsigned int iFlags = qtractorPluginList::Track; if (trackType == qtractorTrack::Midi) iFlags |= qtractorPluginList::Midi; m_pPluginList = new qtractorPluginList(0, iFlags); m_pCurveFile = new qtractorCurveFile(m_pPluginList->curveList()); m_pMidiProgramObserver = nullptr; setHeight(HeightBase); // Default track height. clear(); } // Default desstructor. qtractorTrack::~qtractorTrack (void) { close(); clear(); if (m_pSoloObserver) delete m_pSoloObserver; if (m_pMuteObserver) delete m_pMuteObserver; if (m_pRecordObserver) delete m_pRecordObserver; if (m_pMonitorObserver) delete m_pMonitorObserver; if (m_pSoloSubject) delete m_pSoloSubject; if (m_pMuteSubject) delete m_pMuteSubject; if (m_pRecordSubject) delete m_pRecordSubject; if (m_pMonitorSubject) delete m_pMonitorSubject; qDeleteAll(m_controllers); m_controllers.clear(); if (m_pCurveFile) delete m_pCurveFile; if (m_pPluginList) delete m_pPluginList; if (m_pMonitor) delete m_pMonitor; } // Reset track. void qtractorTrack::clear (void) { setClipRecord(nullptr); clearTakeInfo(); m_clips.clear(); m_pPluginList->clear(); m_pCurveFile->clear(); m_iMidiTag = 0; m_midiNoteMin = 0; m_midiNoteMax = 0; m_midiVolume = 0; m_midiPanning = 0x40; m_props.midiBankSelMethod = -1; m_props.midiBank = -1; m_props.midiProg = -1; m_props.monitor = false; m_props.record = false; m_props.mute = false; m_props.solo = false; m_props.gain = 1.0f; m_props.panning = 0.0f; if (m_pSyncThread) { if (m_pSyncThread->isRunning()) do { m_pSyncThread->setRunState(false); // m_pSyncThread->terminate(); m_pSyncThread->sync(); } while (!m_pSyncThread->wait(100)); delete m_pSyncThread; m_pSyncThread = nullptr; } m_iZoomHeightBase = -1; } // Track open method. bool qtractorTrack::open (void) { close(); if (m_pSession == nullptr) return false; // Depending on track type... qtractorEngine *pEngine = nullptr; qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); switch (m_props.trackType) { case qtractorTrack::Audio: pEngine = pAudioEngine; break; case qtractorTrack::Midi: pEngine = pMidiEngine; break; default: break; } // Got it? if (pEngine == nullptr) return false; // (Re)assign the input bus to the track. m_pInputBus = pEngine->findInputBus(inputBusName()); // Fallback to first usable one... if (m_pInputBus == nullptr) { QListIterator iter(pEngine->buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Input) { m_pInputBus = pBus; break; } } // Set the bus name back... if (m_pInputBus) setInputBusName(m_pInputBus->busName()); } // (Re)assign the output bus to the track. m_pOutputBus = pEngine->findOutputBus(outputBusName()); // Fallback to first usable one... if (m_pOutputBus == nullptr) { QListIterator iter(pEngine->buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { m_pOutputBus = pBus; break; } } // Set the bus name back... if (m_pOutputBus) setOutputBusName(m_pOutputBus->busName()); } #if 0 // Check proper bus assignment... if (m_pInputBus == nullptr || m_pOutputBus == nullptr) return false; #endif // Remember current (output) monitor, for later deletion... qtractorMonitor *pMonitor = m_pMonitor; m_pMonitor = nullptr; // (Re)allocate (output) monitor... switch (m_props.trackType) { case qtractorTrack::Audio: { qtractorAudioBus *pAudioBus = static_cast (m_pOutputBus); if (pAudioBus) { m_pMonitor = new qtractorAudioMonitor( pAudioBus->channels(), m_props.gain, m_props.panning); m_pPluginList->setChannels(pAudioBus->channels(), qtractorPluginList::AudioTrack); } break; } case qtractorTrack::Midi: { qtractorMidiBus *pMidiBus = static_cast (m_pOutputBus); if (pMidiBus) { qtractorMidiMonitor *pMidiMonitor = static_cast (pMonitor); if (pMidiMonitor) { m_pMonitor = new qtractorMidiMonitor(*pMidiMonitor); } else { m_pMonitor = new qtractorMidiMonitor( m_props.gain, m_props.panning); } m_pMidiVolumeObserver = new MidiVolumeObserver( this, m_pMonitor->gainSubject()); m_pMidiPanningObserver = new MidiPanningObserver( this, m_pMonitor->panningSubject()); } // Get audio bus as for the plugin list... qtractorAudioBus *pAudioBus = nullptr; qtractorMidiManager *pMidiManager = m_pPluginList->midiManager(); if (pMidiManager) pAudioBus = pMidiManager->audioOutputBus(); if (pAudioBus == nullptr) { // Output bus gets to be the first available output bus... QListIterator iter(pAudioEngine->buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { pAudioBus = static_cast (pBus); break; } } } // Set plugin-list buffer alright... if (pAudioBus) { m_pPluginList->setChannels(pAudioBus->channels(), qtractorPluginList::MidiTrack); } // Set MIDI bank/program observer... if (m_pPluginList->midiProgramSubject()) { m_pMidiProgramObserver = new MidiProgramObserver(this, m_pPluginList->midiProgramSubject()); } break; } default: break; } // Before we get rid of old monitor... if (pMonitor) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { // Update mixer strip... qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) { qtractorMixerStrip *pStrip = pMixer->trackRack()->findStrip(pMonitor); if (pStrip) pStrip->setTrack(this); } // Update track-list as well... qtractorTrackList *pTrackList = pMainForm->tracks()->trackList(); if (pTrackList) pTrackList->updateTrack(this); } // Update gain and panning curve new subjects... #if 0 qtractorCurveList *pCurveList = m_pPluginList->curveList(); if (pCurveList) { qtractorCurve *pCurve = pCurveList->first(); while (pCurve) { qtractorSubject *pSubject = pCurve->subject(); if (pSubject) { if (pSubject == pMonitor->gainSubject()) { pCurve->setSubject(m_pMonitor->gainSubject()); m_pMonitor->gainSubject()->setCurve(pCurve); } else if (pSubject == pMonitor->panningSubject()) { pCurve->setSubject(m_pMonitor->panningSubject()); m_pMonitor->panningSubject()->setCurve(pCurve); } } pCurve = pCurve->next(); } } #else // Panning subject ownership transfer... qtractorCurve *pCurve = pMonitor->panningSubject()->curve(); if (pCurve) { pCurve->setSubject(m_pMonitor->panningSubject()); m_pMonitor->panningSubject()->setCurve(pCurve); } // Gain subject ownership transfer... pCurve = pMonitor->gainSubject()->curve(); if (pCurve) { pCurve->setSubject(m_pMonitor->gainSubject()); m_pMonitor->gainSubject()->setCurve(pCurve); } #endif // That's it... delete pMonitor; } // Set on plug-list latency compensation... m_pPluginList->setLatency(m_props.pluginListLatency); // Ah, at least make new name feedback... updateTrackName(); // Formerly on mixer track strip update, // but now where it surely belongs, here... // mapControllers(); applyCurveFile(m_pCurveFile); // Done. return (m_pMonitor != nullptr); } // Track close method. void qtractorTrack::close (void) { #if 0// Sure there's no subject automation going on?... qtractorSubject::resetQueue(); #endif if (m_pMidiVolumeObserver) { delete m_pMidiVolumeObserver; m_pMidiVolumeObserver = nullptr; } if (m_pMidiPanningObserver) { delete m_pMidiPanningObserver; m_pMidiPanningObserver = nullptr; } if (m_pMidiProgramObserver) { delete m_pMidiProgramObserver; m_pMidiProgramObserver = nullptr; } #if 0 if (m_pMonitor) { delete m_pMonitor; m_pMonitor = nullptr; } #endif m_pInputBus = nullptr; m_pOutputBus = nullptr; setClipRecord(nullptr); } // Session accessor. qtractorSession *qtractorTrack::session (void) const { return m_pSession; } // Track name accessors. const QString& qtractorTrack::trackName (void) const { return m_props.trackName; } void qtractorTrack::setTrackName ( const QString& sTrackName ) { m_props.trackName = sTrackName; } QString qtractorTrack::shortTrackName (void) const { return shortTrackName(m_props.trackName); } void qtractorTrack::updateTrackName (void) { const int iMaxLength = 12; const QString sEllipsis(3, '.'); QString sTrackName = shortTrackName(); if (sTrackName.length() >= iMaxLength + sEllipsis.length()) sTrackName = sTrackName.left(iMaxLength).trimmed() + sEllipsis; m_pMonitorSubject->setName(QObject::tr("%1 Monitor").arg(sTrackName)); m_pRecordSubject->setName(QObject::tr("%1 Record").arg(sTrackName)); m_pMuteSubject->setName(QObject::tr("%1 Mute").arg(sTrackName)); m_pSoloSubject->setName(QObject::tr("%1 Solo").arg(sTrackName)); if (m_pMonitor) { if (m_props.trackType == qtractorTrack::Midi) { m_pMonitor->gainSubject()->setName( QObject::tr("%1 Volume").arg(sTrackName)); } else { m_pMonitor->gainSubject()->setName( QObject::tr("%1 Gain").arg(sTrackName)); } m_pMonitor->panningSubject()->setName( QObject::tr("%1 Pan").arg(sTrackName)); } m_pPluginList->setName(sTrackName); } QString qtractorTrack::shortTrackName ( const QString& sTrackName ) { return QString(sTrackName).remove(QRegularExpression("\n.+")).simplified(); } // Track icon (filename) accessors. const QString& qtractorTrack::trackIcon (void) const { return m_props.trackIcon; } void qtractorTrack::setTrackIcon ( const QString& sTrackIcon ) { if (!QIcon::fromTheme(sTrackIcon).isNull() || QFileInfo(sTrackIcon).exists()) m_props.trackIcon = sTrackIcon; else m_props.trackIcon.clear(); } // Track type accessors. qtractorTrack::TrackType qtractorTrack::trackType (void) const { return m_props.trackType; } void qtractorTrack::setTrackType ( qtractorTrack::TrackType trackType ) { // Don't change anything if we're already the same type... if (m_props.trackType == trackType) return; // Acquire a new midi-tag... if (m_props.trackType == qtractorTrack::Midi) m_pSession->releaseMidiTag(this); // Set new track type, now... m_props.trackType = trackType; // Acquire a new MIDI-tag and setup the plugin-list flags... unsigned int iFlags = qtractorPluginList::Track; // Get current audio output bus for the plugin list... if (m_props.trackType == qtractorTrack::Midi) { m_pSession->acquireMidiTag(this); iFlags |= qtractorPluginList::Midi; } // (Re)set plugin-list... m_pPluginList->setChannels(0, iFlags); } // Record monitoring status accessors. bool qtractorTrack::isMonitor (void) const { return (m_pMonitorSubject->value() > 0.0f); } void qtractorTrack::setMonitor ( bool bMonitor ) { m_props.monitor = bMonitor; m_pMonitorSubject->setValue(bMonitor ? 1.0f : 0.0f); m_pSession->autoDeactivatePlugins(); } // Record status accessors. void qtractorTrack::setRecord ( bool bRecord ) { const bool bOldRecord = m_props.record; // isRecord(); if ((bOldRecord && !bRecord) || (!bOldRecord && bRecord)) m_pSession->setRecordTracks(bRecord); m_props.record = bRecord; m_pRecordSubject->setValue(bRecord ? 1.0f : 0.0f); m_pSession->autoDeactivatePlugins(); if (m_pSession->isRecording()) { unsigned long iClipStart = m_pSession->playHead(); if (m_pSession->isPunching()) { const unsigned long iPunchIn = m_pSession->punchIn(); if (iClipStart < iPunchIn) iClipStart = iPunchIn; } const unsigned long iFrameTime = m_pSession->frameTimeEx(); m_pSession->trackRecord(this, bRecord, iClipStart, iFrameTime); } else if (!bRecord) m_pSession->trackRecord(this, false, 0, 0); } bool qtractorTrack::isRecord (void) const { return m_props.record; // (m_pRecordSubject->value() > 0.0f); } // Mute status accessors. void qtractorTrack::setMute ( bool bMute ) { if (m_pSession->isPlaying() && bMute) m_pSession->trackMute(this, bMute); const bool bOldMute = m_props.mute; // isMute(); if ((bOldMute && !bMute) || (!bOldMute && bMute)) m_pSession->setMuteTracks(bMute); m_props.mute = bMute; m_pMuteSubject->setValue(bMute ? 1.0f : 0.0f); if (m_pSession->isPlaying() && !bMute) m_pSession->trackMute(this, bMute); m_pSession->autoDeactivatePlugins(); } bool qtractorTrack::isMute (void) const { return m_props.mute; // (m_pMuteSubject->value() > 0.0f); } // Solo status accessors. void qtractorTrack::setSolo ( bool bSolo ) { if (m_pSession->isPlaying() && bSolo) m_pSession->trackSolo(this, bSolo); const bool bOldSolo = m_props.solo; // isSolo(); if ((bOldSolo && !bSolo) || (!bOldSolo && bSolo)) m_pSession->setSoloTracks(bSolo); m_props.solo = bSolo; m_pSoloSubject->setValue(bSolo ? 1.0f : 0.0f); if (m_pSession->isPlaying() && !bSolo) m_pSession->trackSolo(this, bSolo); m_pSession->autoDeactivatePlugins(); } bool qtractorTrack::isSolo (void) const { return m_props.solo; // (m_pSoloSubject->value() > 0.0f); } // Track gain (volume) accessor. void qtractorTrack::setGain ( float fGain ) { m_props.gain = fGain; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) { qtractorMixerStrip *pStrip = pMixer->trackRack()->findStrip(m_pMonitor); if (pStrip && pStrip->meter()) pStrip->meter()->setGain(fGain); } } } float qtractorTrack::gain (void) const { return (m_pMonitor ? m_pMonitor->gain() : m_props.gain); } float qtractorTrack::prevGain (void) const { return (m_pMonitor ? m_pMonitor->prevGain() : 1.0f); } // Track stereo-panning accessor. void qtractorTrack::setPanning ( float fPanning ) { m_props.panning = fPanning; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) { qtractorMixerStrip *pStrip = pMixer->trackRack()->findStrip(m_pMonitor); if (pStrip && pStrip->meter()) pStrip->meter()->setPanning(fPanning); } } } float qtractorTrack::panning (void) const { return (m_pMonitor ? m_pMonitor->panning() : m_props.panning); } float qtractorTrack::prevPanning (void) const { return (m_pMonitor ? m_pMonitor->prevPanning() : 0.0f); } // MIDI specific: track-tag accessors. void qtractorTrack::setMidiTag ( unsigned short iMidiTag ) { m_iMidiTag = (iMidiTag % 0xff); } unsigned short qtractorTrack::midiTag (void) const { return m_iMidiTag; } // MIDI specific: omni (capture) mode acessors. void qtractorTrack::setMidiOmni ( bool bMidiOmni ) { m_props.midiOmni = bMidiOmni; } bool qtractorTrack::isMidiOmni (void) const { return m_props.midiOmni; } // MIDI specific: channel acessors. void qtractorTrack::setMidiChannel ( unsigned short iMidiChannel ) { m_props.midiChannel = iMidiChannel; } unsigned short qtractorTrack::midiChannel (void) const { return m_props.midiChannel; } // MIDI specific: bank accessors. void qtractorTrack::setMidiBankSelMethod ( int iMidiBankSelMethod ) { m_props.midiBankSelMethod = iMidiBankSelMethod; } int qtractorTrack::midiBankSelMethod (void) const { return m_props.midiBankSelMethod; } // MIDI specific: bank accessors. void qtractorTrack::setMidiBank ( int iMidiBank ) { m_props.midiBank = iMidiBank; } int qtractorTrack::midiBank (void) const { return m_props.midiBank; } // MIDI specific: program accessors. void qtractorTrack::setMidiProg ( int iMidiProg ) { m_props.midiProg = iMidiProg; } int qtractorTrack::midiProg (void) const { return m_props.midiProg; } // MIDI drum mode (UI). void qtractorTrack::setMidiDrums ( bool bMidiDrums ) { m_props.midiDrums = bMidiDrums; } bool qtractorTrack::isMidiDrums (void) const { return m_props.midiDrums; } // MIDI specific: note minimum/maximum range. void qtractorTrack::setMidiNoteMin ( unsigned char note ) { if (m_midiNoteMin > note || m_midiNoteMin == 0) m_midiNoteMin = note; } unsigned char qtractorTrack::midiNoteMin (void) const { return m_midiNoteMin; } void qtractorTrack::setMidiNoteMax ( unsigned char note ) { if (m_midiNoteMax < note || m_midiNoteMax == 0) m_midiNoteMax = note; } unsigned char qtractorTrack::midiNoteMax (void) const { return m_midiNoteMax; } // MIDI specific volume controller. void qtractorTrack::setMidiVolume ( unsigned char vol, bool bUpdate ) { if (m_midiVolume == vol) return; m_midiVolume = vol; qtractorMidiBus *pMidiBus = static_cast (m_pOutputBus); if (pMidiBus == nullptr) return; if (bUpdate) { pMidiBus->setController(this, MIDI_CHANNEL_VOLUME, vol); return; } qtractorMidiManager *pMidiManager = nullptr; if (m_pPluginList) pMidiManager = m_pPluginList->midiManager(); if (pMidiManager) pMidiManager->setController(midiChannel(), MIDI_CHANNEL_VOLUME, vol); if (pMidiBus->pluginList_out()) { pMidiManager = pMidiBus->pluginList_out()->midiManager(); if (pMidiManager) pMidiManager->setController(midiChannel(), MIDI_CHANNEL_VOLUME, vol); } } unsigned char qtractorTrack::midiVolume (void) const { return m_midiVolume; } // MIDI specific panning controller. void qtractorTrack::setMidiPanning ( unsigned char pan, bool bUpdate ) { if (m_midiPanning == pan) return; m_midiPanning = pan; qtractorMidiBus *pMidiBus = static_cast (m_pOutputBus); if (pMidiBus == nullptr) return; if (bUpdate) { pMidiBus->setController(this, MIDI_CHANNEL_PANNING, pan); return; } qtractorMidiManager *pMidiManager = nullptr; if (m_pPluginList) pMidiManager = m_pPluginList->midiManager(); if (pMidiManager) pMidiManager->setController(midiChannel(), MIDI_CHANNEL_PANNING, pan); if (pMidiBus->pluginList_out()) { pMidiManager = pMidiBus->pluginList_out()->midiManager(); if (pMidiManager) pMidiManager->setController(midiChannel(), MIDI_CHANNEL_PANNING, pan); } } unsigned char qtractorTrack::midiPanning (void) const { return m_midiPanning; } // Assigned input bus name accessors. void qtractorTrack::setInputBusName ( const QString& sBusName ) { m_props.inputBusName = sBusName; } const QString& qtractorTrack::inputBusName (void) const { return m_props.inputBusName; } // Assigned output bus name accessors. void qtractorTrack::setOutputBusName ( const QString& sBusName ) { m_props.outputBusName = sBusName; } const QString& qtractorTrack::outputBusName (void) const { return m_props.outputBusName; } // Assigned audio bus accessors. qtractorBus *qtractorTrack::inputBus (void) const { return m_pInputBus; } qtractorBus *qtractorTrack::outputBus (void) const { return m_pOutputBus; } // Track monitor accessors. qtractorMonitor *qtractorTrack::monitor (void) const { return m_pMonitor; } // Track plugin-chain accessors. qtractorPluginList *qtractorTrack::pluginList (void) const { return m_pPluginList; } // Plugin latency compensation accessors. void qtractorTrack::setPluginListLatency ( bool bPluginListLatency ) { m_props.pluginListLatency = bPluginListLatency; } bool qtractorTrack::isPluginListLatency (void) const { return m_props.pluginListLatency; } // Normalized view height accessors. int qtractorTrack::height (void) const { return m_iHeight; } void qtractorTrack::setHeight ( int iHeight ) { m_iHeight = iHeight; if (m_iHeight < HeightMin) m_iHeight = HeightMin; m_iHeightBase = m_iHeight; updateZoomHeight(); } void qtractorTrack::updateHeight (void) { if (m_pSession) { m_iHeight = (100 * m_iZoomHeight) / m_pSession->verticalZoom(); if (m_iHeight < HeightMin) m_iHeight = HeightMin; } } // Base zoomed view height accessor. int qtractorTrack::zoomHeightBase (void) const { if (m_pSession) return (m_iHeightBase * m_pSession->verticalZoom()) / 100; else return m_iHeightBase; } // Zoomed view height accessors. int qtractorTrack::zoomHeight (void) const { return m_iZoomHeight; } void qtractorTrack::setZoomHeight ( int iZoomHeight ) { m_iZoomHeight = iZoomHeight; if (m_iZoomHeight < HeightMin) m_iZoomHeight = HeightMin; updateHeight(); } void qtractorTrack::updateZoomHeight (void) { if (m_pSession) { m_iZoomHeight = (m_iHeight * m_pSession->verticalZoom()) / 100; if (m_iZoomHeight < HeightMin) m_iZoomHeight = HeightMin; } } // Visual height minimize/toggle. int qtractorTrack::minimizeZoomHeight (void) { int iZoomHeight = m_iZoomHeight; if (iZoomHeight > HeightMin) { m_iZoomHeightBase = iZoomHeight; iZoomHeight = HeightMin; } else if (m_iZoomHeightBase > HeightMin) iZoomHeight = m_iZoomHeightBase; else if (m_pSession) { iZoomHeight = (HeightBase * m_pSession->verticalZoom()) / 100; m_iZoomHeightBase = iZoomHeight; } return iZoomHeight; } // Clip list management methods. const qtractorList& qtractorTrack::clips (void) const { return m_clips; } // Insert a new clip in guaranteed sorted fashion. void qtractorTrack::addClip ( qtractorClip *pClip ) { if (m_pSession == nullptr) return; if (m_props.trackType == qtractorTrack::Audio) m_pSession->files()->addClipItem(qtractorFileList::Audio, pClip); else if (m_props.trackType == qtractorTrack::Midi) { m_pSession->files()->addClipItem(qtractorFileList::Midi, pClip); // Special case for initial MIDI tracks... qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) { if (midiBankSelMethod() < 0) setMidiBankSelMethod(pMidiClip->bankSelMethod()); if (midiBank() < 0) setMidiBank(pMidiClip->bank()); if (midiProg() < 0) setMidiProg(pMidiClip->prog()); } } // Now do insert the clip in proper place in track... insertClip(pClip); } void qtractorTrack::addClipEx ( qtractorClip *pClip ) { // Preliminary settings... pClip->setTrack(this); pClip->open(); addClip(pClip); } void qtractorTrack::insertClip ( qtractorClip *pClip ) { qtractorClip *pNextClip = m_clips.first(); while (pNextClip && pNextClip->clipStart() < pClip->clipStart()) pNextClip = pNextClip->next(); if (pNextClip) m_clips.insertBefore(pClip, pNextClip); else m_clips.append(pClip); pClip->setActive(true); } void qtractorTrack::removeClipEx ( qtractorClip *pClip ) { // Preliminary settings... // pClip->setTrack(nullptr); pClip->close(); removeClip(pClip); } void qtractorTrack::removeClip ( qtractorClip *pClip ) { if (m_pSession == nullptr) return; if (m_props.trackType == qtractorTrack::Audio) m_pSession->files()->removeClipItem(qtractorFileList::Audio, pClip); else if (m_props.trackType == qtractorTrack::Midi) m_pSession->files()->removeClipItem(qtractorFileList::Midi, pClip); if (m_bClipRecordEx && m_pClipRecord == pClip) { m_bClipRecordEx = false; m_pClipRecord = nullptr; m_iClipRecordStart = 0; setRecord(false); } pClip->setActive(false); m_clips.unlink(pClip); } // Current clip on record (capture). void qtractorTrack::setClipRecord ( qtractorClip *pClipRecord ) { if (!m_bClipRecordEx && m_pClipRecord) delete m_pClipRecord; m_pClipRecord = pClipRecord; if (m_pClipRecord == nullptr) { m_iClipRecordStart = 0; if (m_bClipRecordEx) { m_bClipRecordEx = false; setRecord(false); } } } qtractorClip *qtractorTrack::clipRecord (void) const { return m_pClipRecord; } // Current clip on record absolute start frame (capture). void qtractorTrack::setClipRecordStart ( unsigned long iClipRecordStart ) { m_iClipRecordStart = iClipRecordStart; } unsigned long qtractorTrack::clipRecordStart (void) const { return m_iClipRecordStart; } unsigned long qtractorTrack::clipRecordEnd ( unsigned long iFrameTime ) const { // HACK: Care of loop-recording + punch-in/out... if (m_pSession && m_pSession->isPunching()) { const unsigned long iPlayHead = m_pSession->playHead(); unsigned long iPunchIn = m_pSession->punchIn(); unsigned long iPunchOut = m_pSession->punchOut(); if (iFrameTime < iPunchIn) return iPunchIn; // Cancelled... const unsigned long iLoopStart = m_pSession->loopStart(); const unsigned long iLoopEnd = m_pSession->loopEnd(); if (iLoopStart < iLoopEnd && m_pSession->loopRecordingMode() > 0 && iLoopEnd > iPlayHead && iLoopEnd < iFrameTime) { if (iPunchIn < iLoopStart) iPunchIn = iLoopStart; if (iPunchOut > iLoopEnd) iPunchOut = iLoopEnd; const unsigned long iLoopLength = iLoopEnd - iLoopStart; const unsigned int iLoopCount = (iFrameTime - iPunchIn) / iLoopLength; iPunchOut += iLoopCount * iLoopLength; } // Make sure it's really about to punch-out... if (iFrameTime > iPunchOut) return iPunchOut; } unsigned long iClipRecordEnd = iFrameTime; if (iClipRecordEnd >= m_iClipRecordStart) iClipRecordEnd -= m_iClipRecordStart; if (m_pClipRecord) iClipRecordEnd += m_pClipRecord->clipStart(); return iClipRecordEnd; } // Set current clip on exclusive recording. void qtractorTrack::setClipRecordEx ( bool bClipRecordEx ) { m_bClipRecordEx = bClipRecordEx; } bool qtractorTrack::isClipRecordEx (void) const { return m_bClipRecordEx; } // Background color accessors. void qtractorTrack::setBackground ( const QColor& bg ) { m_props.background = bg; } const QColor& qtractorTrack::background (void) const { return m_props.background; } // Foreground color accessors. void qtractorTrack::setForeground ( const QColor& fg ) { m_props.foreground = fg; } const QColor& qtractorTrack::foreground (void) const { return m_props.foreground; } // Default track color saturation factor [0..500]. int qtractorTrack::g_iTrackColorSaturation = 100; void qtractorTrack::setTrackColorSaturation ( int iTrackColorSaturation ) { g_iTrackColorSaturation = iTrackColorSaturation; } int qtractorTrack::trackColorSaturation (void) { return g_iTrackColorSaturation; } // Generate a default track color. QColor qtractorTrack::trackColor ( int iTrack ) { const int c[3] = { 0xff, 0xcc, 0x99 }; QColor color( c[iTrack % 3], c[(iTrack / 3) % 3], c[(iTrack / 9) % 3]); int h, s, v; color.getHsv(&h, &s, &v); s += (s * (g_iTrackColorSaturation - 100)) / 100; if (s < 0) s = 0; else if (s > 255) s = 255; color.setHsv(h, s, v); return color; } // Alternate properties accessor. void qtractorTrack::setProperties ( const qtractorTrack::Properties& props ) { m_props = props; } qtractorTrack::Properties& qtractorTrack::properties (void) { return m_props; } // Reset state properties (as needed on copy/duplicate) void qtractorTrack::resetProperties (void) { const bool bMonitor = m_props.monitor; const bool bRecord = m_props.record; const bool bMute = m_props.mute; const bool bSolo = m_props.solo; m_props.monitor = false; m_props.record = false;; m_props.mute = false; m_props.solo = false; setMonitor(bMonitor); setRecord(bRecord); setMute(bMute); setSolo(bSolo); } // Track special process cycle executive. void qtractorTrack::process ( qtractorClip *pClip, unsigned long iFrameStart, unsigned long iFrameEnd ) { // Audio-buffers needs some preparation... const unsigned int nframes = iFrameEnd - iFrameStart; qtractorAudioMonitor *pAudioMonitor = nullptr; qtractorAudioBus *pOutputBus = nullptr; if (m_props.trackType == qtractorTrack::Audio) { pAudioMonitor = static_cast (m_pMonitor); pOutputBus = static_cast (m_pOutputBus); // Prepare this track buffer... if (pOutputBus) { qtractorAudioBus *pInputBus = (m_pSession->isTrackMonitor(this) ? static_cast (m_pInputBus) : nullptr); pOutputBus->buffer_prepare(nframes, pInputBus); } } // Playback... if (!isMute() && (!m_pSession->soloTracks() || isSolo())) { const unsigned long iLatency = m_pPluginList->latency(); const unsigned long iFrameStart2 = iFrameStart + iLatency; const unsigned long iFrameEnd2 = iFrameEnd + iLatency; // Now, for every clip... while (pClip && pClip->clipStart() < iFrameEnd2) { if (!pClip->isClipMute() && iFrameStart2 < pClip->clipStart() + pClip->clipLength()) pClip->process(iFrameStart2, iFrameEnd2); pClip = pClip->next(); } } // Audio buffers needs monitoring and commitment... if (pAudioMonitor && pOutputBus) { // Plugin chain post-processing... m_pPluginList->process(pOutputBus->buffer(), nframes); // Monitor passthru... pAudioMonitor->process(pOutputBus->buffer(), nframes); // Actually render it... pOutputBus->buffer_commit(nframes); } } // Freewheeling process cycle executive (needed for export). void qtractorTrack::process_export ( qtractorClip *pClip, unsigned long iFrameStart, unsigned long iFrameEnd ) { // Track automation processing... process_curve(iFrameStart); // Audio-buffers needs some preparation... const unsigned int nframes = iFrameEnd - iFrameStart; qtractorAudioMonitor *pAudioMonitor = nullptr; qtractorAudioBus *pOutputBus = nullptr; if (m_props.trackType == qtractorTrack::Audio) { pAudioMonitor = static_cast (m_pMonitor); pOutputBus = static_cast (m_pOutputBus); if (pOutputBus) pOutputBus->buffer_prepare(nframes); } // Playback... if (!isMute() && (!m_pSession->soloTracks() || isSolo())) { const unsigned long iLatency = m_pPluginList->latency(); const unsigned long iFrameStart2 = iFrameStart + iLatency; const unsigned long iFrameEnd2 = iFrameEnd + iLatency; // Now, for every clip... while (pClip && pClip->clipStart() < iFrameEnd2) { if (iFrameStart2 < pClip->clipStart() + pClip->clipLength()) pClip->process_export(iFrameStart2, iFrameEnd2); pClip = pClip->next(); } } // Audio buffers needs monitoring and commitment... if (pAudioMonitor && pOutputBus) { // Plugin chain post-processing... m_pPluginList->process(pOutputBus->buffer(), nframes); // Monitor passthru... pAudioMonitor->process(pOutputBus->buffer(), nframes); // Actually render it... pOutputBus->buffer_commit(nframes); } } // Track special process record executive (audio recording only). void qtractorTrack::process_record ( unsigned long iFrameStart, unsigned long iFrameEnd ) { // Audio track-recording? qtractorAudioBus *pInputBus = static_cast (m_pInputBus); qtractorAudioClip *pAudioClip = static_cast (m_pClipRecord); if (pAudioClip && pInputBus) { // Clip recording... unsigned int nframes = iFrameEnd - iFrameStart; // Punch-in/out recording... if (m_pSession->isPunching() && m_pSession->frameTimeEx() < iFrameEnd) { const unsigned long iPunchIn = m_pSession->punchIn(); // Punch-in (likely...) if (iPunchIn < iFrameEnd) { unsigned int offset = 0; if (iPunchIn >= iFrameStart) { offset += (iPunchIn - iFrameStart); nframes = (iFrameEnd - iPunchIn); } // Punch-in recording... pAudioClip->write( pInputBus->in(), nframes, pInputBus->channels(), offset); } } else { // Regular full-length recording... pAudioClip->write( pInputBus->in(), nframes, pInputBus->channels()); } // Record non-passthru metering... qtractorAudioMonitor *pAudioMonitor = static_cast (m_pMonitor); if (pAudioMonitor) { pAudioMonitor->process_meter( pInputBus->in(), nframes, pInputBus->channels()); } } } // Track special process automation executive. void qtractorTrack::process_curve ( unsigned long iFrame ) { qtractorCurveList *pCurveList = curveList(); if (pCurveList && pCurveList->isProcess()) pCurveList->process(iFrame); } // Track paint method. void qtractorTrack::drawTrack ( QPainter *pPainter, const QRect& trackRect, unsigned long iTrackStart, unsigned long iTrackEnd, qtractorClip *pClip ) { const int y = trackRect.y(); const int h = trackRect.height(); if (pClip == nullptr) pClip = m_clips.first(); // Track/clip background... QColor bg = background(); const QPen pen(bg.darker()); bg.setAlpha(192); // translucency... #ifdef CONFIG_GRADIENT QLinearGradient grad(0, y, 0, y + h); grad.setColorAt(0.4, bg); grad.setColorAt(1.0, bg.darker(130)); const QBrush brush(grad); #else const QBrush brush(bg); #endif qtractorClip *pClipRecordEx = (m_bClipRecordEx ? m_pClipRecord : nullptr); const int x0 = m_pSession->pixelFromFrame(iTrackStart); while (pClip) { unsigned long iClipStart = pClip->clipStart(); if (iClipStart > iTrackEnd) break; unsigned long iClipEnd = iClipStart + pClip->clipLength(); if (iClipStart < iTrackEnd && iClipEnd > iTrackStart) { unsigned long iClipOffset = 0; // pClip->clipOffset(); if (iClipStart < iTrackStart) { iClipOffset += (iTrackStart - iClipStart); iClipStart = iTrackStart; } if (iClipEnd > iTrackEnd) iClipEnd = iTrackEnd; const int x1 = m_pSession->pixelFromFrame(iClipStart) - x0; const int x2 = m_pSession->pixelFromFrame(iClipEnd) - x0; if (x1 < x2) { pPainter->setPen(pen); pPainter->setBrush(brush); // Draw the clip... const QRect clipRect(x1, y, x2 - x1, h); pClip->drawClip(pPainter, clipRect, iClipOffset); if (pClip == pClipRecordEx) pPainter->fillRect(clipRect, QColor(255, 0, 0, 60)); else if (pClip->isClipMute()) pPainter->fillRect(clipRect, QColor(0, 0, 0, 60)); } } pClip = pClip->next(); } if (m_props.mute || (!m_props.solo && m_pSession->soloTracks())) pPainter->fillRect(trackRect, QColor(0, 0, 0, 60)); } // Track loop point setler. void qtractorTrack::setLoop ( unsigned long iLoopStart, unsigned long iLoopEnd ) { qtractorClip *pClip = m_clips.first(); while (pClip) { // Convert loop-points from session to clip... const unsigned long iClipStart = pClip->clipStart(); const unsigned long iClipEnd = iClipStart + pClip->clipLength(); if (iLoopStart < iClipEnd && iLoopEnd > iClipStart) { // Set clip inner-loop... pClip->setLoop( (iLoopStart > iClipStart ? iLoopStart - iClipStart : 0), (iLoopEnd < iClipEnd ? iLoopEnd : iClipEnd) - iClipStart); } else { // Clear/reaet clip-loop... pClip->setLoop(0, 0); } pClip = pClip->next(); } } // MIDI track instrument patching. void qtractorTrack::setMidiPatch ( qtractorInstrumentList *pInstruments ) { const int iProg = midiProg(); if (iProg < 0) return; qtractorMidiBus *pMidiBus = static_cast (m_pOutputBus); if (pMidiBus == nullptr) return; const unsigned short iChannel = midiChannel(); const qtractorMidiBus::Patch& patch = pMidiBus->patch(iChannel); const int iBank = midiBank(); int iBankSelMethod = midiBankSelMethod(); if (iBankSelMethod < 0) { const QString& sInstrumentName = patch.instrumentName; if (!sInstrumentName.isEmpty() && pInstruments->contains(sInstrumentName)) { const qtractorInstrument& instr = pInstruments->value(sInstrumentName); iBankSelMethod = instr.bankSelMethod(); } else if (iBank >= 0) iBankSelMethod = 0; } pMidiBus->setPatch(iChannel, patch.instrumentName, iBankSelMethod, iBank, iProg, this); } // Update all clips editors. void qtractorTrack::updateClipEditors (void) { qtractorClip *pClip = m_clips.first(); while (pClip) { pClip->updateEditor(true); pClip = pClip->next(); } } // Audio buffer ring-cache (playlist) methods. qtractorAudioBufferThread *qtractorTrack::syncThread (void) { if (m_pSyncThread == nullptr) { m_pSyncThread = new qtractorAudioBufferThread(); m_pSyncThread->start(QThread::HighPriority); } else { m_pSyncThread->checkSyncSize(m_clips.count()); } return m_pSyncThread; } // Track state (monitor record, mute, solo) button setup. qtractorSubject *qtractorTrack::monitorSubject (void) const { return m_pMonitorSubject; } qtractorSubject *qtractorTrack::recordSubject (void) const { return m_pRecordSubject; } qtractorSubject *qtractorTrack::muteSubject (void) const { return m_pMuteSubject; } qtractorSubject *qtractorTrack::soloSubject (void) const { return m_pSoloSubject; } qtractorMidiControlObserver *qtractorTrack::monitorObserver (void) const { return m_pMonitorObserver; } qtractorMidiControlObserver *qtractorTrack::recordObserver (void) const { return static_cast (m_pRecordObserver); } qtractorMidiControlObserver *qtractorTrack::muteObserver (void) const { return static_cast (m_pMuteObserver); } qtractorMidiControlObserver *qtractorTrack::soloObserver (void) const { return static_cast (m_pSoloObserver); } // Track state (monitor) notifier (proto-slot). void qtractorTrack::monitorChangeNotify ( bool bOn ) { #ifdef CONFIG_DEBUG qDebug("qtractorTrack[%p]::monitorChangeNotify(%d)", this, int(bOn)); #endif // Put it in the form of an undoable command... if (m_pSession) m_pSession->execute( new qtractorTrackMonitorCommand(this, bOn)); } // Track state (record, mute, solo) notifier (proto-slot). void qtractorTrack::stateChangeNotify ( ToolType toolType, bool bOn ) { #ifdef CONFIG_DEBUG qDebug("qtractorTrack[%p]::stateChangeNotify(%d, %d)", this, int(toolType), int(bOn)); #endif // Put it in the form of an undoable command... if (m_pSession) m_pSession->execute( new qtractorTrackStateCommand(this, toolType, bOn)); } // Document element methods. bool qtractorTrack::loadElement ( qtractorDocument *pDocument, QDomElement *pElement ) { if (m_pSession == nullptr) return false; qtractorTrack::setTrackName(pElement->attribute("name")); qtractorTrack::setTrackType( qtractorTrack::trackTypeFromText(pElement->attribute("type"))); // Reset take(record) descriptor/id registry. clearTakeInfo(); // Load track children... for (QDomNode nChild = pElement->firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert node to element... QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; // Load (other) track properties.. if (eChild.tagName() == "properties") { for (QDomNode nProp = eChild.firstChild(); !nProp.isNull(); nProp = nProp.nextSibling()) { // Convert property node to element... QDomElement eProp = nProp.toElement(); if (eProp.isNull()) continue; if (eProp.tagName() == "input-bus") qtractorTrack::setInputBusName(eProp.text()); else if (eProp.tagName() == "output-bus") qtractorTrack::setOutputBusName(eProp.text()); else if (eProp.tagName() == "plugin-list-latency") qtractorTrack::setPluginListLatency( qtractorDocument::boolFromText(eProp.text())); else if (eProp.tagName() == "midi-omni") qtractorTrack::setMidiOmni( qtractorDocument::boolFromText(eProp.text())); else if (eProp.tagName() == "midi-channel") qtractorTrack::setMidiChannel(eProp.text().toUShort()); else if (eProp.tagName() == "midi-bank-sel-method") qtractorTrack::setMidiBankSelMethod(eProp.text().toInt()); else if (eProp.tagName() == "midi-bank") qtractorTrack::setMidiBank(eProp.text().toInt()); else if (eProp.tagName() == "midi-program") qtractorTrack::setMidiProg(eProp.text().toInt()); else if (eProp.tagName() == "midi-drums") qtractorTrack::setMidiDrums( qtractorDocument::boolFromText(eProp.text())); else if (eProp.tagName() == "icon") qtractorTrack::setTrackIcon(eProp.text()); } } else // Load track state.. if (eChild.tagName() == "state") { for (QDomNode nState = eChild.firstChild(); !nState.isNull(); nState = nState.nextSibling()) { // Convert state node to element... QDomElement eState = nState.toElement(); if (eState.isNull()) continue; if (eState.tagName() == "mute") qtractorTrack::setMute( qtractorDocument::boolFromText(eState.text())); else if (eState.tagName() == "solo") qtractorTrack::setSolo( qtractorDocument::boolFromText(eState.text())); else if (eState.tagName() == "record") qtractorTrack::setRecord( qtractorDocument::boolFromText(eState.text())); else if (eState.tagName() == "monitor") qtractorTrack::setMonitor( qtractorDocument::boolFromText(eState.text())); else if (eState.tagName() == "gain") qtractorTrack::setGain(eState.text().toFloat()); else if (eState.tagName() == "panning") qtractorTrack::setPanning(eState.text().toFloat()); } } else if (eChild.tagName() == "view") { for (QDomNode nView = eChild.firstChild(); !nView.isNull(); nView = nView.nextSibling()) { // Convert view node to element... QDomElement eView = nView.toElement(); if (eView.isNull()) continue; if (eView.tagName() == "height") { qtractorTrack::setHeight(eView.text().toInt()); } else if (eView.tagName() == "background-color") { #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) qtractorTrack::setBackground(QColor::fromString(eView.text())); #else qtractorTrack::setBackground(QColor(eView.text())); #endif } else if (eView.tagName() == "foreground-color") { #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) qtractorTrack::setForeground(QColor::fromString(eView.text())); #else qtractorTrack::setForeground(QColor(eView.text())); #endif } } } else if (eChild.tagName() == "controllers") { // Load track controllers... qtractorTrack::loadControllers(&eChild); } else if (eChild.tagName() == "curve-file") { // Load track automation curves... qtractorTrack::loadCurveFile(&eChild, m_pCurveFile); } else // Load clips... if (eChild.tagName() == "clips" && !pDocument->isTemplate()) { for (QDomNode nClip = eChild.firstChild(); !nClip.isNull(); nClip = nClip.nextSibling()) { // Convert clip node to element... QDomElement eClip = nClip.toElement(); if (eClip.isNull()) continue; if (eClip.tagName() == "clip") { qtractorClip *pClip = nullptr; switch (qtractorTrack::trackType()) { case qtractorTrack::Audio: pClip = new qtractorAudioClip(this); break; case qtractorTrack::Midi: pClip = new qtractorMidiClip(this); break; case qtractorTrack::None: default: break; } if (pClip == nullptr) return false; if (!pClip->loadElement(pDocument, &eClip)) return false; qtractorTrack::addClipEx(pClip); } } } else // Load plugins... if (eChild.tagName() == "plugins") m_pPluginList->loadElement(pDocument, &eChild); } // Reset take(record) descriptor/id registry. clearTakeInfo(); return true; } bool qtractorTrack::saveElement ( qtractorDocument *pDocument, QDomElement *pElement ) const { pElement->setAttribute("name", qtractorTrack::trackName()); pElement->setAttribute("type", qtractorTrack::textFromTrackType(qtractorTrack::trackType())); // Reset take(record) descriptor/id registry. clearTakeInfo(); // Save track properties... QDomElement eProps = pDocument->document()->createElement("properties"); const QString& sTrackIcon = qtractorTrack::trackIcon(); if (!sTrackIcon.isEmpty()) pDocument->saveTextElement("icon", sTrackIcon, &eProps); pDocument->saveTextElement("input-bus", qtractorTrack::inputBusName(), &eProps); pDocument->saveTextElement("output-bus", qtractorTrack::outputBusName(), &eProps); const bool bPluginListLatency = qtractorTrack::isPluginListLatency(); if (bPluginListLatency) pDocument->saveTextElement("plugin-list-latency", qtractorDocument::textFromBool(bPluginListLatency), &eProps); if (qtractorTrack::trackType() == qtractorTrack::Midi) { pDocument->saveTextElement("midi-omni", qtractorDocument::textFromBool(qtractorTrack::isMidiOmni()), &eProps); pDocument->saveTextElement("midi-channel", QString::number(qtractorTrack::midiChannel()), &eProps); if (qtractorTrack::midiBankSelMethod() >= 0) { pDocument->saveTextElement("midi-bank-sel-method", QString::number(qtractorTrack::midiBankSelMethod()), &eProps); } if (qtractorTrack::midiBank() >= 0) { pDocument->saveTextElement("midi-bank", QString::number(qtractorTrack::midiBank()), &eProps); } if (qtractorTrack::midiProg() >= 0) { pDocument->saveTextElement("midi-program", QString::number(qtractorTrack::midiProg()), &eProps); } pDocument->saveTextElement("midi-drums", qtractorDocument::textFromBool(qtractorTrack::isMidiDrums()), &eProps); } pElement->appendChild(eProps); // Save track state... QDomElement eState = pDocument->document()->createElement("state"); pDocument->saveTextElement("mute", qtractorDocument::textFromBool(qtractorTrack::isMute()), &eState); pDocument->saveTextElement("solo", qtractorDocument::textFromBool(qtractorTrack::isSolo()), &eState); pDocument->saveTextElement("record", qtractorDocument::textFromBool(qtractorTrack::isRecord() && !qtractorTrack::isClipRecordEx()), &eState); pDocument->saveTextElement("monitor", qtractorDocument::textFromBool(qtractorTrack::isMonitor()), &eState); pDocument->saveTextElement("gain", QString::number(qtractorTrack::gain()), &eState); pDocument->saveTextElement("panning", QString::number(qtractorTrack::panning()), &eState); pElement->appendChild(eState); // Save track view attributes... QDomElement eView = pDocument->document()->createElement("view"); pDocument->saveTextElement("height", QString::number(qtractorTrack::height()), &eView); pDocument->saveTextElement("background-color", qtractorTrack::background().name(), &eView); pDocument->saveTextElement("foreground-color", qtractorTrack::foreground().name(), &eView); pElement->appendChild(eView); // Save track controllers... QDomElement eControllers = pDocument->document()->createElement("controllers"); qtractorTrack::saveControllers(pDocument, &eControllers); pElement->appendChild(eControllers); // Save track automation... qtractorCurveList *pCurveList = qtractorTrack::curveList(); if (pCurveList && !pCurveList->isEmpty()) { qtractorCurveFile cfile(pCurveList); QDomElement eCurveFile = pDocument->document()->createElement("curve-file"); qtractorTrack::saveCurveFile(pDocument, &eCurveFile, &cfile); pElement->appendChild(eCurveFile); } // Clips are not saved when in template mode... if (!pDocument->isTemplate()) { // Save track clips... QDomElement eClips = pDocument->document()->createElement("clips"); for (qtractorClip *pClip = qtractorTrack::clips().first(); pClip; pClip = pClip->next()) { // Create the new clip element... QDomElement eClip = pDocument->document()->createElement("clip"); if (!pClip->saveElement(pDocument, &eClip)) return false; // Add this clip... eClips.appendChild(eClip); } pElement->appendChild(eClips); } // Save track plugins... QDomElement ePlugins = pDocument->document()->createElement("plugins"); m_pPluginList->saveElement(pDocument, &ePlugins); pElement->appendChild(ePlugins); // Reset take(record) descriptor/id registry. clearTakeInfo(); return true; } // Track type textual helper methods. qtractorTrack::TrackType qtractorTrack::trackTypeFromText ( const QString& sText ) { TrackType trackType = None; if (sText == "audio") trackType = Audio; else if (sText == "midi") trackType = Midi; return trackType; } QString qtractorTrack::textFromTrackType ( TrackType trackType ) { QString sText; switch (trackType) { case Audio: sText = "audio"; break; case Midi: sText = "midi"; break; case None: default: sText = "none"; break; } return sText; } // Load track state (record, mute, solo) controllers (MIDI). void qtractorTrack::loadControllers ( QDomElement *pElement ) { qtractorMidiControl::loadControllers(pElement, m_controllers); } // Save track state (record, mute, solo) controllers (MIDI). void qtractorTrack::saveControllers ( qtractorDocument *pDocument, QDomElement *pElement ) const { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; if (m_pMonitor == nullptr) return; qtractorMidiControl::Controllers controllers; if (pMidiControl->isMidiObserverMapped(m_pMonitorObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = m_pMonitorObserver->subject()->name(); pController->index = 0; // 0=MonitorObserver pController->ctype = m_pMonitorObserver->type(); pController->channel = m_pMonitorObserver->channel(); pController->param = m_pMonitorObserver->param(); pController->logarithmic = m_pMonitorObserver->isLogarithmic(); pController->feedback = m_pMonitorObserver->isFeedback(); pController->invert = m_pMonitorObserver->isInvert(); pController->hook = m_pMonitorObserver->isHook(); pController->latch = m_pMonitorObserver->isLatch(); controllers.append(pController); } qtractorMidiControlObserver *pPanObserver = m_pMonitor->panningObserver(); if (pMidiControl->isMidiObserverMapped(pPanObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = m_pMonitorObserver->subject()->name(); pController->index = 1; // 1=PanObserver pController->ctype = pPanObserver->type(); pController->channel = pPanObserver->channel(); pController->param = pPanObserver->param(); pController->logarithmic = pPanObserver->isLogarithmic(); pController->feedback = pPanObserver->isFeedback(); pController->invert = pPanObserver->isInvert(); pController->hook = pPanObserver->isHook(); pController->latch = pPanObserver->isLatch(); controllers.append(pController); } qtractorMidiControlObserver *pGainObserver = m_pMonitor->gainObserver(); if (pMidiControl->isMidiObserverMapped(pGainObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = pGainObserver->subject()->name(); pController->index = 2; // 2=GainObserver pController->ctype = pGainObserver->type(); pController->channel = pGainObserver->channel(); pController->param = pGainObserver->param(); pController->logarithmic = pGainObserver->isLogarithmic(); pController->feedback = pGainObserver->isFeedback(); pController->invert = pGainObserver->isInvert(); pController->hook = pGainObserver->isHook(); pController->latch = pGainObserver->isLatch(); controllers.append(pController); } if (pMidiControl->isMidiObserverMapped(m_pRecordObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = m_pRecordObserver->subject()->name(); pController->index = 3; // 3=RecordObserver pController->ctype = m_pRecordObserver->type(); pController->channel = m_pRecordObserver->channel(); pController->param = m_pRecordObserver->param(); pController->logarithmic = m_pRecordObserver->isLogarithmic(); pController->feedback = m_pRecordObserver->isFeedback(); pController->invert = m_pRecordObserver->isInvert(); pController->hook = m_pRecordObserver->isHook(); pController->latch = m_pRecordObserver->isLatch(); controllers.append(pController); } if (pMidiControl->isMidiObserverMapped(m_pMuteObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = m_pMuteObserver->subject()->name(); pController->index = 4; // 4=MuteObserver pController->ctype = m_pMuteObserver->type(); pController->channel = m_pMuteObserver->channel(); pController->param = m_pMuteObserver->param(); pController->logarithmic = m_pMuteObserver->isLogarithmic(); pController->feedback = m_pMuteObserver->isFeedback(); pController->invert = m_pMuteObserver->isInvert(); pController->hook = m_pMuteObserver->isHook(); pController->latch = m_pMuteObserver->isLatch(); controllers.append(pController); } if (pMidiControl->isMidiObserverMapped(m_pSoloObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = m_pSoloObserver->subject()->name(); pController->index = 5; // 5=SoloObserver pController->ctype = m_pSoloObserver->type(); pController->channel = m_pSoloObserver->channel(); pController->param = m_pSoloObserver->param(); pController->logarithmic = m_pSoloObserver->isLogarithmic(); pController->feedback = m_pSoloObserver->isFeedback(); pController->invert = m_pSoloObserver->isInvert(); pController->hook = m_pSoloObserver->isHook(); pController->latch = m_pSoloObserver->isLatch(); controllers.append(pController); } qtractorMidiControl::saveControllers(pDocument, pElement, controllers); qDeleteAll(controllers); controllers.clear(); } // Map track state (monitor, gain, pan, record, mute, solo) controllers (MIDI). void qtractorTrack::mapControllers (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; if (m_pMonitor == nullptr) return; QListIterator iter(m_controllers); while (iter.hasNext()) { qtractorMidiControl::Controller *pController = iter.next(); qtractorMidiControlObserver *pObserver = nullptr; switch (pController->index) { case 0: // 0=MonitorObserver pObserver = monitorObserver(); break; case 1: // 1=PanObserver pObserver = m_pMonitor->panningObserver(); break; case 2: // 2=GainObserver pObserver = m_pMonitor->gainObserver(); break; case 3: // 3=RecordObserver pObserver = recordObserver(); break; case 4: // 4=MuteObserver pObserver = muteObserver(); break; case 5: // 5=SoloObserver pObserver = soloObserver(); break; } if (pObserver) { pObserver->setType(pController->ctype); pObserver->setChannel(pController->channel); pObserver->setParam(pController->param); pObserver->setLogarithmic(pController->logarithmic); pObserver->setFeedback(pController->feedback); pObserver->setInvert(pController->invert); pObserver->setHook(pController->hook); pObserver->setLatch(pController->latch); pMidiControl->mapMidiObserver(pObserver); } } qDeleteAll(m_controllers); m_controllers.clear(); } // Track automation curve list accessor. qtractorCurveList *qtractorTrack::curveList (void) const { return (m_pPluginList ? m_pPluginList->curveList() : nullptr); } // Track automation curve serializer accessor. qtractorCurveFile *qtractorTrack::curveFile (void) const { return m_pCurveFile; } // Track automation current curve accessors. void qtractorTrack::setCurrentCurve ( qtractorCurve *pCurrentCurve ) { qtractorCurveList *pCurveList = curveList(); if (pCurveList) pCurveList->setCurrentCurve(pCurrentCurve); } qtractorCurve *qtractorTrack::currentCurve (void) const { qtractorCurveList *pCurveList = curveList(); return (pCurveList ? pCurveList->currentCurve() : nullptr); } // Load track automation curves (monitor, gain, pan, record, mute, solo). void qtractorTrack::loadCurveFile ( QDomElement *pElement, qtractorCurveFile *pCurveFile ) { if (pCurveFile) pCurveFile->load(pElement); } // Save track automation curves (monitor, gain, pan, record, mute, solo). void qtractorTrack::saveCurveFile ( qtractorDocument *pDocument, QDomElement *pElement, qtractorCurveFile *pCurveFile ) const { if (m_pMonitor == nullptr) return; if (pCurveFile == nullptr) return; qtractorCurveList *pCurveList = pCurveFile->list(); if (pCurveList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; pCurveFile->clear(); pCurveFile->setBaseDir(pSession->sessionDir()); qtractorCurve *pCurve; pCurve = monitorSubject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pCurve->subject()->name(); pCurveItem->index = 0; // 0=MonitorSubject pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->channel = 0; pCurveItem->param = 80; // 80=General Purpose Button 1 (on/off) pCurveItem->mode = pCurve->mode(); pCurveItem->process = pCurve->isProcess(); pCurveItem->capture = pCurve->isCapture(); pCurveItem->locked = pCurve->isLocked(); pCurveItem->logarithmic = pCurve->isLogarithmic(); pCurveItem->color = pCurve->color(); pCurveItem->subject = pCurve->subject(); pCurveFile->addItem(pCurveItem); } pCurve = m_pMonitor->panningSubject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pCurve->subject()->name(); pCurveItem->index = 1; // 1=PanningSubject pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->channel = 0; pCurveItem->param = 10; // 10=Pan Position (coarse) pCurveItem->mode = pCurve->mode(); pCurveItem->process = pCurve->isProcess(); pCurveItem->capture = pCurve->isCapture(); pCurveItem->locked = pCurve->isLocked(); pCurveItem->logarithmic = pCurve->isLogarithmic(); pCurveItem->color = pCurve->color(); pCurveItem->subject = pCurve->subject(); pCurveFile->addItem(pCurveItem); } pCurve = m_pMonitor->gainSubject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pCurve->subject()->name(); pCurveItem->index = 2; // 2=GainSubject pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->channel = 0; pCurveItem->param = 7; // 7=Volume (coarse) pCurveItem->mode = pCurve->mode(); pCurveItem->process = pCurve->isProcess(); pCurveItem->capture = pCurve->isCapture(); pCurveItem->locked = pCurve->isLocked(); pCurveItem->logarithmic = pCurve->isLogarithmic(); pCurveItem->color = pCurve->color(); pCurveItem->subject = pCurve->subject(); pCurveFile->addItem(pCurveItem); } pCurve = recordSubject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pCurve->subject()->name(); pCurveItem->index = 3; // 3=RecordSubject pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->channel = 0; pCurveItem->param = 81; // 81=General Purpose Button 2 (on/off) pCurveItem->mode = pCurve->mode(); pCurveItem->process = pCurve->isProcess(); pCurveItem->capture = pCurve->isCapture(); pCurveItem->locked = pCurve->isLocked(); pCurveItem->logarithmic = pCurve->isLogarithmic(); pCurveItem->color = pCurve->color(); pCurveItem->subject = pCurve->subject(); pCurveFile->addItem(pCurveItem); } pCurve = muteSubject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pCurve->subject()->name(); pCurveItem->index = 4; // 4=MuteSubject pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->channel = 0; pCurveItem->param = 82; // 82=General Purpose Button 3 (on/off) pCurveItem->mode = pCurve->mode(); pCurveItem->process = pCurve->isProcess(); pCurveItem->capture = pCurve->isCapture(); pCurveItem->locked = pCurve->isLocked(); pCurveItem->logarithmic = pCurve->isLogarithmic(); pCurveItem->color = pCurve->color(); pCurveItem->subject = pCurve->subject(); pCurveFile->addItem(pCurveItem); } pCurve = soloSubject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pCurve->subject()->name(); pCurveItem->index = 5; // 5=SoloSubject pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->channel = 0; pCurveItem->param = 83; // 83=General Purpose Button 4 (on/off) pCurveItem->mode = pCurve->mode(); pCurveItem->process = pCurve->isProcess(); pCurveItem->capture = pCurve->isCapture(); pCurveItem->locked = pCurve->isLocked(); pCurveItem->logarithmic = pCurve->isLogarithmic(); pCurveItem->color = pCurve->color(); pCurveItem->subject = pCurve->subject(); pCurveFile->addItem(pCurveItem); } if (pCurveFile->isEmpty()) return; const QString sBaseName(shortTrackName() + "_curve"); const bool bTemporary = pDocument->isTemporary(); const QString& sFilename = pSession->createFilePath(sBaseName, "mid", !bTemporary); pSession->files()->addFileItem(qtractorFileList::Midi, sFilename, bTemporary); pCurveFile->setFilename(sFilename); pCurveFile->save(pDocument, pElement, pSession->timeScale()); } // Apply track automation curves (monitor, gain, pan, record, mute, solo). void qtractorTrack::applyCurveFile ( qtractorCurveFile *pCurveFile ) const { if (m_pMonitor == nullptr) return; if (pCurveFile == nullptr) return; if (pCurveFile->items().isEmpty()) return; qtractorCurveList *pCurveList = pCurveFile->list(); if (pCurveList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; pCurveFile->setBaseDir(pSession->sessionDir()); QListIterator iter(pCurveFile->items()); while (iter.hasNext()) { qtractorCurveFile::Item *pCurveItem = iter.next(); switch (pCurveItem->index) { case 0: // 0=MonitorSubject pCurveItem->subject = monitorSubject(); break; case 1: // 1=PanSubject pCurveItem->subject = m_pMonitor->panningSubject(); break; case 2: // 2=GainSubject pCurveItem->subject = m_pMonitor->gainSubject(); break; case 3: // 3=RecordSubject pCurveItem->subject = recordSubject(); break; case 4: // 4=MuteSubject pCurveItem->subject = muteSubject(); break; case 5: // 5=SoloSubject pCurveItem->subject = soloSubject(); break; } } pCurveFile->apply(pSession->timeScale()); } // Update tracks/list-view. void qtractorTrack::updateTrack (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return; pTracks->updateTrack(this); } void qtractorTrack::updateMidiTrack (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return; pTracks->updateMidiTrack(this); } void qtractorTrack::updateMidiClips (void) { const int iMidiBankSelMethod = qtractorTrack::midiBankSelMethod(); const int iMidiBank = qtractorTrack::midiBank(); const int iMidiProg = qtractorTrack::midiProg(); for (qtractorClip *pClip = qtractorTrack::clips().first(); pClip; pClip = pClip->next()) { qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) { // Check for MIDI track's bank/program changes... bool bDirty = pMidiClip->isDirty(); qtractorMidiSequence *pSeq = pMidiClip->sequence(); if (pSeq && ( pSeq->bankSelMethod() != iMidiBankSelMethod || pSeq->bank() != iMidiBank || pSeq->prog() != iMidiProg)) { pSeq->setBankSelMethod(iMidiBankSelMethod); pSeq->setBank(iMidiBank); pSeq->setProg(iMidiProg); bDirty = true; } // Are any dirty changes pending commit? if (bDirty) { const QString& sFilename = pMidiClip->createFilePathRevision(); pMidiClip->saveCopyFile(sFilename, true); } // Re-open the MIDI clip anyway... //pMidiClip->open(); } } } // Update all plugin forms, if visible. void qtractorTrack::refreshPluginForms (void) { if (m_pPluginList == nullptr) return; qtractorPlugin *pPlugin = m_pPluginList->first(); while (pPlugin) { pPlugin->refreshForm(); pPlugin = pPlugin->next(); } } // end of qtractorTrack.cpp qtractor-1.5.11/src/PaxHeaders/qtractorThumbView.cpp0000644000000000000000000000012715124701674017514 xustar0029 mtime=1767080892.80126342 29 atime=1767080892.80126342 29 ctime=1767080892.80126342 qtractor-1.5.11/src/qtractorThumbView.cpp0000644000175000001440000003272415124701674017510 0ustar00rncbcusers// qtractorThumbView.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorThumbView.h" #include "qtractorTrackView.h" #include "qtractorSession.h" #include "qtractorTracks.h" #include "qtractorClip.h" #include "qtractorRubberBand.h" #include "qtractorMainForm.h" #include #include #include #include #include #include //------------------------------------------------------------------------- // qtractorThumbView -- Session track line thumb view. // Constructor. qtractorThumbView::qtractorThumbView( QWidget *pParent ) : QFrame(pParent) { // Avoid intensively annoying repaints... QFrame::setAttribute(Qt::WA_StaticContents); QFrame::setAttribute(Qt::WA_OpaquePaintEvent); QFrame::setFrameShape(QFrame::Panel); QFrame::setFrameShadow(QFrame::Sunken); QFrame::setMinimumSize(QSize(120, 32)); QFrame::setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); QFrame::setFocusPolicy(Qt::ClickFocus); // Local contents length (in frames). m_iContentsLength = 0; // Local play-head positioning. m_iPlayHeadX = 0; m_dragState = DragNone; m_pRubberBand = new qtractorRubberBand(QRubberBand::Rectangle, this, 2); #if 0 QPalette pal(m_pRubberBand->palette()); pal.setColor(m_pRubberBand->foregroundRole(), pal.highlight().color()); m_pRubberBand->setPalette(pal); m_pRubberBand->setBackgroundRole(QPalette::NoRole); #endif m_pRubberBand->show(); QFrame::setToolTip(tr("Thumb view")); } // (Re)create the complete view pixmap. void qtractorThumbView::updateContents (void) { const int w = QFrame::width(); const int h = QFrame::height(); if (w < 1 || h < 1) return; const QPalette& pal = QFrame::palette(); m_pixmap = QPixmap(w, h); m_pixmap.fill(pal.mid().color()); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return; const int n1 = pSession->tracks().count(); if (n1 < 1) return; QPainter painter(&m_pixmap); // painter.initFrom(this); // painter.setFont(QFrame::font()); const QBrush shade(QColor(0, 0, 0, 60)); // Local contents length (in frames). m_iContentsLength = pSession->sessionEnd(); if (m_iContentsLength > 0) { qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iContentsLength); m_iContentsLength += pNode->frameFromBeat( pNode->beat + 2 * pSession->beatsPerBar()) - pNode->frame; } else { m_iContentsLength += pTimeScale->frameFromPixel( pTracks->trackView()->width()); } const int ch = pTracks->trackView()->contentsHeight(); const int f2 = 1 + (m_iContentsLength / w); const int h1 = (h / n1) - 1; int x2, w2; if (ch > 0) { int y2 = 0; qtractorTrack *pTrack = pSession->tracks().first(); while (pTrack && y2 < h) { const int h2 = 1 + ((h * pTrack->zoomHeight()) / ch); QColor bg(pTrack->background()); if (pTrack->isMute() || (!pTrack->isSolo() && pSession->soloTracks())) bg = bg.darker(); qtractorClip *pClip = pTrack->clips().first(); while (pClip) { x2 = int(pClip->clipStart() / f2); w2 = int(pClip->clipLength() / f2); painter.fillRect(x2, y2, w2, h2, bg); if (pClip->isClipMute()) painter.fillRect(x2, y2, w2, h2, shade); pClip = pClip->next(); } y2 += h2; if (h1 > 1) ++y2; pTrack = pTrack->next(); } } // Draw the location marker lines, if any... qtractorTimeScale::Marker *pMarker = pTimeScale->markers().first(); while (pMarker) { x2 = int(pMarker->frame / f2); painter.setPen(pMarker->color); painter.drawLine(x2, 0, x2, h); pMarker = pMarker->next(); } // Draw the loop-bound lines, if any... if (pSession->isLooping()) { const QBrush shade(QColor(0, 0, 0, 60)); painter.setPen(Qt::darkCyan); x2 = int(pSession->loopStart() / f2); if (x2 < w) { painter.fillRect(QRect(0, 0, x2, h), shade); painter.drawLine(x2, 0, x2, h); } x2 = int(pSession->loopEnd() / f2); if (x2 < w) { painter.fillRect(QRect(x2, 0, w - x2, h), shade); painter.drawLine(x2, 0, x2, h); } } // Don't forget the punch-in/out ones too... if (pSession->isPunching()) { painter.setPen(Qt::darkMagenta); x2 = int(pSession->punchIn() / f2); if (x2 < w) { painter.fillRect(QRect(0, 0, x2, h), shade); painter.drawLine(x2, 0, x2, h); } x2 = int(pSession->punchOut() / f2); if (x2 < w) { painter.fillRect(QRect(x2, 0, w - x2, h), shade); painter.drawLine(x2, 0, x2, h); } } // May trigger an update now. update(); } // Update thumb-position. void qtractorThumbView::updateThumb ( int dx ) { const int w = QFrame::width(); const int h = QFrame::height(); if (w < 1 || h < 1) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return; const int cw = pSession->pixelFromFrame(m_iContentsLength) + 1; int x2 = dx + (w * pTracks->trackView()->contentsX()) / cw; int w2 = (w * pTracks->trackView()->viewport()->width()) / cw; if (w2 < 8) w2 = 8; else if (w2 > w) w2 = w; if (x2 < 0) x2 = 0; else if (x2 > w - w2) x2 = w - w2; m_pRubberBand->setGeometry(x2, 0, w2, h); } // Update playhead-position. void qtractorThumbView::updatePlayHead ( unsigned long iPlayHead ) { const int w = QFrame::width(); const int h = QFrame::height(); if (w < 1 || h < 1) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return; const int f2 = 1 + (m_iContentsLength / w); // Extra: update current playhead position... const int x2 = int(iPlayHead / f2); if (m_iPlayHeadX != x2) { // Override old playhead line... update(QRect(m_iPlayHeadX, 0, 1, h)); // New position is in... m_iPlayHeadX = x2; // And draw it... update(QRect(m_iPlayHeadX, 0, 1, h)); } } // Update view-position. void qtractorThumbView::updateView ( int dx ) { const int w = QFrame::width(); if (w < 1) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return; qtractorTrackView *pTrackView = pTracks->trackView(); if (pTrackView == nullptr) return; const int cw = pSession->pixelFromFrame(m_iContentsLength) + 1; const int cy = pTrackView->contentsY(); int cx = pTrackView->contentsX() + (dx * cw) / w; if (cx < 0) cx = 0; pTrackView->setSyncViewHoldOn(true); pTrackView->setContentsPos(cx, cy); } // Set playhead-position (indirect). void qtractorThumbView::setPlayHeadX ( int iPlayHeadX ) { const int w = QFrame::width(); if (w < 1) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return; qtractorTrackView *pTrackView = pTracks->trackView(); if (pTrackView == nullptr) return; const int f2 = 1 + (m_iContentsLength / w); const unsigned long iPlayHead = f2 * iPlayHeadX; pSession->setPlayHead(iPlayHead); pTrackView->setPlayHeadAutoBackward(iPlayHead); pTrackView->setSyncViewHoldOn(false); } // Session track-line paint method. void qtractorThumbView::paintEvent ( QPaintEvent *pPaintEvent ) { QPainter painter(this); // Render the famous pixmap region... const QRect& rect = pPaintEvent->rect(); painter.drawPixmap(rect, m_pixmap, rect); const int w = QFrame::width(); const int h = QFrame::height(); if (w < 1 || h < 1) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; const int f2 = 1 + (m_iContentsLength / w); int x2; // Draw current edit-bound lines... painter.setPen(Qt::blue); x2 = int(pSession->editHead() / f2); if (x2 >= rect.left() && x2 <= rect.right()) painter.drawLine(x2, 0, x2, h); x2 = int(pSession->editTail() / f2); if (x2 >= rect.left() && x2 <= rect.right()) painter.drawLine(x2, 0, x2, h); x2 = int(pSession->playHeadAutoBackward() / f2); if (x2 >= rect.left() && x2 <= rect.right()) { painter.setPen(QColor(240, 0, 0, 60)); painter.drawLine(x2, 0, x2, h); } // Draw current play-head as well... x2 = m_iPlayHeadX; if (x2 >= rect.left() && x2 <= rect.right()) { painter.setPen(Qt::red); painter.drawLine(x2, 0, x2, h); } // Shade-out what's not in view... if (m_pRubberBand) { const QColor rgba(0, 0, 0, 96); const QRect& rect2 = m_pRubberBand->geometry(); if (rect2.left() > rect.left()) painter.fillRect( rect.left(), rect.top(), rect2.left() - rect.left(), rect.height(), rgba); if (rect2.right() < rect.right() + 1) painter.fillRect( rect2.right() + 1, rect.top(), rect.right() - rect2.right(), rect.height(), rgba); } } // Session track-line paint method. void qtractorThumbView::resizeEvent ( QResizeEvent *pResizeEvent ) { QFrame::resizeEvent(pResizeEvent); updateContents(); updateThumb(); } // Handle selection with mouse. void qtractorThumbView::mousePressEvent ( QMouseEvent *pMouseEvent ) { // Force null state. m_dragState = DragNone; // Only expected behavior with left-button pressed... if (pMouseEvent->button() == Qt::LeftButton) { QFrame::setCursor(QCursor(Qt::PointingHandCursor)); m_posDrag = pMouseEvent->pos(); const QRect& rect = m_pRubberBand->geometry(); if (rect.contains(pMouseEvent->pos())) { m_dragState = DragStart; } else { m_dragState = DragClick; } } else if (pMouseEvent->button() == Qt::MiddleButton) { // Make it change playhead?... if (pMouseEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) setPlayHeadX(pMouseEvent->pos().x()); } QFrame::mousePressEvent(pMouseEvent); } void qtractorThumbView::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { // Only expected behavior with left-button pressed... if (pMouseEvent->buttons() & Qt::LeftButton) { const QPoint& pos = pMouseEvent->pos(); if ((m_dragState == DragStart || m_dragState == DragClick) && (pos - m_posDrag).manhattanLength() > QApplication::startDragDistance()) { m_dragState = DragMove; QFrame::setCursor(QCursor(Qt::SizeHorCursor)); } if (m_dragState == DragMove && rect().contains(pos)) { updateView(pos.x() - m_posDrag.x()); m_posDrag.setX(pos.x()); } } QFrame::mouseMoveEvent(pMouseEvent); } void qtractorThumbView::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { QFrame::mouseReleaseEvent(pMouseEvent); // Only expected behavior with left-button pressed... if (pMouseEvent->button() == Qt::LeftButton) { const QPoint& pos = pMouseEvent->pos(); if (m_dragState == DragMove) updateView(pos.x() - m_posDrag.x()); else { if (m_dragState == DragStart || m_dragState == DragClick) { const QRect& rect = m_pRubberBand->geometry(); m_posDrag.setX(((rect.left() + rect.right()) >> 1)); updateView(pos.x() - m_posDrag.x()); } // Make it change playhead?... if (pMouseEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) setPlayHeadX(pos.x()); } } // Clean up. resetDragState(); } // Reset drag/select state. void qtractorThumbView::resetDragState (void) { // Restore uncommitted thumb position?... if (m_dragState == DragMove) updateThumb(); // Cancel any dragging out there... if (m_dragState != DragNone) QFrame::unsetCursor(); // Force null state. m_dragState = DragNone; // HACK: give focus to track-view... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && pMainForm->tracks()) pMainForm->tracks()->trackView()->setFocus(); } // Keyboard event handler. void qtractorThumbView::keyPressEvent ( QKeyEvent *pKeyEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorThumbView::keyPressEvent(%d)", pKeyEvent->key()); #endif switch (pKeyEvent->key()) { case Qt::Key_Escape: resetDragState(); break; default: QFrame::keyPressEvent(pKeyEvent); break; } } // end of qtractorThumbView.cpp qtractor-1.5.11/src/PaxHeaders/qtractorShortcutForm.ui0000644000000000000000000000013215124701674020070 xustar0030 mtime=1767080892.800263424 30 atime=1767080892.800263424 30 ctime=1767080892.800263424 qtractor-1.5.11/src/qtractorShortcutForm.ui0000644000175000001440000000746315124701674020072 0ustar00rncbcusers rncbc aka Rui Nuno Capela qtractor - An Audio/MIDI multi-track sequencer. Copyright (C) 2005-2025, 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. qtractorShortcutForm 0 0 640 320 Shortcuts 320 0 Shortcut search string (regular expression) true false true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows 16 16 false true false true false 4 Menu/Action Description Keyboard MIDI Controller Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ShortcutSearchComboBox ShortcutTable DialogButtonBox qtractor-1.5.11/src/PaxHeaders/qtractorCurveFile.cpp0000644000000000000000000000013215124701674017462 xustar0030 mtime=1767080892.783263496 30 atime=1767080892.783263496 30 ctime=1767080892.783263496 qtractor-1.5.11/src/qtractorCurveFile.cpp0000644000175000001440000002171715124701674017462 0ustar00rncbcusers// qtractorCurveFile.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorCurveFile.h" #include "qtractorDocument.h" #include "qtractorTimeScale.h" #include "qtractorMidiFile.h" #include "qtractorMidiControl.h" #include "qtractorMessageList.h" #include "qtractorSession.h" #include #include //---------------------------------------------------------------------- // class qtractorCurveFile -- Automation curve file interface impl. // // Curve item list serialization methods. void qtractorCurveFile::load ( QDomElement *pElement ) { clear(); for (QDomNode nChild = pElement->firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert node to element, if any. QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; // Check for child item... if (eChild.tagName() == "filename") m_sFilename = eChild.text(); else if (eChild.tagName() == "current") m_iCurrentIndex = eChild.text().toULong(); else if (eChild.tagName() == "curve-items") { for (QDomNode nItem = eChild.firstChild(); !nItem.isNull(); nItem = nItem.nextSibling()) { // Convert node to element, if any. QDomElement eItem = nItem.toElement(); if (eItem.isNull()) continue; // Check for controller item... if (eItem.tagName() == "curve-item") { Item *pItem = new Item; pItem->name = eItem.attribute("name"); pItem->index = eItem.attribute("index").toULong(); pItem->ctype = qtractorMidiEvent::CONTROLLER; // Default. pItem->mode = modeFromText(eItem.attribute("mode")); pItem->process = false; // Defaults. pItem->capture = false; pItem->locked = false; pItem->logarithmic = false; for (QDomNode nProp = eItem.firstChild(); !nProp.isNull(); nProp = nProp.nextSibling()) { // Convert node to element, if any. QDomElement eProp = nProp.toElement(); if (eProp.isNull()) continue; // Check for property item... if (eProp.tagName() == "type") pItem->ctype = qtractorMidiControl::typeFromText(eProp.text()); if (eProp.tagName() == "channel") pItem->channel = eProp.text().toUShort(); else if (eProp.tagName() == "param") pItem->param = eProp.text().toUShort(); else if (eProp.tagName() == "process") pItem->process = qtractorDocument::boolFromText(eProp.text()); else if (eProp.tagName() == "capture") pItem->capture = qtractorDocument::boolFromText(eProp.text()); else if (eProp.tagName() == "locked") pItem->locked = qtractorDocument::boolFromText(eProp.text()); else if (eProp.tagName() == "logarithmic") pItem->logarithmic = qtractorDocument::boolFromText(eProp.text()); else if (eProp.tagName() == "color") { #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) pItem->color = QColor::fromString(eProp.text()); #else pItem->color = QColor(eProp.text()); #endif } } pItem->subject = nullptr; addItem(pItem); } } } } } void qtractorCurveFile::save ( qtractorDocument *pDocument, QDomElement *pElement, qtractorTimeScale *pTimeScale ) const { if (m_pCurveList == nullptr) return; const unsigned short iSeqs = m_items.count(); if (iSeqs < 1) return; qtractorMidiFile file; if (!file.open(m_sFilename, qtractorMidiFile::Write)) return; const unsigned short iTicksPerBeat = pTimeScale->ticksPerBeat(); unsigned short iSeq = 0; qtractorMidiSequence **ppSeqs = new qtractorMidiSequence * [iSeqs]; for ( ; iSeq < iSeqs; ++iSeq) ppSeqs[iSeq] = new qtractorMidiSequence(QString(), 0, iTicksPerBeat); iSeq = 0; unsigned long iCurrentIndex = 0; QDomElement eItems = pDocument->document()->createElement("curve-items"); QListIterator iter(m_items); while (iter.hasNext()) { Item *pItem = iter.next(); qtractorCurve *pCurve = (pItem->subject)->curve(); if (pCurve && !pCurve->isEmpty()) { qtractorMidiSequence *pSeq = ppSeqs[iSeq]; pCurve->writeMidiSequence(pSeq, pItem->ctype, pItem->channel, pItem->param, pTimeScale); QDomElement eItem = pDocument->document()->createElement("curve-item"); eItem.setAttribute("name", pItem->name); eItem.setAttribute("index", QString::number(pItem->index)); eItem.setAttribute("mode", textFromMode(pItem->mode)); pDocument->saveTextElement("type", qtractorMidiControl::textFromType(pItem->ctype), &eItem); pDocument->saveTextElement("channel", QString::number(pItem->channel), &eItem); pDocument->saveTextElement("param", QString::number(pItem->param), &eItem); pDocument->saveTextElement("mode", textFromMode(pItem->mode), &eItem); pDocument->saveTextElement("process", pDocument->textFromBool(pItem->process), &eItem); pDocument->saveTextElement("capture", pDocument->textFromBool(pItem->capture), &eItem); pDocument->saveTextElement("locked", pDocument->textFromBool(pItem->locked), &eItem); pDocument->saveTextElement("logarithmic", pDocument->textFromBool(pItem->logarithmic), &eItem); pDocument->saveTextElement("color", pItem->color.name(), &eItem); if (m_pCurveList->currentCurve() == pCurve) iCurrentIndex = pItem->index; eItems.appendChild(eItem); ++iSeq; } } pElement->appendChild(eItems); file.writeHeader(1, iSeqs, iTicksPerBeat); file.writeTracks(ppSeqs, iSeqs); file.close(); for (iSeq = 0; iSeq < iSeqs; ++iSeq) delete ppSeqs[iSeq]; delete [] ppSeqs; QString sFilename; if (pDocument->isArchive() || pDocument->isSymLink()) sFilename = pDocument->addFile(m_sFilename); else sFilename = QDir(m_sBaseDir).relativeFilePath(m_sFilename); pDocument->saveTextElement("filename", sFilename, pElement); if (iCurrentIndex > 0) { pDocument->saveTextElement("current", QString::number(iCurrentIndex), pElement); } } void qtractorCurveFile::apply ( qtractorTimeScale *pTimeScale ) { if (m_pCurveList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const QString& sFilename = QDir(m_sBaseDir).absoluteFilePath(m_sFilename); qtractorMidiFile file; if (!file.open(sFilename, qtractorMidiFile::Read)) { const QString& sText = QObject::tr("%1: Automation/curve file not found.") .arg(sFilename); qtractorMessageList::append(sText); return; } // Transient curve-file registry method as far to // avoid duplicates across load/save/record cycles... pSession->acquireFilePath(sFilename); const unsigned short iTicksPerBeat = pTimeScale->ticksPerBeat(); unsigned short iSeq = 0; qtractorCurve *pCurrentCurve = nullptr; QListIterator iter(m_items); while (iter.hasNext()) { Item *pItem = iter.next(); if (pItem->subject) { qtractorCurve *pCurve = (pItem->subject)->curve(); if (pCurve == nullptr) pCurve = new qtractorCurve(m_pCurveList, pItem->subject, pItem->mode); if (m_iCurrentIndex == pItem->index) pCurrentCurve = pCurve; qtractorMidiSequence seq(QString(), pItem->channel, iTicksPerBeat); if (file.readTrack(&seq, iSeq)) { pCurve->readMidiSequence(&seq, pItem->ctype, pItem->channel, pItem->param, pTimeScale); } pCurve->setProcess(pItem->process); pCurve->setCapture(pItem->capture); pCurve->setLocked(pItem->locked); pCurve->setLogarithmic(pItem->logarithmic); pCurve->setColor(pItem->color); } ++iSeq; } file.close(); if (pCurrentCurve) m_pCurveList->setCurrentCurve(pCurrentCurve); clear(); } // Text/curve-mode converters... qtractorCurve::Mode qtractorCurveFile::modeFromText ( const QString& sText ) { qtractorCurve::Mode mode = qtractorCurve::Hold; if (sText == "Spline") mode = qtractorCurve::Spline; else if (sText == "Linear") mode = qtractorCurve::Linear; return mode; } QString qtractorCurveFile::textFromMode ( qtractorCurve::Mode mode ) { QString sText; switch (mode) { case qtractorCurve::Spline: sText = "Spline"; break; case qtractorCurve::Linear: sText = "Linear"; break; case qtractorCurve::Hold: default: sText = "Hold"; break; } return sText; } // end of qtractorCurveFile.cpp qtractor-1.5.11/src/PaxHeaders/qtractorAudioMadFile.cpp0000644000000000000000000000013215124701674020061 xustar0030 mtime=1767080892.779263512 30 atime=1767080892.779263512 30 ctime=1767080892.779263512 qtractor-1.5.11/src/qtractorAudioMadFile.cpp0000644000175000001440000003351615124701674020061 0ustar00rncbcusers// qtractorAudioMadFile.cpp // /**************************************************************************** Copyright (C) 2005-2019, 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 "qtractorAbout.h" #include "qtractorAudioMadFile.h" #include // Frame list mutex. QMutex qtractorAudioMadFile::g_mutex; //---------------------------------------------------------------------- // class qtractorAudioMadFile -- Buffered audio file implementation. // // Constructor. qtractorAudioMadFile::qtractorAudioMadFile ( unsigned int iBufferSize ) { // Initialize state variables. m_iMode = qtractorAudioMadFile::None; m_pFile = nullptr; m_iBitRate = 0; m_iChannels = 0; m_iSampleRate = 0; m_iFramesEst = 0; m_bEndOfStream = false; // Input buffer stuff. m_iInputBufferSize = iBufferSize; m_pInputBuffer = nullptr; // Output ring-buffer stuff. m_iRingBufferSize = 0; m_iRingBufferMask = 0; m_iRingBufferRead = 0; m_iRingBufferWrite = 0; m_ppRingBuffer = nullptr; // Frame mapping for sample-accurate seeking. m_iSeekOffset = 0; } // Destructor. qtractorAudioMadFile::~qtractorAudioMadFile (void) { close(); } // Open method. bool qtractorAudioMadFile::open ( const QString& sFilename, int iMode ) { #ifdef DEBUG_0 qDebug("qtractorAudioMadFile::open(\"%s\", %d)", sFilename.toUtf8().constData(), iMode); #endif close(); // Whether for Read or Write... sorry only read is allowed. if (iMode != qtractorAudioMadFile::Read) return false; const QByteArray aFilename = sFilename.toUtf8(); m_pFile = ::fopen(aFilename.constData(), "rb"); if (m_pFile == nullptr) return false; // Create the decoded frame list. m_pFrameList = createFrameList(sFilename); if (m_pFrameList == nullptr) { close(); return false; } // We've been here before we'll the total decoded length of the file... unsigned long iFramesEst = 0; g_mutex.lock(); if (m_pFrameList->count() > 0) iFramesEst = m_pFrameList->last().iOutputOffset; g_mutex.unlock(); #ifdef CONFIG_LIBMAD mad_stream_init(&m_madStream); mad_frame_init(&m_madFrame); mad_synth_init(&m_madSynth); #endif // CONFIG_LIBMAD struct stat st; const int fdFile = fileno(m_pFile); if (::fstat(fdFile, &st) < 0 || st.st_size == 0) { close(); return false; } // Read the very first bunch of raw-data... if (!input()) { close(); return false; } #ifdef CONFIG_LIBMAD if (mad_header_decode(&m_madFrame.header, &m_madStream) < 0) { if (m_madStream.error == MAD_ERROR_BUFLEN) { close(); return false; } if (!MAD_RECOVERABLE(m_madStream.error)) { close(); return false; } } #endif // CONFIG_LIBMAD // Do the very first frame decoding... m_bEndOfStream = !decode(); // Get a rough estimate of the total decoded length of the file... if (iFramesEst < 1 && m_iBitRate > 0) { iFramesEst = (m_bEndOfStream ? m_curr.iOutputOffset : (unsigned long) ((float) m_iSampleRate * st.st_size * 8.0f / (float) m_iBitRate)); } m_iFramesEst = iFramesEst; #ifdef DEBUG_0 qDebug("qtractorAudioMadFile::open(\"%s\", %d) bit_rate=%u farmes_est=%lu", sFilename.toUtf8().constData(), iMode, m_iBitRate, m_iFramesEst); #endif // Set open mode (deterministically). m_iMode = iMode; return true; } // Local input method. bool qtractorAudioMadFile::input (void) { #ifdef DEBUG_0 qDebug("qtractorAudioMadFile::input()"); #endif #ifdef CONFIG_LIBMAD // Can't go on if EOF. if (feof(m_pFile)) return false; // Allocate input buffer if not already. if (m_pInputBuffer == nullptr) { unsigned int iBufferSize = (4096 << 1); while (iBufferSize < m_iInputBufferSize) iBufferSize <<= 1; m_iInputBufferSize = iBufferSize; m_pInputBuffer = new unsigned char [iBufferSize + MAD_BUFFER_GUARD]; // Decoder mapping initialization. m_curr.iInputOffset = 0; m_curr.iOutputOffset = 0; m_curr.iDecodeCount = 0; } unsigned long iRemaining; unsigned char *pReadStart; unsigned long iReadSize; if (m_madStream.next_frame) { iRemaining = m_madStream.bufend - m_madStream.next_frame; ::memmove(m_pInputBuffer, m_madStream.next_frame, iRemaining); pReadStart = m_pInputBuffer + iRemaining; iReadSize = m_iInputBufferSize - iRemaining; } else { iRemaining = 0; pReadStart = m_pInputBuffer; iReadSize = m_iInputBufferSize; } long iRead = ::fread(pReadStart, 1, iReadSize, m_pFile); if (iRead > 0) { // Update the input offset, as for next time... m_curr.iInputOffset += iRead; // Time to add some frame mapping, on each 3rd iteration... g_mutex.lock(); if ((++m_curr.iDecodeCount % 3) == 0 && (m_pFrameList->count() < 1 || m_pFrameList->last().iOutputOffset < m_curr.iOutputOffset)) { m_pFrameList->append(FrameNode( m_curr.iInputOffset - iRemaining, m_curr.iOutputOffset, m_curr.iDecodeCount)); } g_mutex.unlock(); // Add some decode buffer guard... if (iRead < (int) iReadSize) { ::memset(pReadStart + iRead, 0, MAD_BUFFER_GUARD); iRead += MAD_BUFFER_GUARD; } mad_stream_buffer(&m_madStream, m_pInputBuffer, iRead + iRemaining); } return (iRead > 0); #else // CONFIG_LIBMAD return false; #endif } // Local decode method. bool qtractorAudioMadFile::decode (void) { #ifdef DEBUG_0 qDebug("qtractorAudioMadFile::decode()"); #endif #ifdef CONFIG_LIBMAD bool bError = (mad_frame_decode(&m_madFrame, &m_madStream) < 0); while (bError && (m_madStream.error == MAD_ERROR_BUFLEN || MAD_RECOVERABLE(m_madStream.error))) { if (!input()) return false; bError = (mad_frame_decode(&m_madFrame, &m_madStream) < 0); } #ifdef DEBUG_0 if (bError) { qDebug("qtractorAudioMadFile::decode()" " ERROR[%lu]: madStream.error=%d (0x%04x)", m_curr.iOutputOffset, m_madStream.error, m_madStream.error); } #endif if (bError) return MAD_RECOVERABLE(m_madStream.error); mad_synth_frame(&m_madSynth, &m_madFrame); const unsigned int iFrames = m_madSynth.pcm.length; if (m_ppRingBuffer == nullptr) { // Set initial stream parameters. m_iBitRate = m_madFrame.header.bitrate; m_iChannels = m_madSynth.pcm.channels; m_iSampleRate = m_madSynth.pcm.samplerate; // Create/allocate internal output ring-buffer. m_iRingBufferSize = (4096 << 1); while (m_iRingBufferSize < m_iInputBufferSize) m_iRingBufferSize <<= 1; m_iRingBufferMask = (m_iRingBufferSize - 1); // Allocate actual buffer stuff... m_ppRingBuffer = new float* [m_iChannels]; for (unsigned short i = 0; i < m_iChannels; ++i) m_ppRingBuffer[i] = new float [m_iRingBufferSize]; // Reset ring-buffer pointers. m_iRingBufferRead = 0; m_iRingBufferWrite = 0; } const float fScale = 1.0f / (float) (1L << MAD_F_FRACBITS); for (unsigned int n = 0; n < iFrames; ++n) { if (m_curr.iOutputOffset >= m_iSeekOffset) { for (unsigned short i = 0; i < m_iChannels; ++i) { const int iSample = bError ? 0 : *(m_madSynth.pcm.samples[i] + n); m_ppRingBuffer[i][m_iRingBufferWrite] = fScale * (float) iSample; } ++m_iRingBufferWrite &= m_iRingBufferMask; } #ifdef DEBUG_0 else if (n == 0) { qDebug("qtractorAudioMadFile::decode(%lu) i=%lu o=%lu c=%u", m_iSeekOffset, m_curr.iInputOffset, m_curr.iOutputOffset, m_curr.iDecodeCount); } #endif ++m_curr.iOutputOffset; } return true; #else // CONFIG_LIBMAD return false; #endif } // Read method. int qtractorAudioMadFile::read ( float **ppFrames, unsigned int iFrames ) { #ifdef DEBUG_0 qDebug("qtractorAudioMadFile::read(%p, %d)", ppFrames, iFrames); #endif unsigned int nread = 0; if (m_ppRingBuffer) { if (iFrames > (m_iRingBufferSize >> 1)) iFrames = (m_iRingBufferSize >> 1); while ((nread = readable()) < iFrames && !m_bEndOfStream) { if (!decode()) { g_mutex.lock(); if (m_pFrameList->count() < 1 || m_pFrameList->last().iOutputOffset < m_curr.iOutputOffset) m_pFrameList->append(m_curr); g_mutex.unlock(); m_bEndOfStream = true; } } if (nread > iFrames) nread = iFrames; // Move the data around... unsigned int r = m_iRingBufferRead; unsigned int n1, n2; if (r + nread > m_iRingBufferSize) { n1 = (m_iRingBufferSize - r); n2 = (r + nread) & m_iRingBufferMask; } else { n1 = nread; n2 = 0; } for (unsigned short i = 0; i < m_iChannels; ++i) { ::memcpy(ppFrames[i], (float *)(m_ppRingBuffer[i] + r), n1 * sizeof(float)); if (n2 > 0) { ::memcpy((float *)(ppFrames[i] + n1), m_ppRingBuffer[i], n2 * sizeof(float)); } } m_iRingBufferRead = (r + nread) & m_iRingBufferMask; m_iSeekOffset += nread; } return nread; } // Write method. int qtractorAudioMadFile::write ( float **/*ppFrames*/, unsigned int /*iFrames*/ ) { return 0; } // Seek method. bool qtractorAudioMadFile::seek ( unsigned long iOffset ) { #ifdef DEBUG_0 qDebug("qtractorAudioMadFile::seek(%lu)", iOffset); #endif // Avoid unprecise seeks... if (iOffset == m_iSeekOffset) return true; // This is the target situation... m_iSeekOffset = iOffset; // Are qe seeking backward or forward // from last known decoded position? g_mutex.lock(); if (m_pFrameList->count() > 0 && m_pFrameList->last().iOutputOffset > iOffset) { // Assume the worst case (seek to very beginning...) m_curr.iInputOffset = 0; m_curr.iOutputOffset = 0; m_curr.iDecodeCount = 0; // Find the previous mapped 3rd frame that fits location... QListIterator iter(*m_pFrameList); iter.toBack(); while (iter.hasPrevious()) { if (iter.previous().iOutputOffset < iOffset) { if (iter.hasPrevious()) m_curr = iter.previous(); break; } } g_mutex.unlock(); #ifdef DEBUG_0 qDebug("qtractorAudioMadFile::seek(%lu) i=%lu o=%lu c=%u", iOffset, m_curr.iInputOffset, m_curr.iOutputOffset, m_curr.iDecodeCount); #endif // Rewind file position... if (::fseek(m_pFile, m_curr.iInputOffset, SEEK_SET)) return false; #ifdef CONFIG_LIBMAD // Release MAD structs... mad_synth_finish(&m_madSynth); mad_frame_finish(&m_madFrame); mad_stream_finish(&m_madStream); // Reset MAD structs... mad_stream_init(&m_madStream); mad_frame_init(&m_madFrame); mad_synth_init(&m_madSynth); #endif // CONFIG_LIBMAD // Reread first seeked input bunch... if (!input()) return false; } else g_mutex.unlock(); // Reset ring-buffer pointers. m_iRingBufferRead = 0; m_iRingBufferWrite = 0; m_bEndOfStream = false; // Now loop until we find the target offset... while (m_curr.iOutputOffset < m_iSeekOffset && !m_bEndOfStream) m_bEndOfStream = !decode(); return !m_bEndOfStream; } // Close method. void qtractorAudioMadFile::close (void) { #ifdef DEBUG_0 qDebug("qtractorAudioMadFile::close()"); #endif // Free allocated buffers, if any. if (m_ppRingBuffer) { for (unsigned short i = 0; i < m_iChannels; ++i) delete [] m_ppRingBuffer[i]; delete [] m_ppRingBuffer; m_ppRingBuffer = nullptr; } if (m_pInputBuffer) { delete [] m_pInputBuffer; m_pInputBuffer = nullptr; } if (m_pFile) { #ifdef CONFIG_LIBMAD mad_synth_finish(&m_madSynth); mad_frame_finish(&m_madFrame); mad_stream_finish(&m_madStream); #endif // CONFIG_LIBMAD ::fclose(m_pFile); m_pFile = nullptr; } // Frame lists are never destroyed here // (they're cached for whole life-time of the program). m_pFrameList = nullptr; // Reset all other state relevant variables. m_bEndOfStream = false; m_iFramesEst = 0; m_iSampleRate = 0; m_iChannels = 0; m_iBitRate = 0; m_iMode = qtractorAudioMadFile::None; } // Open mode accessor. int qtractorAudioMadFile::mode (void) const { return m_iMode; } // Open channel(s) accessor. unsigned short qtractorAudioMadFile::channels (void) const { return m_iChannels; } // Estimated number of frames specialty (aprox. 8secs). unsigned long qtractorAudioMadFile::frames (void) const { return m_iFramesEst; } // Sample rate specialty. unsigned int qtractorAudioMadFile::sampleRate (void) const { return m_iSampleRate; } // Internal ring-buffer helper methods. unsigned int qtractorAudioMadFile::readable (void) const { unsigned int w = m_iRingBufferWrite; unsigned int r = m_iRingBufferRead; if (w > r) { return (w - r); } else { return (w - r + m_iRingBufferSize) & m_iRingBufferMask; } } unsigned int qtractorAudioMadFile::writable (void) const { unsigned int w = m_iRingBufferWrite; unsigned int r = m_iRingBufferRead; if (w > r){ return ((r - w + m_iRingBufferSize) & m_iRingBufferMask) - 1; } else if (r > w) { return (r - w) - 1; } else { return m_iRingBufferSize - 1; } } // Frame list factory method. qtractorAudioMadFile::FrameList *qtractorAudioMadFile::createFrameList ( const QString& sFilename ) { // Frame list hash repository // (declared here for proper cleanup). class FrameListFactory : public QHash { public: // Destructor. ~FrameListFactory() { QMutableHashIterator iter(*this); while (iter.hasNext()) { FrameList *pFrameList = iter.next().value(); iter.remove(); delete pFrameList; } } }; // Do the factory thing here... static FrameListFactory s_lists; FrameList *pFrameList = s_lists.value(sFilename, nullptr); if (pFrameList == nullptr) { pFrameList = new FrameList(); s_lists.insert(sFilename, pFrameList); } return pFrameList; } // end of qtractorAudioMadFile.cpp qtractor-1.5.11/src/PaxHeaders/qtractorLv2Gtk2Plugin.h0000644000000000000000000000013215124701674017615 xustar0030 mtime=1767080892.786263483 30 atime=1767080892.786263483 30 ctime=1767080892.786263483 qtractor-1.5.11/src/qtractorLv2Gtk2Plugin.h0000644000175000001440000000257515124701674017616 0ustar00rncbcusers// qtractorLv2Gtk2Plugin.h // /**************************************************************************** Copyright (C) 2005-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 __qtractorLv2Gtk2Plugin_h #define __qtractorLv2Gtk2Plugin_h #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_UI #ifdef CONFIG_LV2_UI_GTK2 namespace qtractorLv2Gtk2Plugin { // Module entry/exit points. void init_main(); void exit_main(); } // namespace qtractorLv2Gtk2Plugin #endif // CONFIG_LV2_UI_GTK2 #endif // CONFIG_LV2_UI #endif // CONFIG_LV2 #endif // __qtractorLv2Gtk2Plugin_h // end of qtractorLv2Gtk2Plugin.h qtractor-1.5.11/src/PaxHeaders/qtractorTracks.h0000644000000000000000000000013215124701674016472 xustar0030 mtime=1767080892.804263407 30 atime=1767080892.804263407 30 ctime=1767080892.804263407 qtractor-1.5.11/src/qtractorTracks.h0000644000175000001440000002047115124701674016466 0ustar00rncbcusers// qtractorTracks.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorTracks_h #define __qtractorTracks_h #include // Forward declarations. class qtractorTrackList; class qtractorTrackTime; class qtractorTrackView; class qtractorTrack; class qtractorClip; class qtractorClipCommand; class qtractorClipRangeCommand; class qtractorClipToolCommand; class qtractorImportTrackCommand; class qtractorMidiToolsForm; class qtractorMidiManager; //---------------------------------------------------------------------------- // qtractorTracks -- The main session track listview widget. class qtractorTracks : public QSplitter { Q_OBJECT public: // Constructor. qtractorTracks(QWidget *pParent); // Destructor. ~qtractorTracks(); // Child widgets accessors. qtractorTrackList *trackList() const; qtractorTrackTime *trackTime() const; qtractorTrackView *trackView() const; // Update/sync from session tracks. void updateContents(bool bRefresh = false); // Primordial track management methods. qtractorTrack *currentTrack() const; bool addTrack(); bool removeTrack(qtractorTrack *pTrack = nullptr); bool editTrack(qtractorTrack *pTrack = nullptr); bool copyTrack(qtractorTrack *pTrack = nullptr); // Add new tracks from audio/MIDI file(s)... bool addTracks(const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset = 0, unsigned long iClipLength = 0, qtractorTrack *pAfterTrack = nullptr); bool addAudioTracks(const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset = 0, unsigned long iClipLength = 0, qtractorTrack *pAfterTrack = nullptr); bool addMidiTracks(const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset = 0, unsigned long iClipLength = 0, qtractorTrack *pAfterTrack = nullptr); bool addMidiTrackChannel(const QString& sPath, int iTrackChannel, unsigned long iClipStart, qtractorTrack *pAfterTrack = nullptr); // Import new tracks from audio/MIDI file(s)... bool importTracks(const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset = 0, unsigned long iClipLength = 0, qtractorTrack *pAfterTrack = nullptr, qtractorImportTrackCommand *pImportTrackCommand = nullptr); bool importAudioTracks(const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset = 0, unsigned long iClipLength = 0, qtractorTrack *pAfterTrack = nullptr, qtractorImportTrackCommand *pImportTrackCommand = nullptr); bool importMidiTracks(const QStringList& files, unsigned long iClipStart, unsigned long iClipOffset = 0, unsigned long iClipLength = 0, qtractorTrack *pAfterTrack = nullptr, qtractorImportTrackCommand *pImportTrackCommand = nullptr); // Track-list active maintenance update. void updateTrack(qtractorTrack *pTrack = nullptr); // MIDI track/bus/channel alias active maintenance method. void updateMidiTrack(qtractorTrack *pMidiTrack); // MIDI track meters maintenance method. void updateMidiTrackItem(qtractorMidiManager *pMidiManager); // Primordial clip management methods. void setCurrentClip(qtractorClip *pClip); qtractorClip *currentClip() const; bool newClip(); bool editClip(qtractorClip *pClip = nullptr); bool muteClip(qtractorClip *pClip = nullptr); bool unlinkClip(qtractorClip *pClip = nullptr); bool splitClip(qtractorClip *pClip = nullptr); bool normalizeClip(qtractorClip *pClip = nullptr); bool rangeClip(qtractorClip *pClip = nullptr); bool loopClip(qtractorClip *pClip = nullptr); bool tempoClip(qtractorClip *pClip = nullptr); bool crossFadeClip(qtractorClip *pClip = nullptr); bool executeClipTool(int iTool, qtractorClip *pClip = nullptr); bool importClips(QStringList files, unsigned long iClipStart = 0); bool exportClips(); bool mergeClips(); // Whether there's anything currently selected. bool isSelected() const; // Whether there's any clip currently selected. bool isClipSelected() const; // Whether there's any curve/automation currently selected. bool isCurveSelected() const; // Whether there's a single track selection. qtractorTrack *singleTrackSelected(); // Retrieve actual clip selection range. void clipSelectedRange( unsigned long& iSelectStart, unsigned long& iSelectEnd) const; // Clipboard methods. void cutClipboard(); void copyClipboard(); void pasteClipboard(); // Special paste/repeat prompt. void pasteRepeatClipboard(); // Delete selection method. void deleteSelect(); // Split selection method. void splitSelect(); // Selection methods. void selectEditRange(bool bReset = false); void selectCurrentTrack(bool bReset = false); void selectCurrentTrackRange(bool bReset = false); void selectAll(); void selectNone(); void selectInvert(); // Insertion and removal methods. bool insertEditRange(qtractorTrack *pTrack = nullptr); bool removeEditRange(qtractorTrack *pTrack = nullptr); // Simple main-form redirectors. void selectionChangeNotify(); void contentsChangeNotify(); void dirtyChangeNotify(); // Overall selection clear/reset. void clearSelect(bool bReset = false); // Overall selection update. void updateSelect(); // Overall contents reset. void clear(); // Zoom (view) modes. enum { ZoomNone = 0, ZoomHorizontal = 1, ZoomVertical = 2, ZoomAll = 3 }; void setZoomMode(int iZoomMode); int zoomMode() const; // Zoom view actuators. void zoomIn(); void zoomOut(); void zoomReset(); // Track-list update (current track only). void updateTrackList(qtractorTrack *pTrack = nullptr); // Update/sync recording tracks. void updateContentsRecord(); protected: // Zoom factor constants. enum { ZoomMin = 10, ZoomBase = 100, ZoomMax = 1000, ZoomStep = 10 }; // Zoom step evaluator. int zoomStep() const; // Common zoom factor settlers. void horizontalZoomStep(int iZoomStep); void verticalZoomStep(int iZoomStep); // Zoom centering context. struct ZoomCenter { int x, y, ch; unsigned long frame; }; // Zoom centering prepare and post methods. void zoomCenterPre(ZoomCenter& zc) const; void zoomCenterPost(const ZoomCenter& zc); // Multi-clip command builders. bool normalizeClipCommand( qtractorClipCommand *pClipCommand, qtractorClip *pClip); bool addClipToolCommand( qtractorClipToolCommand *pClipToolCommand, qtractorClip *pClip, qtractorMidiToolsForm *pMidiToolsForm); // Common clip-export/merge methods. bool mergeExportClips(qtractorClipCommand *pClipCommand); // Specialized clip-export/merge methods. bool mergeExportAudioClips(qtractorClipCommand *pClipCommand); bool mergeExportMidiClips(qtractorClipCommand *pClipCommand); bool rangeClipEx(qtractorClip *pClip, bool bLoopSet); // Insertion and removal methods (track). int insertEditRangeTrack( qtractorClipRangeCommand *pClipRangeCommand, qtractorTrack *pTrack, unsigned long iInsertStart, unsigned long iInsertEnd, unsigned int iInsertOptions) const; int removeEditRangeTrack( qtractorClipRangeCommand *pClipRangeCommand, qtractorTrack *pTrack, unsigned long iRemoveStart, unsigned long iRemoveEnd, unsigned int iRemoveOptions) const; public slots: // Track-view update (obviously a slot). void updateTrackView(); protected slots: // Zoom view slots. void horizontalZoomInSlot(); void horizontalZoomOutSlot(); void verticalZoomInSlot(); void verticalZoomOutSlot(); void viewZoomResetSlot(); private: // The main child widgets. qtractorTrackList *m_pTrackList; qtractorTrackTime *m_pTrackTime; qtractorTrackView *m_pTrackView; // Zoom mode flag. int m_iZoomMode; }; #endif // __qtractorTracks_h // end of qtractorTracks.h qtractor-1.5.11/src/PaxHeaders/qtractorAudioPeak.cpp0000644000000000000000000000013215124701674017440 xustar0030 mtime=1767080892.779263512 30 atime=1767080892.779263512 30 ctime=1767080892.779263512 qtractor-1.5.11/src/qtractorAudioPeak.cpp0000644000175000001440000006702315124701674017440 0ustar00rncbcusers// qtractorAudioPeak.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorAudioPeak.h" #include "qtractorAudioFile.h" #include "qtractorAudioEngine.h" #include "qtractorSession.h" #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) #define birthTime created #endif // Audio file buffer size in frames per channel. static const unsigned int c_iAudioFrames = (32 * 1024); // Peak file buffer size in frames per channel. static const unsigned int c_iPeakFrames = (8 * 1024); // Default peak period as a digest representation in frames per channel. static const unsigned short c_iPeakPeriod = 1024; // Default peak filename extension. static const QString c_sPeakFileExt = ".peak"; //---------------------------------------------------------------------- // class qtractorAudioPeakThread -- Audio Peak file thread. // class qtractorAudioPeakThread : public QThread { public: // Constructor. qtractorAudioPeakThread(unsigned int iSyncSize = 128); // Destructor. ~qtractorAudioPeakThread(); // Thread run state accessors. void setRunState(bool bRunState); bool runState() const; // Wake from executive wait condition. void sync(qtractorAudioPeakFile *pPeakFile = nullptr); protected: // The main thread executive. void run(); // Actual peak file creation methods. // (this is just about to be used internally) bool openPeakFile(); bool writePeakFile(); void closePeakFile(); void notifyPeakEvent() const; private: // The peak file queue instance reference. unsigned int m_iSyncSize; unsigned int m_iSyncMask; qtractorAudioPeakFile **m_ppSyncItems; volatile unsigned int m_iSyncRead; volatile unsigned int m_iSyncWrite; // Whether the thread is logically running. volatile bool m_bRunState; // Thread synchronization objects. QMutex m_mutex; QWaitCondition m_cond; // Current audio peak file instance. qtractorAudioPeakFile *m_pPeakFile; // Current audio file instance. qtractorAudioFile *m_pAudioFile; // Current audio file buffer. float **m_ppAudioFrames; }; // Constructor. qtractorAudioPeakThread::qtractorAudioPeakThread ( unsigned int iSyncSize ) { m_iSyncSize = (64 << 1); while (m_iSyncSize < iSyncSize) m_iSyncSize <<= 1; m_iSyncMask = (m_iSyncSize - 1); m_ppSyncItems = new qtractorAudioPeakFile * [m_iSyncSize]; m_iSyncRead = 0; m_iSyncWrite = 0; ::memset(m_ppSyncItems, 0, m_iSyncSize * sizeof(qtractorAudioPeakFile *)); m_bRunState = false; m_pPeakFile = nullptr; m_pAudioFile = nullptr; m_ppAudioFrames = nullptr; } // Destructor. qtractorAudioPeakThread::~qtractorAudioPeakThread (void) { delete [] m_ppSyncItems; } // Run state accessor. void qtractorAudioPeakThread::setRunState ( bool bRunState ) { // QMutexLocker locker(&m_mutex); m_bRunState = bRunState; } bool qtractorAudioPeakThread::runState (void) const { return m_bRunState; } // Wake from executive wait condition. void qtractorAudioPeakThread::sync ( qtractorAudioPeakFile *pPeakFile ) { if (pPeakFile == nullptr) { unsigned int r = m_iSyncRead; unsigned int w = m_iSyncWrite; while (r != w) { qtractorAudioPeakFile *pSyncItem = m_ppSyncItems[r]; if (pSyncItem) pSyncItem->setWaitSync(false); ++r &= m_iSyncMask; w = m_iSyncWrite; } // m_iSyncRead = r; } else { // !pPeakFile->isWaitSync() unsigned int n; unsigned int r = m_iSyncRead; unsigned int w = m_iSyncWrite; if (w > r) { n = ((r - w + m_iSyncSize) & m_iSyncMask) - 1; } else if (r > w) { n = (r - w) - 1; } else { n = m_iSyncSize - 1; } if (n > 0) { pPeakFile->setWaitSync(true); m_ppSyncItems[w] = pPeakFile; m_iSyncWrite = (w + 1) & m_iSyncMask; } } if (m_mutex.tryLock()) { m_cond.wakeAll(); m_mutex.unlock(); } #ifdef CONFIG_DEBUG_0 else qDebug("qtractorAudioPeakThread[%p]::sync(): tryLock() failed.", this); #endif } // The main thread executive cycle. void qtractorAudioPeakThread::run (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioPeakThread[%p]::run(): started...", this); #endif m_mutex.lock(); m_bRunState = true; while (m_bRunState) { // Do whatever we must, then wait for more... unsigned int r = m_iSyncRead; unsigned int w = m_iSyncWrite; while (m_bRunState && r != w) { m_pPeakFile = m_ppSyncItems[r]; if (m_pPeakFile && m_pPeakFile->isWaitSync()) { if (openPeakFile()) { // Go ahead with the whole bunch... while (writePeakFile()); // We're done. closePeakFile(); } m_pPeakFile->setWaitSync(false); m_pPeakFile = nullptr; } m_ppSyncItems[r] = nullptr; ++r &= m_iSyncMask; w = m_iSyncWrite; } m_iSyncRead = r; // Are we still in the game? if (m_bRunState) { // Send notification event, anyway... notifyPeakEvent(); // Wait for sync... m_cond.wait(&m_mutex); } } m_mutex.unlock(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioPeakThread[%p]::run(): stopped.\n", this); #endif } // Open the peak file for create. bool qtractorAudioPeakThread::openPeakFile (void) { m_pAudioFile = qtractorAudioFileFactory::createAudioFile(m_pPeakFile->filename()); if (m_pAudioFile == nullptr) return false; if (!m_pAudioFile->open(m_pPeakFile->filename())) { delete m_pAudioFile; m_pAudioFile = nullptr; return false; } const unsigned short iChannels = m_pAudioFile->channels(); const unsigned int iSampleRate = m_pAudioFile->sampleRate(); if (!m_pPeakFile->openWrite(iChannels, iSampleRate)) { delete m_pAudioFile; m_pAudioFile = nullptr; return false; } #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioPeakThread::openPeakFile(%p)", m_pPeakFile); #endif // Allocate audio file frame buffer // and peak period accumulators. m_ppAudioFrames = new float* [iChannels]; for (unsigned short i = 0; i < iChannels; ++i) m_ppAudioFrames[i] = new float [c_iAudioFrames]; // Make sure audio file decoder makes no head-start... m_pAudioFile->seek(0); return true; } // Create the peak file chunk. bool qtractorAudioPeakThread::writePeakFile (void) { if (!m_bRunState) return false; if (m_ppAudioFrames == nullptr) return false; #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioPeakThread::writePeakFile(%p)", m_pPeakFile); #endif // Read another bunch of frames from the physical audio file... int nread = m_pAudioFile->read(m_ppAudioFrames, c_iAudioFrames); if (nread > 0) nread = m_pPeakFile->write(m_ppAudioFrames, nread); return (nread > 0); } // Close the (hopefully) created peak file. void qtractorAudioPeakThread::closePeakFile (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioPeakThread::closePeakFile(%p)", m_pPeakFile); #endif // Always force target file close. m_pPeakFile->closeWrite(); // Get rid of physical used stuff. if (m_ppAudioFrames) { const unsigned short iChannels = m_pAudioFile->channels(); for (unsigned short k = 0; k < iChannels; ++k) delete [] m_ppAudioFrames[k]; delete [] m_ppAudioFrames; m_ppAudioFrames = nullptr; } // Finally the source file too. if (m_pAudioFile) { delete m_pAudioFile; m_pAudioFile = nullptr; } // Send notification event, someway... notifyPeakEvent(); } // Send notification event, someway... void qtractorAudioPeakThread::notifyPeakEvent (void) const { if (!m_bRunState) return; qtractorAudioPeakFactory *pPeakFactory = qtractorAudioPeakFactory::getInstance(); if (pPeakFactory) pPeakFactory->notifyPeakEvent(); } //---------------------------------------------------------------------- // class qtractorAudioPeakFile -- Audio peak file (ref'counted) // // Constructor. qtractorAudioPeakFile::qtractorAudioPeakFile ( const QString& sFilename, float fTimeStretch ) { // Initialize instance variables. m_sFilename = sFilename; m_fTimeStretch = fTimeStretch; m_openMode = None; m_peakHeader.period = 0; m_peakHeader.channels = 0; m_pBuffer = nullptr; m_iBuffSize = 0; m_iBuffLength = 0; m_iBuffOffset = 0; m_bWaitSync = false; m_iRefCount = 0; m_pWriter = nullptr; // Set (unique) peak filename... QDir dir; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) dir.setPath(pSession->sessionDir()); const QFileInfo fileInfo(sFilename); const QString& sPeakFilePrefix = QFileInfo(dir, fileInfo.fileName()).filePath(); const QString& sPeakName = peakName(sFilename, fTimeStretch); const QFileInfo peakInfo(sPeakFilePrefix + '_' + QString::number(qHash(sPeakName), 16) + c_sPeakFileExt); m_peakFile.setFileName(peakInfo.absoluteFilePath()); } // Default destructor. qtractorAudioPeakFile::~qtractorAudioPeakFile (void) { cleanup(); } // Open an existing peak file cache. bool qtractorAudioPeakFile::openRead (void) { // If it's already open, just tell the news. if (m_openMode != None) return true; // Are we still waiting for its creation? if (m_bWaitSync) return false; // Need some preliminary file information... QFileInfo fileInfo(m_sFilename); QFileInfo peakInfo(m_peakFile.fileName()); // Have we a peak file up-to-date, // or must the peak file be (re)created? if (!peakInfo.exists() || peakInfo.birthTime() < fileInfo.birthTime()) { // || peakInfo.lastModified() < fileInfo.lastModified()) { qtractorAudioPeakFactory *pPeakFactory = qtractorAudioPeakFactory::getInstance(); if (pPeakFactory) pPeakFactory->sync(this); // Think again... return false; } // Make things critical... QMutexLocker locker(&m_mutex); // Just open and go ahead with first bunch... if (!m_peakFile.open(QIODevice::ReadOnly)) return false; if (m_peakFile.read((char *) &m_peakHeader, sizeof(Header)) != qint64(sizeof(Header))) { m_peakFile.close(); return false; } // Set open mode... m_openMode = Read; #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioPeakFile[%p]::openRead() ---", this); qDebug("name = %s", m_peakFile.fileName().toUtf8().constData()); qDebug("filename = %s", m_sFilename.toUtf8().constData()); qDebug("timeStretch = %g", m_fTimeStretch); qDebug("header = %lu", sizeof(Header)); qDebug("frame = %lu", sizeof(Frame)); qDebug("period = %d", m_peakHeader.period); qDebug("channels = %d", m_peakHeader.channels); qDebug("---"); #endif // Its a certain success... return true; } // Free all attended resources for this peak file. void qtractorAudioPeakFile::closeRead (void) { // Make things critical... QMutexLocker locker(&m_mutex); // Close file. if (m_openMode == Read) { m_peakFile.close(); m_openMode = None; } if (m_pBuffer) delete [] m_pBuffer; m_pBuffer = nullptr; m_iBuffSize = 0; m_iBuffLength = 0; m_iBuffOffset = 0; } // Audio properties accessors. const QString& qtractorAudioPeakFile::filename (void) const { return m_sFilename; } float qtractorAudioPeakFile::timeStretch (void) const { return m_fTimeStretch; } QString qtractorAudioPeakFile::peakName (void) const { return peakName(m_sFilename, m_fTimeStretch); } // Peak cache properties accessors. QString qtractorAudioPeakFile::name (void) const { return m_peakFile.fileName(); } unsigned short qtractorAudioPeakFile::period (void) { return m_peakHeader.period; } unsigned short qtractorAudioPeakFile::channels (void) { return m_peakHeader.channels; } // Read frames from peak file. qtractorAudioPeakFile::Frame *qtractorAudioPeakFile::read ( unsigned long iPeakOffset, unsigned int iPeakLength ) { // Must be open for something... if (m_openMode == None) return nullptr; // Make things critical... QMutexLocker locker(&m_mutex); #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioPeakFile[%p]::read(%lu, %u) [%lu, %u, %u]", this, iPeakOffset, iPeakLength, m_iBuffOffset, m_iBuffLength, m_iBuffSize); #endif // Cache effect, only valid if we're really reading... const unsigned long iPeakEnd = iPeakOffset + iPeakLength; if (iPeakOffset >= m_iBuffOffset && m_iBuffOffset < iPeakEnd) { const unsigned long iBuffEnd = m_iBuffOffset + m_iBuffLength; const unsigned long iBuffOffset = m_peakHeader.channels * (iPeakOffset - m_iBuffOffset); if (iBuffEnd >= iPeakEnd) return m_pBuffer + iBuffOffset; if (m_iBuffOffset + m_iBuffSize >= iPeakEnd) { m_iBuffLength += readBuffer(m_iBuffLength, iBuffEnd, iPeakEnd - iBuffEnd); return m_pBuffer + iBuffOffset; } } // Read peak data as requested... m_iBuffLength = readBuffer(0, iPeakOffset, iPeakLength); m_iBuffOffset = iPeakOffset; return m_pBuffer; } // Read frames from peak file into local buffer cache. unsigned int qtractorAudioPeakFile::readBuffer ( unsigned int iBuffOffset, unsigned long iPeakOffset, unsigned int iPeakLength ) { const unsigned int nsize = m_peakHeader.channels * sizeof(Frame); // Shall we reallocate? if (iBuffOffset + iPeakLength > m_iBuffSize) { Frame *pOldBuffer = m_pBuffer; m_iBuffSize += (iPeakLength << 1); m_pBuffer = new Frame [m_peakHeader.channels * m_iBuffSize]; if (pOldBuffer) { ::memcpy(m_pBuffer, pOldBuffer, m_iBuffLength * nsize); delete [] pOldBuffer; } } #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioPeakFile[%p]::readBuffer(%u, %lu, %u) [%lu, %u, %u]", this, iBuffOffset, iPeakOffset, iPeakLength, m_iBuffOffset, m_iBuffLength, m_iBuffSize); #endif // Grab new contents from peak file... char *pBuffer = (char *) (m_pBuffer + m_peakHeader.channels * iBuffOffset); const unsigned long iOffset = iPeakOffset * nsize; const unsigned int iLength = iPeakLength * nsize; int nread = 0; if (m_peakFile.seek(sizeof(Header) + iOffset)) nread = int(m_peakFile.read(&pBuffer[0], iLength)); // Zero the remaining... if (nread < int(iLength)) ::memset(&pBuffer[nread], 0, iLength - nread); return nread / nsize; } // Open an new peak file for writing. bool qtractorAudioPeakFile::openWrite ( unsigned short iChannels, unsigned int iSampleRate ) { // We need the master peak period reference. qtractorAudioPeakFactory *pPeakFactory = qtractorAudioPeakFactory::getInstance(); if (pPeakFactory == nullptr) return false; // If it's already open, just tell the news. if (m_openMode == Write) return true; // Make things critical... QMutexLocker locker(&m_mutex); // We'll force (re)open if already reading (duh?) if (m_openMode == Read) { m_peakFile.close(); m_openMode = None; } // Just open and go ahead with it... if (!m_peakFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) return false; // Set open mode... m_openMode = Write; // Initialize header... m_peakHeader.period = pPeakFactory->peakPeriod(); m_peakHeader.channels = iChannels; // Write peak file header. if (m_peakFile.write((const char *) &m_peakHeader, sizeof(Header)) != qint64(sizeof(Header))) { m_peakFile.close(); return false; } #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioPeakFile[%p]::openWrite() ---", this); qDebug("name = %s", m_peakFile.fileName().toUtf8().constData()); qDebug("filename = %s", m_sFilename.toUtf8().constData()); qDebug("timeStretch = %g", m_fTimeStretch); qDebug("header = %u", sizeof(Header)); qDebug("frame = %u", sizeof(Frame)); qDebug("period = %d", m_peakHeader.period); qDebug("channels = %d", m_peakHeader.channels); qDebug("---"); #endif // Go ahead, it's already open. m_pWriter = new Writer(); m_pWriter->offset = 0; m_pWriter->amax = new float [m_peakHeader.channels]; m_pWriter->amin = new float [m_peakHeader.channels]; m_pWriter->arms = new float [m_peakHeader.channels]; for (unsigned short i = 0; i < m_peakHeader.channels; ++i) m_pWriter->amax[i] = m_pWriter->amin[i] = m_pWriter->arms[i] = 0.0f; // Get resample/timestretch-aware internal peak period ratio... m_pWriter->period_p = iSampleRate; qtractorAudioEngine *pAudioEngine = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pAudioEngine = pSession->audioEngine(); if (pAudioEngine) { const unsigned int num = (iSampleRate * m_peakHeader.period); const unsigned int den = (unsigned int) ::rintf(float(pAudioEngine->sampleRate() * m_fTimeStretch)); m_pWriter->period_q = (num / den); m_pWriter->period_r = (num % den); if (m_pWriter->period_r > 0) { m_pWriter->period_r += m_peakHeader.period; m_pWriter->period_r /= m_peakHeader.period; } } else { m_pWriter->period_q = m_peakHeader.period; m_pWriter->period_r = 0; } // Start counting for peak generator and writer... m_pWriter->nframe = 0; m_pWriter->npeak = 0; m_pWriter->nread = 0; m_pWriter->nwrite = m_pWriter->period_q; // It's a certain success... return true; } // Close the (hopefully) created peak file. void qtractorAudioPeakFile::closeWrite (void) { // Make things critical... QMutexLocker locker(&m_mutex); // Flush and close... if (m_openMode == Write) { if (m_pWriter && m_pWriter->npeak > 0) writeFrame(); m_peakFile.close(); m_openMode = None; } if (m_pWriter) { delete [] m_pWriter->amax; delete [] m_pWriter->amin; delete [] m_pWriter->arms; delete m_pWriter; m_pWriter = nullptr; } } // Write peak from audio frame methods. int qtractorAudioPeakFile::write ( float **ppAudioFrames, unsigned int iAudioFrames ) { // Make things critical... QMutexLocker locker(&m_mutex); // We should be actually open for writing... if (m_openMode != Write || m_pWriter == nullptr) return 0; for (unsigned int n = 0; n < iAudioFrames; ++n) { // Accumulate for this sample frame... for (unsigned short k = 0; k < m_peakHeader.channels; ++k) { const float fSample = ppAudioFrames[k][n]; if (m_pWriter->amax[k] < fSample || m_pWriter->npeak == 0) m_pWriter->amax[k] = fSample; if (m_pWriter->amin[k] > fSample || m_pWriter->npeak == 0) m_pWriter->amin[k] = fSample; m_pWriter->arms[k] += (fSample * fSample); } // Count peak frames (incremental)... ++m_pWriter->npeak; // Have we reached the peak accumulative period? if (++m_pWriter->nread >= m_pWriter->nwrite) { // Estimate next stop... m_pWriter->nwrite += m_pWriter->period_q; // Apply rounding fraction, if any... if (m_pWriter->period_r > 0) { m_pWriter->nframe += m_pWriter->period_q; if (m_pWriter->nframe >= m_pWriter->period_p) { m_pWriter->nwrite += m_pWriter->period_r; m_pWriter->nframe = 0; } } // Write peak frame out. writeFrame(); // We'll reset counter. m_pWriter->npeak = 0; } } return iAudioFrames; } static inline unsigned char unormf ( const float x ) { // const int i = int(255.0f * x); const int i = int(510.0f * x / (1.0f + x)); // slight-compression! return (i > 255 ? 255 : i); } void qtractorAudioPeakFile::writeFrame (void) { if (m_pWriter == nullptr) return; if (!m_peakFile.seek(sizeof(Header) + m_pWriter->offset)) return; Frame frame; for (unsigned short k = 0; k < m_peakHeader.channels; ++k) { // Write the denormalized peak values... float& fmax = m_pWriter->amax[k]; float& fmin = m_pWriter->amin[k]; float& frms = m_pWriter->arms[k]; frame.max = unormf(::fabsf(fmax)); frame.min = unormf(::fabsf(fmin)); frame.rms = unormf(::sqrtf(frms / float(m_pWriter->npeak))); // Reset peak period accumulators... fmax = fmin = frms = 0.0f; // Bail out?... m_pWriter->offset += m_peakFile.write((const char *) &frame, sizeof(Frame)); } } // Reference count methods. void qtractorAudioPeakFile::addRef (void) { ++m_iRefCount; } void qtractorAudioPeakFile::removeRef (void) { if (--m_iRefCount == 0) cleanup(); } // Physical removal. void qtractorAudioPeakFile::remove (void) { m_peakFile.remove(); } // Clean/close method. void qtractorAudioPeakFile::cleanup ( bool bAutoRemove ) { // Check if it's aborting (ought to be atomic)... const bool bAborted = (m_bWaitSync || bAutoRemove); m_bWaitSync = false; // Close the file, anyway now. closeWrite(); closeRead(); // Physically remove the file if aborted... if (bAborted) remove(); } // Sync thread state flags accessors. void qtractorAudioPeakFile::setWaitSync ( bool bWaitSync ) { m_bWaitSync = bWaitSync; } bool qtractorAudioPeakFile::isWaitSync (void) const { return m_bWaitSync; } // Peak filename standard. QString qtractorAudioPeakFile::peakName ( const QString& sFilename, float fTimeStretch ) { return sFilename + '_' + QString::number(fTimeStretch); } //---------------------------------------------------------------------- // class qtractorAudioPeak -- Audio Peak file pseudo-cache. // // Constructor. qtractorAudioPeak::qtractorAudioPeak ( qtractorAudioPeakFile *pPeakFile ) : m_pPeakFile(pPeakFile), m_pPeakFrames(nullptr), m_iPeakLength(0), m_iPeakHash(0) { m_pPeakFile->addRef(); } // Copy contructor. qtractorAudioPeak::qtractorAudioPeak ( const qtractorAudioPeak& peak ) : m_pPeakFile(peak.m_pPeakFile), m_pPeakFrames(nullptr), m_iPeakLength(0), m_iPeakHash(0) { m_pPeakFile->addRef(); } // Default destructor. qtractorAudioPeak::~qtractorAudioPeak (void) { m_pPeakFile->removeRef(); if (m_pPeakFrames) delete [] m_pPeakFrames; } // Peak frame buffer reader-cache executive. qtractorAudioPeakFile::Frame *qtractorAudioPeak::peakFrames ( unsigned long iFrameOffset, unsigned long iFrameLength, int width ) { // Try open current peak file as is... if (!m_pPeakFile->openRead()) return nullptr; // Just in case resolutions might change... const unsigned short iPeakPeriod = m_pPeakFile->period(); if (iPeakPeriod < 1) return nullptr; // Peak frames length estimation... const unsigned int iPeakLength = (iFrameLength / iPeakPeriod); if (iPeakLength < 1) return nullptr; // We'll get a brand new peak frames alright... const unsigned short iChannels = m_pPeakFile->channels(); if (iChannels < 1) return nullptr; // Have we been here before? if (m_pPeakFrames) { // Check if we have the same previous hash... if (!m_pPeakFile->isWaitSync()) { const unsigned int iPeakHash = qHash(iPeakPeriod) ^ qHash(iFrameOffset) ^ qHash(iFrameLength) ^ qHash(width); if (m_iPeakHash == iPeakHash) return m_pPeakFrames; m_iPeakHash = iPeakHash; } // Clenup previous frame-buffers... delete [] m_pPeakFrames; m_pPeakFrames = nullptr; m_iPeakLength = 0; } // Grab them in... const unsigned long iPeakOffset = (iFrameOffset / iPeakPeriod); qtractorAudioPeakFile::Frame *pPeakFrames = m_pPeakFile->read(iPeakOffset, iPeakLength); if (pPeakFrames == nullptr) return nullptr; // Check if we better aggregate over the frame buffer.... const int p1 = int(iPeakLength); const int n1 = iChannels * p1; if (width < p1 && width > 1) { const int w2 = (width >> 1) + 1; const int n2 = iChannels * w2; m_pPeakFrames = new qtractorAudioPeakFile::Frame [n2]; int n = 0; int i = 0; while (n < n2) { const int i2 = iChannels * (((n + iChannels) * p1) / n2); if (i2 >= n1) break; for (unsigned short k = 0; k < iChannels; ++k) { qtractorAudioPeakFile::Frame *pNewFrame = &m_pPeakFrames[n + k]; pNewFrame->max = 0; pNewFrame->min = 0; pNewFrame->rms = 0; for (int j = i; j < i2; j += iChannels) { qtractorAudioPeakFile::Frame *pOldFrame = &pPeakFrames[j + k]; if (pNewFrame->max < pOldFrame->max) pNewFrame->max = pOldFrame->max; if (pNewFrame->min < pOldFrame->min) pNewFrame->min = pOldFrame->min; if (pNewFrame->rms < pOldFrame->rms) pNewFrame->rms = pOldFrame->rms; } } n += iChannels; i = i2; } // New-indirect frame buffer length... m_iPeakLength = n / iChannels; // Done-indirect. } else { // Direct-copy frame-buffer... m_pPeakFrames = new qtractorAudioPeakFile::Frame [n1]; ::memcpy(m_pPeakFrames, pPeakFrames, n1 * sizeof(qtractorAudioPeakFile::Frame)); // New-direct frame buffer length... m_iPeakLength = iPeakLength; // Done-direct. } return m_pPeakFrames; } //---------------------------------------------------------------------- // class qtractorAudioPeakFactory -- Audio peak file factory (singleton). // // Singleton instance pointer. qtractorAudioPeakFactory *qtractorAudioPeakFactory::g_pPeakFactory = nullptr; // Singleton instance accessor (static). qtractorAudioPeakFactory *qtractorAudioPeakFactory::getInstance (void) { return g_pPeakFactory; } // Constructor. qtractorAudioPeakFactory::qtractorAudioPeakFactory ( QObject *pParent ) : QObject(pParent), m_bAutoRemove(false), m_pPeakThread(nullptr), m_iPeakPeriod(c_iPeakPeriod) { // Pseudo-singleton reference setup. g_pPeakFactory = this; } // Default destructor. qtractorAudioPeakFactory::~qtractorAudioPeakFactory (void) { if (m_pPeakThread) { if (m_pPeakThread->isRunning()) do { m_pPeakThread->setRunState(false); // m_pPeakThread->terminate(); m_pPeakThread->sync(); } while (!m_pPeakThread->wait(100)); delete m_pPeakThread; m_pPeakThread = nullptr; } cleanup(); // Pseudo-singleton reference shut-down. g_pPeakFactory = nullptr; } // The peak period accessors. void qtractorAudioPeakFactory::setPeakPeriod ( unsigned short iPeakPeriod ) { if (iPeakPeriod == m_iPeakPeriod) return; QMutexLocker locker(&m_mutex); sync(nullptr); m_iPeakPeriod = iPeakPeriod; // Refresh all current peak files (asynchronously)... PeakFiles::ConstIterator iter = m_peaks.constBegin(); const PeakFiles::ConstIterator& iter_end = m_peaks.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorAudioPeakFile *pPeakFile = iter.value(); pPeakFile->cleanup(true); sync(pPeakFile); } } unsigned short qtractorAudioPeakFactory::peakPeriod (void) const { return m_iPeakPeriod; } // The peak file factory-methods. qtractorAudioPeak* qtractorAudioPeakFactory::createPeak ( const QString& sFilename, float fTimeStretch ) { QMutexLocker locker(&m_mutex); if (m_pPeakThread == nullptr) { m_pPeakThread = new qtractorAudioPeakThread(); m_pPeakThread->start(); } const QString& sPeakName = qtractorAudioPeakFile::peakName(sFilename, fTimeStretch); qtractorAudioPeakFile *pPeakFile = m_peaks.value(sPeakName); if (pPeakFile == nullptr) { pPeakFile = new qtractorAudioPeakFile(sFilename, fTimeStretch); m_peaks.insert(sPeakName, pPeakFile); } return new qtractorAudioPeak(pPeakFile); } // Auto-delete property. void qtractorAudioPeakFactory::setAutoRemove ( bool bAutoRemove ) { m_bAutoRemove = bAutoRemove; } bool qtractorAudioPeakFactory::isAutoRemove (void) const { return m_bAutoRemove; } // Event notifier. void qtractorAudioPeakFactory::notifyPeakEvent (void) { emit peakEvent(); } // Base sync method. void qtractorAudioPeakFactory::sync ( qtractorAudioPeakFile *pPeakFile ) { if (m_pPeakThread) m_pPeakThread->sync(pPeakFile); } // Cleanup method. void qtractorAudioPeakFactory::cleanup (void) { QMutexLocker locker(&m_mutex); sync(nullptr); // Cleanup all current registered peak files... PeakFiles::ConstIterator iter = m_peaks.constBegin(); const PeakFiles::ConstIterator& iter_end = m_peaks.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorAudioPeakFile *pPeakFile = iter.value(); pPeakFile->cleanup(m_bAutoRemove); } qDeleteAll(m_peaks); m_peaks.clear(); // Reset to default resolution... m_iPeakPeriod = c_iPeakPeriod; } // end of qtractorAudioPeak.cpp qtractor-1.5.11/src/PaxHeaders/qtractorAudioBuffer.h0000644000000000000000000000013215124701674017436 xustar0030 mtime=1767080892.777862586 30 atime=1767080892.777862586 30 ctime=1767080892.777862586 qtractor-1.5.11/src/qtractorAudioBuffer.h0000644000175000001440000002026115124701674017427 0ustar00rncbcusers// qtractorAudioBuffer.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorAudioBuffer_h #define __qtractorAudioBuffer_h #include "qtractorList.h" #include "qtractorAudioFile.h" #include "qtractorRingBuffer.h" #ifdef CONFIG_LIBSAMPLERATE // libsamplerate API #include #endif #include #include #include // Forward declarations. class qtractorAudioPeakFile; class qtractorAudioBuffer; class qtractorTimeStretcher; //---------------------------------------------------------------------- // class qtractorAudioBufferThread -- Ring-cache manager thread. // class qtractorAudioBufferThread : public QThread { public: // Constructor. qtractorAudioBufferThread(unsigned int iSyncSize = 8); // Destructor. ~qtractorAudioBufferThread(); // Thread run state accessors. void setRunState(bool bRunState); bool runState() const; // Wake from executive wait condition (RT-safe). void sync(qtractorAudioBuffer *pAudioBuffer = nullptr); // Bypass executive wait condition (non RT-safe). void syncExport(); // Conditional resize check. void checkSyncSize(unsigned int iSyncSize); protected: // The main thread executives. void run(); void process(); private: // Instance variables. unsigned int m_iSyncSize; unsigned int m_iSyncMask; qtractorAudioBuffer **m_ppSyncItems; volatile unsigned int m_iSyncRead; volatile unsigned int m_iSyncWrite; // Whether the thread is logically running. volatile bool m_bRunState; // Thread synchronization objects. QMutex m_mutex; QWaitCondition m_cond; }; //---------------------------------------------------------------------- // class qtractorAudioBuffer -- Ring buffer/cache template declaration. // class qtractorAudioBuffer { public: // Constructor. qtractorAudioBuffer( qtractorAudioBufferThread *pSyncThread, unsigned short iChannels); // Default destructor. ~qtractorAudioBuffer(); // Internal file descriptor accessors. qtractorAudioFile *file() const; // File implementation properties. unsigned short channels() const; unsigned long frames() const; // Operational properties. unsigned int bufferSize() const; // Resample ratio accessor. float resampleRatio() const; // Operational initializer/terminator. bool open(const QString& sFilename, int iMode = qtractorAudioFile::Read); void close(); // Buffer data read/write. int read(float **ppFrames, unsigned int iFrames, unsigned int iOffset = 0); int write(float **ppFrames, unsigned int iFrames, unsigned short iChannels = 0, unsigned int iOffset = 0); // Special kind of super-read/channel-mix. int readMix(float **ppFrames, unsigned int iFrames, unsigned short iChannels, unsigned int iOffset, float fGain); int readMux(float **ppFrames, unsigned int iFrames, unsigned short iChannels, unsigned int iOffset, float fGain); // Buffer data seek. bool seek(unsigned long iFrame); // Reset this buffer's state. void reset(bool bLooping); // Logical clip-offset (in frames from beginning-of-file). void setOffset(unsigned long iOffset); unsigned long offset() const; // Logical clip-length (in frames from clip-start/offset). void setLength(unsigned long iLength); unsigned long length() const; // Current (last known) file length accessor. unsigned long fileLength() const; // Local gain/panning accessors. void setGain(float fGain); float gain() const; void setPanning(float fPanning); float panning() const; float channelGain(unsigned short i) const; // Loop points accessors. void setLoop(unsigned long iLoopStart, unsigned long iLoopEnd); unsigned long loopStart() const; unsigned long loopEnd() const; // Time-stretch factor. void setTimeStretch(float fTimeStretch); float timeStretch() const; bool isTimeStretch() const; // Pitch-shift factor. void setPitchShift(float fPitchShift); float pitchShift() const; bool isPitchShift() const; // Sync thread state flags accessors. enum SyncFlag { InitSync = 1, ReadSync = 2, WaitSync = 4, CloseSync = 8 }; void setSyncFlag(SyncFlag flag, bool bOn = true); bool isSyncFlag(SyncFlag flag) const; // Initial thread-sync executive (if file is on read mode, // check whether it can be cache-loaded integrally). void initSync(); // Base sync method. void sync(); // Audio frame process synchronization predicate method. bool inSync(unsigned long iFrameStart, unsigned long iFrameEnd); // Export-mode sync executive. void syncExport(); // Internal peak descriptor accessors. void setPeakFile(qtractorAudioPeakFile *pPeakFile); qtractorAudioPeakFile *peakFile() const; // Buffer engines flags accessors (local option) void setStretcherFlags(unsigned int iStretcherFlags); unsigned int stretcherFlags() const; // Buffer engines flags accessors (local option) static void setDefaultStretcherFlags(unsigned int iStretcherFlags); static unsigned int defaultStretcherFlags(); // Sample-rate converter type accessor (global option). static void setDefaultResampleType(int iResampleType); static int defaultResampleType(); protected: // Read-sync mode methods (playback). void readSync(); // Write-sync mode method (recording). void writeSync(); // Internal-seek sync executive. bool seekSync(unsigned long iFrame); // Last-mile frame buffer-helper processor. int writeFrames(float **ppFrames, unsigned int iFrames); int flushFrames(float **ppFrames, unsigned int iFrames); // Buffer process methods. int readBuffer (unsigned int iFrames); int writeBuffer (unsigned int iFrames); // Special kind of super-read/channel-mix buffer helper. int readMixFrames(float **ppFrames, unsigned int iFrames, unsigned short iChannels, unsigned int iOffset, float fGain); // I/O buffer release. void deleteIOBuffers(); // Frame position converters. unsigned long framesIn(unsigned long iFrames) const; unsigned long framesOut(unsigned long iFrames) const; private: // Audio buffer instance variables. qtractorAudioBufferThread *m_pSyncThread; unsigned short m_iChannels; qtractorAudioFile *m_pFile; qtractorRingBuffer *m_pRingBuffer; unsigned int m_iThreshold; unsigned int m_iBufferSize; volatile unsigned char m_syncFlags; volatile unsigned long m_iReadOffset; volatile unsigned long m_iWriteOffset; unsigned long m_iFileLength; bool m_bIntegral; unsigned long m_iOffset; unsigned long m_iLength; volatile unsigned long m_iLoopStart; volatile unsigned long m_iLoopEnd; unsigned long m_iSeekOffset; qtractorAtomic m_seekPending; float **m_ppFrames; float **m_ppBuffer; bool m_bTimeStretch; float m_fTimeStretch; bool m_bPitchShift; float m_fPitchShift; qtractorTimeStretcher *m_pTimeStretcher; float m_fGain; float m_fPanning; float *m_pfGains; float m_fNextGain; int m_iRampGain; #ifdef CONFIG_LIBSAMPLERATE bool m_bResample; float m_fResampleRatio; unsigned int m_iInputPending; float **m_ppInBuffer; float **m_ppOutBuffer; SRC_STATE **m_ppSrcState; #endif qtractorAudioPeakFile *m_pPeakFile; // Buffer engine mode flags. unsigned int m_iStretcherFlags; static unsigned int g_iDefaultStretcherFlags; // Sample-rate converter type global option. static int g_iDefaultResampleType; }; #endif // __qtractorAudioBuffer_h // end of qtractorAudioBuffer.h qtractor-1.5.11/src/PaxHeaders/qtractorInstrumentForm.ui0000644000000000000000000000013215124701674020425 xustar0030 mtime=1767080892.786263483 30 atime=1767080892.786263483 30 ctime=1767080892.786263483 qtractor-1.5.11/src/qtractorInstrumentForm.ui0000644000175000001440000001565115124701674020425 0ustar00rncbcusers rncbc aka Rui Nuno Capela qtractor - An Audio/MIDI multi-track sequencer. Copyright (C) 2005-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. qtractorInstrumentForm 0 0 580 340 Instruments Qt::Horizontal 240 0 QAbstractItemView::NoSelection true true true Instruments Qt::Vertical 240 0 false true false true Files Path 240 0 QAbstractItemView::NoSelection true true true Names Import from instrument file &Import... Remove instrument file &Remove Move instrument file up on list order &Up Move instrument file down on list order &Down Qt::Vertical QSizePolicy::Expanding 20 20 Export to instrument file E&xport... Close this dialog Close InstrumentsListView FilesListView NamesListView ImportPushButton RemovePushButton MoveUpPushButton MoveDownPushButton ExportPushButton ClosePushButton qtractor-1.5.11/src/PaxHeaders/qtractorMidiControlPlugin.h0000644000000000000000000000013215124701674020645 xustar0030 mtime=1767080892.790263466 30 atime=1767080892.790263466 30 ctime=1767080892.790263466 qtractor-1.5.11/src/qtractorMidiControlPlugin.h0000644000175000001440000001160615124701674020641 0ustar00rncbcusers// qtractorMidiControlPlugin.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorMidiControlPlugin_h #define __qtractorMidiControlPlugin_h #include "qtractorPlugin.h" // Forward declarations. class qtractorMidiBus; //---------------------------------------------------------------------------- // qtractorMidiControlPluginType -- MIDI Controller pseudo-plugin type instance. // class qtractorMidiControlPluginType : public qtractorPluginType { public: // Constructor. qtractorMidiControlPluginType() : qtractorPluginType(nullptr, 0, qtractorPluginType::Control) {} // Factory method (static) static qtractorPlugin *createPlugin(qtractorPluginList *pList); // Specific named accessors. unsigned short channels() const { return 0; } // Derived methods. bool open(); void close(); // Compute the number of instances needed (always one). unsigned short instances(unsigned short iChannels, bool /*bMidi*/) const { return (iChannels > 0 ? 1 : 0); } // Instance cached-deferred accessors. const QString& aboutText(); }; //---------------------------------------------------------------------------- // qtractorMidiControlPlugin -- MIDI Controller pseudo-plugin instance. // class qtractorMidiControlPlugin : public qtractorPlugin { public: // Constructors. qtractorMidiControlPlugin(qtractorPluginList *pList, qtractorMidiControlPluginType *pMidiControlType); // Destructor. ~qtractorMidiControlPlugin(); // Channel/intsance number accessors. void setChannels(unsigned short iChannels); // Do the actual (de)activation. void activate(); void deactivate(); // The main plugin processing procedure. void process(float **ppIBuffer, float **ppOBuffer, unsigned int nframes); // Plugin configuration handlers. void configure(const QString& sKey, const QString& sValue); // Plugin configuration/state snapshot. void freezeConfigs(); void releaseConfigs(); // MIDI specific accessor. qtractorMidiBus *midiBus() const; // Accessors. void setControlType(qtractorMidiControl::ControlType ctype); qtractorMidiControl::ControlType controlType() const; void setControlParam(unsigned short iParam); unsigned short controlParam() const; void setControlChannel(unsigned short iChannel); unsigned short controlChannel() const; void setControlLogarithmic(bool bLogarithmic); bool isControlLogarithmic() const; void setControlInvert(bool bInvert); bool isControlInvert() const; void setControlBipolar(bool bBipolar); bool isControlBipolar() const; void setControlAutoConnect(bool bAutoConnect); bool isControlAutoConnect() const; // Override title/name caption. QString title() const; // Forward decls. class Param; protected: void updateControlBipolar(); void updateControlAutoConnect(); // Parameter update method (virtual). void updateParam(qtractorPlugin::Param *pParam, float fValue, bool bUpdate); private: // Instance variables. qtractorMidiBus *m_pMidiBus; qtractorMidiControl::ControlType m_controlType; unsigned short m_iControlParam; unsigned short m_iControlChannel; bool m_bControlLogarithmic; bool m_bControlInvert; bool m_bControlBipolar; bool m_bControlAutoConnect; Param *m_pControlValueParam; }; //---------------------------------------------------------------------------- // qtractorMidiControlPlugin::Param -- MIDI Controller plugin control input port. // class qtractorMidiControlPlugin::Param : public qtractorPlugin::Param { public: // Constructors. Param(qtractorMidiControlPlugin *pMidiControlPlugin, unsigned long iIndex) : qtractorPlugin::Param(pMidiControlPlugin, iIndex) {} protected: // Property range hints predicate methods. bool isBoundedBelow() const { return true; } bool isBoundedAbove() const { return true; } bool isDefaultValue() const { return true; } bool isLogarithmic() const; bool isSampleRate() const { return false; } bool isInteger() const { return false; } bool isToggled() const { return false; } bool isDisplay() const { return false; } }; #endif // __qtractorMidiControlPlugin_h // end of qtractorMidiControlPlugin.h qtractor-1.5.11/src/PaxHeaders/qtractorAudioIOMatrixForm.cpp0000644000000000000000000000013215124701674021100 xustar0030 mtime=1767080892.779263512 30 atime=1767080892.779263512 30 ctime=1767080892.779263512 qtractor-1.5.11/src/qtractorAudioIOMatrixForm.cpp0000644000175000001440000001674215124701674021102 0ustar00rncbcusers// qtractorAudioIOMatrixForm.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAudioIOMatrixForm.h" #include "ui_qtractorAudioIOMatrixForm.h" #include #include #include #include #include #include //------------------------------------------------------------------------- // qtractorAudioIOMatrixForm::RadioButton class qtractorAudioIOMatrixForm::RadioButton : public QRadioButton { public: RadioButton(QWidget *parent = nullptr) : QRadioButton(parent) {} protected: bool hitButton(const QPoint&) const { return false; } }; //------------------------------------------------------------------------- // qtractorAudioIOMatrixForm::TableCell class qtractorAudioIOMatrixForm::TableCell : public QWidget { public: TableCell(QWidget *parent = nullptr) : QWidget(parent) { m_radio = new RadioButton(this); QHBoxLayout *layout = new QHBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addStretch(1); layout->addWidget(m_radio); layout->addStretch(1); QWidget::setLayout(layout); } RadioButton *radio() const { return m_radio; } private: RadioButton *m_radio; }; //------------------------------------------------------------------------- // qtractorAudioIOMatrixForm qtractorAudioIOMatrixForm::qtractorAudioIOMatrixForm ( QWidget *parent ) : QDialog(parent), p_ui(new Ui::qtractorAudioIOMatrixForm), m_ui(*p_ui) { m_ui.setupUi(this); m_ui.TableWidget->setForm(this); QObject::connect(m_ui.DialogButtons, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtons, SIGNAL(rejected()), SLOT(reject())); QDialog::adjustSize(); } qtractorAudioIOMatrixForm::~qtractorAudioIOMatrixForm (void) { delete p_ui; qDeleteAll(m_groups); } void qtractorAudioIOMatrixForm::setChannels ( int nins, int nouts ) { #ifdef CONFIG_DEBUG qDebug("%s(%d, %d)", __func__, nins, nouts); #endif m_ui.TableWidget->setRowCount(nins); m_ui.TableWidget->setColumnCount(nouts); } int qtractorAudioIOMatrixForm::inputChannels (void) const { return m_ui.TableWidget->rowCount(); } int qtractorAudioIOMatrixForm::outputChannels (void) const { return m_ui.TableWidget->columnCount(); } void qtractorAudioIOMatrixForm::setMatrix ( const QList& matrix ) { m_matrix = matrix; } const QList& qtractorAudioIOMatrixForm::matrix (void) const { return m_matrix; } void qtractorAudioIOMatrixForm::inputChannelsChanged ( int index ) { setChannels(index + 1, outputChannels()); } void qtractorAudioIOMatrixForm::outputChannelsChanged ( int index ) { setChannels(inputChannels(), index + 1); } const QList& qtractorAudioIOMatrixForm::groups (void) const { return m_groups; } void qtractorAudioIOMatrixForm::refresh (void) { m_ui.TableWidget->clear(); qDeleteAll(m_groups); m_groups.clear(); const int nouts = outputChannels(); QStringList cols; for (int col = 0; col < nouts; ++col) cols.append(QString::number(col + 1)); m_ui.TableWidget->setHorizontalHeaderLabels(cols); const int nins = inputChannels(); QStringList rows; for (int row = 0; row < nins; ++row) rows.append(QString::number(row + 1)); m_ui.TableWidget->setVerticalHeaderLabels(rows); for (int row = 0; row < nins; ++row) { int out = -1; if (row < m_matrix.size()) out = m_matrix.at(row); else out = (row % nouts); QButtonGroup *group = new QButtonGroup(this); group->setExclusive(true); for (int col = 0; col < nouts; ++col) { TableCell *cell = new TableCell(); m_ui.TableWidget->setCellWidget(row, col, cell); group->addButton(cell->radio(), col); if (out == col) cell->radio()->setChecked(true); } m_groups.append(group); } } void qtractorAudioIOMatrixForm::accept (void) { m_matrix.clear(); const int nins = inputChannels(); for (int row = 0; row < nins; ++row) { int out = -1; if (row < m_groups.size()) out = m_groups.at(row)->checkedId(); m_matrix.append(out); } QDialog::accept(); } void qtractorAudioIOMatrixForm::reject (void) { // Check if there's any pending changes... if (m_ui.TableWidget->isDirty()) { QMessageBox::StandardButtons buttons = QMessageBox::Apply | QMessageBox::Discard | QMessageBox::Cancel; switch (QMessageBox::warning(this, tr("Warning"), tr("Some settings have been changed.\n\n" "Do you want to apply the changes?"), buttons)) { case QMessageBox::Discard: break; case QMessageBox::Apply: accept(); // Fall-thru... default: // Cancel. return; } } QDialog::reject(); } //------------------------------------------------------------------------- // qtractorAudioIOMatrixForm::TableWidget qtractorAudioIOMatrixForm::TableWidget::TableWidget ( QWidget *parent ) : QTableWidget(2, 2, parent), m_form(nullptr), m_dirty(0) { QTableWidget::setSelectionMode(QTableWidget::NoSelection); QHeaderView *hheader = QTableWidget::horizontalHeader(); if (hheader) { hheader->setSectionResizeMode(QHeaderView::Stretch); hheader->setMinimumSectionSize(24); } QHeaderView *vheader = QTableWidget::verticalHeader(); if (vheader) { vheader->setSectionResizeMode(QHeaderView::Stretch); vheader->setMinimumSectionSize(24); } } void qtractorAudioIOMatrixForm::TableWidget::setForm ( qtractorAudioIOMatrixForm *form ) { m_form = form; } qtractorAudioIOMatrixForm *qtractorAudioIOMatrixForm::TableWidget::form (void) const { return m_form; } bool qtractorAudioIOMatrixForm::TableWidget::isDirty (void) const { return (m_dirty > 0); } void qtractorAudioIOMatrixForm::TableWidget::toggleCell ( int row, int col ) { const QList& groups = m_form->groups(); if (row >= 0 && row < groups.size()) { QButtonGroup *group = groups.at(row); if (group) { QRadioButton *radio = qobject_cast (group->button(col)); if (radio) { const bool on = radio->isChecked(); if (on) group->setExclusive(false); radio->setChecked(!on); if (on) group->setExclusive(true); ++m_dirty; } } } } void qtractorAudioIOMatrixForm::TableWidget::keyPressEvent ( QKeyEvent *event ) { QTableWidget::keyPressEvent(event); if (event->key() == Qt::Key_Space) { const int row = QTableWidget::currentRow(); const int col = QTableWidget::currentColumn(); toggleCell(row, col); } } void qtractorAudioIOMatrixForm::TableWidget::mousePressEvent ( QMouseEvent *event ) { QTableWidget::mousePressEvent(event); if (m_form == nullptr) return; const QPoint& pos = event->pos(); const int row = QTableWidget::rowAt(pos.y()); const int col = QTableWidget::columnAt(pos.x()); toggleCell(row, col); } // end of qtractorAudioIOMatrixForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorFileList.cpp0000644000000000000000000000013215124701674017311 xustar0030 mtime=1767080892.784263491 30 atime=1767080892.784263491 30 ctime=1767080892.784263491 qtractor-1.5.11/src/qtractorFileList.cpp0000644000175000001440000001275515124701674017313 0ustar00rncbcusers// qtractorFileList.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorFileList.h" #include "qtractorClip.h" #include //--------------------------------------------------------------------- // class qtractorFileList::Key -- file hash key. // uint qHash ( const qtractorFileList::Key& key ) { return qHash(key.type()) ^ qHash(key.path()); } //---------------------------------------------------------------------- // class qtractorFileList -- file path registry. // // File path registry management. void qtractorFileList::addFileItem ( qtractorFileList::Type iType, const QString& sPath, bool bAutoRemove ) { Item *pItem = addItem(iType, sPath, bAutoRemove); if (pItem) { pItem->addRef(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorFileList::addFileItem(%d, \"%s\", %d) refCount=%d clips=%d (%d)", int(pItem->type()), pItem->path().toUtf8().constData(), int(bAutoRemove), pItem->refCount(), pItem->clipRefCount(), int(pItem->isAutoRemove())); #endif } } void qtractorFileList::removeFileItem ( qtractorFileList::Type iType, const QString& sPath ) { Item *pItem = findItem(iType, sPath); if (pItem) { pItem->removeRef(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorFileList::removeFileItem(%d, \"%s\") refCount=%d clips=%d (%d)", int(pItem->type()), pItem->path().toUtf8().constData(), pItem->refCount(), pItem->clipRefCount(), int(pItem->isAutoRemove())); #endif removeItem(pItem); } } // Clip/path registry management. void qtractorFileList::addClipItem ( qtractorFileList::Type iType, const QString& sPath, bool bAutoRemove ) { Item *pItem = addItem(iType, sPath, bAutoRemove); if (pItem) { pItem->addClipRef(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorFileList::addClipItem(%d, \"%s\", %d) refCount=%d clips=%d (%d)", int(pItem->type()), pItem->path().toUtf8().constData(), int(bAutoRemove), pItem->refCount(), pItem->clipRefCount(), int(pItem->isAutoRemove())); #endif } } void qtractorFileList::addClipItem ( qtractorFileList::Type iType, qtractorClip *pClip, bool bAutoRemove ) { addClipItem(iType, pClip->filename(), bAutoRemove); } void qtractorFileList::addClipItemEx ( qtractorFileList::Type iType, qtractorClip *pClip, bool bAutoRemove ) { Item *pItem = addItem(iType, pClip->filename(), bAutoRemove); if (pItem) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorFileList::addClipItemEx(%d, \"%s\", %d) refCount=%d clips=%d (%d)", int(pItem->type()), pItem->path().toUtf8().constData(), int(bAutoRemove), pItem->refCount(), pItem->clipRefCount(), int(pItem->isAutoRemove())); #endif } } void qtractorFileList::removeClipItem ( qtractorFileList::Type iType, qtractorClip *pClip ) { Item *pItem = findItem(iType, pClip->filename()); if (pItem) { pItem->removeClipRef(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorFileList::removeClipItem(%d, \"%s\") refCount=%d clips=%d (%d)", int(pItem->type()), pItem->path().toUtf8().constData(), pItem->refCount(), pItem->clipRefCount(), int(pItem->isAutoRemove())); #endif } } // File hash table management. qtractorFileList::Item *qtractorFileList::findItem ( qtractorFileList::Type iType, const QString& sPath ) const { return m_items.value(Key(iType, sPath), nullptr); } qtractorFileList::Item *qtractorFileList::addItem ( qtractorFileList::Type iType, const QString& sPath, bool bAutoRemove ) { Key key(iType, sPath); Item *pItem = m_items.value(key, nullptr); if (pItem == nullptr) { pItem = new Item(key, bAutoRemove); m_items.insert(key, pItem); } else if (bAutoRemove && !pItem->isAutoRemove()) pItem->setAutoRemove(true); return pItem; } void qtractorFileList::removeItem ( qtractorFileList::Item *pItem ) { // pItem->removeRef(); if (pItem->refCount() < 1) { m_items.remove(Key(pItem->type(), pItem->path())); delete pItem; } } void qtractorFileList::cleanup ( bool bForce ) { Hash::ConstIterator iter = m_items.constBegin(); const Hash::ConstIterator& iter_end = m_items.constEnd(); for ( ; iter != iter_end; ++iter) { Item *pItem = iter.value(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorFileList::cleanup(%d, \"%s\") autoRemove=%d refCount=%d clips=%d", int(pItem->type()), pItem->path().toUtf8().constData(), int(pItem->isAutoRemove()), pItem->refCount(), pItem->clipRefCount()); #endif if (pItem->isAutoRemove()) { if (!bForce && pItem->clipRefCount() > 0) { pItem->setAutoRemove(false); // pItem->removeClipRef(); } else // Time for the kill...? if (bForce && pItem->clipRefCount() < 1) { QFile::remove(pItem->path()); } } } } void qtractorFileList::clear (void) { qDeleteAll(m_items); m_items.clear(); } // end of qtractorFileList.cpp qtractor-1.5.11/src/PaxHeaders/qtractorEditRangeForm.ui0000644000000000000000000000013215124701674020117 xustar0030 mtime=1767080892.784263491 30 atime=1767080892.784263491 30 ctime=1767080892.784263491 qtractor-1.5.11/src/qtractorEditRangeForm.ui0000644000175000001440000002364515124701674020121 0ustar00rncbcusers rncbc aka Rui Nuno Capela qtractor - An Audio/MIDI multi-track sequencer. Copyright (C) 2005-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. qtractorEditRangeForm 0 0 340 280 Qt::StrongFocus Edit Range :/images/qtractor.svg Range Selection range &Selection false Loop range &Loop false Punch range &Punch false Edit range &Edit false Custom range &Custom true St&art: RangeStartSpinBox 120 0 Clip start Qt::Horizontal QSizePolicy::Fixed 14 14 En&d: RangeEndSpinBox 120 0 Clip offset Qt::Vertical 8 8 Options Apply to clips in range Cl&ips true Apply to Automation nodes in range A&utomation true Apply to Loop points in range L&oop Apply to Punch In/Out points in range Pu&nch Apply to location Markers in range Mar&kers Apply to Tempo Map nodes in range Te&mpo Map &Format Time display format Frames Time BBT Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorTimeSpinBox QSpinBox
qtractorSpinBox.h
SelectionRangeRadioButton LoopRangeRadioButton PunchRangeRadioButton EditRangeRadioButton CustomRangeRadioButton RangeStartSpinBox RangeEndSpinBox ClipsCheckBox AutomationCheckBox LoopCheckBox PunchCheckBox MarkersCheckBox TempoMapCheckBox FormatComboBox DialogButtonBox
qtractor-1.5.11/src/PaxHeaders/qtractorComboBox.cpp0000644000000000000000000000012415124701674017307 xustar0028 mtime=1767080892.7822635 28 atime=1767080892.7822635 28 ctime=1767080892.7822635 qtractor-1.5.11/src/qtractorComboBox.cpp0000644000175000001440000001062115124701674017276 0ustar00rncbcusers// qtractorComboBox.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorComboBox.h" #include "qtractorAudioFile.h" //------------------------------------------------------------------------- // qtractorAudioFileTypeComboBox - A simple combo-box custom widget. // Constructor. qtractorAudioFileTypeComboBox::qtractorAudioFileTypeComboBox ( QWidget *pParent ) : QComboBox(pParent) { int i = 0; const qtractorAudioFileFactory::FileFormats& formats = qtractorAudioFileFactory::formats(); for (qtractorAudioFileFactory::FileFormat *pFormat : formats) { if (pFormat->type != qtractorAudioFileFactory::MadFile) QComboBox::addItem(pFormat->name, i); ++i; } } // Current type accessors. void qtractorAudioFileTypeComboBox::setCurrentType ( const QString& sExt, int iType ) { const bool bBlockSignals = QComboBox::blockSignals(true); QComboBox::setCurrentIndex(indexOf(sExt, iType)); QComboBox::blockSignals(bBlockSignals); } const void *qtractorAudioFileTypeComboBox::currentHandle (void) const { return handleOf(QComboBox::currentIndex()); } int qtractorAudioFileTypeComboBox::currentType ( const void *handle ) const { if (handle == nullptr) handle = currentHandle(); const qtractorAudioFileFactory::FileFormat *pFormat = static_cast (handle); return (pFormat ? pFormat->data : 0); } QString qtractorAudioFileTypeComboBox::currentExt ( const void *handle ) const { if (handle == nullptr) handle = currentHandle(); const qtractorAudioFileFactory::FileFormat *pFormat = static_cast (handle); return (pFormat ? pFormat->ext : QString()); } // Indexed-type accessors. int qtractorAudioFileTypeComboBox::indexOf ( const QString& sExt, int iType ) const { int i = 0; int iIndex = QComboBox::currentIndex(); const qtractorAudioFileFactory::FileFormats& formats = qtractorAudioFileFactory::formats(); for (qtractorAudioFileFactory::FileFormat *pFormat : formats) { if (sExt == pFormat->ext && (iType == 0 || iType == pFormat->data)) { iIndex = QComboBox::findData(i); break; } ++i; } return iIndex; } const void *qtractorAudioFileTypeComboBox::handleOf ( int iIndex ) const { if (iIndex >= 0 && iIndex < QComboBox::count()) { const int i = QComboBox::itemData(iIndex).toInt(); return qtractorAudioFileFactory::formats().at(i); } else { return nullptr; } } //------------------------------------------------------------------------- // qtractorAudioFileFormatComboBox - A simple combo-box custom widget. // Constructor. qtractorAudioFileFormatComboBox::qtractorAudioFileFormatComboBox ( QWidget *pParent ) : QComboBox(pParent) { static QStringList s_formats; if (s_formats.isEmpty()) { s_formats.append(QObject::tr("Signed 16-Bit")); s_formats.append(QObject::tr("Signed 24-Bit")); s_formats.append(QObject::tr("Signed 32-Bit")); s_formats.append(QObject::tr("Float 32-Bit")); s_formats.append(QObject::tr("Float 64-Bit")); } QComboBox::addItems(s_formats); } //------------------------------------------------------------------------- // qtractorMidiFileFormatComboBox - A simple combo-box custom widget. // Constructor. qtractorMidiFileFormatComboBox::qtractorMidiFileFormatComboBox ( QWidget *pParent ) : QComboBox(pParent) { static QStringList s_formats; if (s_formats.isEmpty()) { s_formats.append(QObject::tr("SMF Format 0")); s_formats.append(QObject::tr("SMF Format 1")); } QComboBox::addItems(s_formats); } // end of qtractorComboBox.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiMeter.h0000644000000000000000000000013215124701674017122 xustar0030 mtime=1767080892.794263449 30 atime=1767080892.794263449 30 ctime=1767080892.794263449 qtractor-1.5.11/src/qtractorMidiMeter.h0000644000175000001440000001477715124701674017132 0ustar00rncbcusers// qtractorMidiMeter.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorMidiMeter_h #define __qtractorMidiMeter_h #include "qtractorMeter.h" #include // Forward declarations. class qtractorMidiMeter; class qtractorMidiMonitor; class qtractorAudioMeter; class qtractorAudioOutputMonitor; class QLabel; class QResizeEvent; class QPaintEvent; //---------------------------------------------------------------------------- // qtractorMidiMeterScale -- MIDI meter bridge scale widget. class qtractorMidiMeterScale : public qtractorMeterScale { public: // Constructor. qtractorMidiMeterScale(qtractorMidiMeter *pMidiMeter); protected: // Actual scale drawing method. void paintScale(QPainter *p); }; //---------------------------------------------------------------------------- // qtractorMidiMeterValue -- MIDI meter bridge value widget. class qtractorMidiMeterValue : public qtractorMeterValue { public: // Constructor. qtractorMidiMeterValue(qtractorMidiMeter *pMidiMeter); // Value refreshment. void refresh(unsigned long iStamp); protected: // Specific event handlers. void paintEvent(QPaintEvent *); void resizeEvent(QResizeEvent *); private: // Running variables. int m_iValue; float m_fValueDecay; int m_iPeak; int m_iPeakHold; float m_fPeakDecay; }; //---------------------------------------------------------------------------- // qtractorMidiMeterLed -- MIDI meter bridge LED widget. class qtractorMidiMeterLed : public qtractorMeterValue { public: // Constructor. qtractorMidiMeterLed(qtractorMidiMeter *pMidiMeter); // Default destructor. ~qtractorMidiMeterLed(); // Value refreshment. void refresh(unsigned long iStamp); private: // Local instance variables. QLabel *m_pMidiLabel; // Running variables. unsigned int m_iMidiCount; // MIDI I/O LED pixmap stuff. enum { LedOff = 0, LedOn = 1, LedCount = 2 }; static int g_iLedRefCount; static QPixmap *g_pLedPixmap[LedCount]; }; //---------------------------------------------------------------------------- // qtractorMidiMeter -- MIDI meter bridge slot widget. class qtractorMidiMeter : public qtractorMeter { public: // Constructor. qtractorMidiMeter( qtractorMidiMonitor *pMidiMonitor, QWidget *pParent = nullptr); // Default destructor. ~qtractorMidiMeter(); // Virtual monitor accessor. void setMonitor(qtractorMonitor *pMonitor); qtractorMonitor *monitor() const; // MIDI monitor accessor. void setMidiMonitor(qtractorMidiMonitor *pMidiMonitor); qtractorMidiMonitor *midiMonitor() const; // Monitor reset. void reset(); // Pixmap accessors. const QPixmap& pixmap() const; void updatePixmap(); // Color/level indexes. enum { ColorPeak = 0, ColorOver = 1, ColorBack = 2, ColorFore = 3, ColorCount = 4 }; // Common resource accessors. static void setColor(int iIndex, const QColor& color); static const QColor& color(int iIndex); static const QColor& defaultColor(int iIndex); protected: // Specific event handlers. void resizeEvent(QResizeEvent *); private: // Local instance variables. qtractorMidiMonitor *m_pMidiMonitor; qtractorMidiMeterValue *m_pMidiValue; QPixmap m_pixmap; static QColor g_defaultColors[ColorCount]; static QColor g_currentColors[ColorCount]; }; //---------------------------------------------------------------------------- // qtractorMidiComboMeter -- MIDI meter combo widget. class qtractorMidiComboMeter : public QWidget { Q_OBJECT public: // Constructor. qtractorMidiComboMeter( qtractorMidiMonitor *pMidiMonitor, QWidget *pParent = nullptr); // Default destructor. ~qtractorMidiComboMeter(); // Regular MIDI meter accessor. qtractorMidiMeter *midiMeter() const; // Combined audio-output meter accessor. qtractorAudioMeter *audioMeter() const; // Virtual monitor accessor. void setMonitor(qtractorMonitor *pMonitor); qtractorMonitor *monitor() const; // MIDI monitor accessor. void setMidiMonitor(qtractorMidiMonitor *pMidiMonitor); qtractorMidiMonitor *midiMonitor() const; // Audio-output monitor accessor. void setAudioOutputMonitor(qtractorAudioOutputMonitor *pAudioOutputMonitor); qtractorAudioOutputMonitor *audioOutputMonitor() const; // Monitor reset. void reset(); protected: // Resize event handler. void resizeEvent(QResizeEvent *); private: // Local instance variables. qtractorMidiMeter *m_pMidiMeter; qtractorAudioMeter *m_pAudioMeter; }; //---------------------------------------------------------------------------- // qtractorMidiMixerMeter -- MIDI mixer-strip meter bridge widget. class qtractorMidiMixerMeter : public qtractorMixerMeter { Q_OBJECT public: // Constructor. qtractorMidiMixerMeter( qtractorMidiMonitor *pMidiMonitor, QWidget *pParent = nullptr); // Default destructor. ~qtractorMidiMixerMeter(); // Virtual monitor accessor. void setMonitor(qtractorMonitor *pMonitor); qtractorMonitor *monitor() const; // MIDI monitor accessor. void setMidiMonitor(qtractorMidiMonitor *pMidiMonitor); qtractorMidiMonitor *midiMonitor() const; // Audio-output monitor accessor. void setAudioOutputMonitor(qtractorAudioOutputMonitor *pAudioOutputMonitor); qtractorAudioOutputMonitor *audioOutputMonitor() const; // Local slider update methods. void updatePanning(); void updateGain(); // Monitor reset. void reset(); protected: // Resize event handler. void resizeEvent(QResizeEvent *); private: // Local forward declarations. class GainSliderInterface; class GainSpinBoxInterface; // Local instance variables. qtractorMidiComboMeter *m_pMidiMeter; qtractorMidiMeterScale *m_pMidiScale; qtractorMidiMeterLed *m_pMidiLed; }; #endif // __qtractorMidiMeter_h // end of qtractorMidiMeter.h qtractor-1.5.11/src/PaxHeaders/qtractorCommand.h0000644000000000000000000000012415124701674016622 xustar0028 mtime=1767080892.7822635 28 atime=1767080892.7822635 28 ctime=1767080892.7822635 qtractor-1.5.11/src/qtractorCommand.h0000644000175000001440000000776215124701674016625 0ustar00rncbcusers// qtractorCommand.h // /**************************************************************************** Copyright (C) 2005-2022, 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 __qtractorCommand_h #define __qtractorCommand_h #include "qtractorList.h" #include #include // Forward declarations. class QAction; //---------------------------------------------------------------------- // class qtractorCommand - declaration. // class qtractorCommand : public qtractorList::Link { public: // Constructor. qtractorCommand(const QString& sName) : m_sName(sName), m_flags(Refresh) {} // Virtual destructor. virtual ~qtractorCommand() {} // Descriptive command name accessors. void setName(const QString& sName) { m_sName = sName; } const QString& name() const { return m_sName; } // Command flags. enum Flag { None = 0, AutoDelete = 1, Refresh = 2, Clear = 4, Select = 8, Reset = 16, ClearSelect = Clear | Select, ClearSelectReset = ClearSelect | Reset }; // Command flags accessor. unsigned int flags() const { return m_flags; } // Auto-removal/deletion flag accessors. void setAutoDelete(bool bAutoDelete) { setFlags(AutoDelete, bAutoDelete); } bool isAutoDelete() const { return isFlags(AutoDelete); } // Contents-refresh accessors. void setRefresh(bool bRefresh) { setFlags(Refresh, bRefresh); } bool isRefresh() const { return isFlags(Refresh); } // Selection clear/reset accessors. void setClearSelect(bool bClearSelect) { setFlags(ClearSelect, bClearSelect); } bool isClearSelect() const { return isFlags(ClearSelect); } void setClearSelectReset(bool bClearSelectReset) { setFlags(ClearSelectReset, bClearSelectReset); } bool isClearSelectReset() const { return isFlags(ClearSelectReset); } // Cannonical command methods. virtual bool redo() = 0; virtual bool undo() = 0; protected: // Discrete flag accessors. void setFlags(unsigned int flags, bool bOn = true) { if (bOn) m_flags |= flags; else m_flags &= ~flags; } bool isFlags(unsigned int flags) const { return ((m_flags & flags) == flags); } private: // Instance variables. QString m_sName; unsigned int m_flags; }; //---------------------------------------------------------------------- // class qtractorCommandList - declaration. // class qtractorCommandList : public QObject { Q_OBJECT public: // Constructor. qtractorCommandList(); // Destructor. virtual ~qtractorCommandList(); // Command stack cleaner. void clear(); // Command cursor accessors. qtractorCommand *lastCommand() const; qtractorCommand *nextCommand() const; // Remove last command from command chain. void removeLastCommand(); // Special backout method. void backout(qtractorCommand *pCommand); // Cannonical command methods. bool push(qtractorCommand *pCommand); bool exec(qtractorCommand *pCommand); bool undo(); bool redo(); // Command action update helper. void updateAction(QAction *pAction, qtractorCommand *pCommand) const; signals: // Command update notification. void updateNotifySignal(unsigned int); private: // Instance variables. qtractorList m_commands; qtractorCommand *m_pLastCommand; }; #endif // __qtractorCommand_h // end of qtractorCommand.h qtractor-1.5.11/src/PaxHeaders/qtractorWsolaTimeStretcher.h0000644000000000000000000000013215124701674021033 xustar0030 mtime=1767080892.805263403 30 atime=1767080892.805263403 30 ctime=1767080892.805263403 qtractor-1.5.11/src/qtractorWsolaTimeStretcher.h0000644000175000001440000002246615124701674021035 0ustar00rncbcusers// qtractorWsolaTimeStretcher.h // /**************************************************************************** Copyright (C) 2005-2025, rncbc aka Rui Nuno Capela. All rights reserved. Adapted and refactored from the SoundTouch library (L)GPL, Copyright (C) 2001-2012, Olli Parviainen. 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 __qtractorWsolaTimeStretcher_h #define __qtractorWsolaTimeStretcher_h #include #include #include //--------------------------------------------------------------------------- // qtractorWsolaTimeStretcher - Time-stretch (tempo change) processed sound. // class qtractorWsolaTimeStretcher { public: // Constructor. qtractorWsolaTimeStretcher( unsigned short iChannels = 2, unsigned int iSampleRate = 44100); // Destructor. ~qtractorWsolaTimeStretcher(); // Sets the number of channels, 1=mono, 2=stereo. void setChannels(unsigned short iChannels); // Get the assigned number of channels, 1=mono, 2=stereo. unsigned short channels() const; // Sets new target tempo; smaller values represent // slower tempo, larger faster tempo. void setTempo(float fTempo); // Get assigned target tempo. float tempo() const; // Set quick-seek mode (hierachical search). void setQuickSeek(bool bQuickSeek); // Get quick-seek mode. bool isQuickSeek() const; // Default values for sound processing parameters. enum { // Default length of a single processing sequence, in milliseconds. // This determines to how long sequences the original sound is // chopped in the time-stretch algorithm. // // The larger this value is, the lesser number of sequences are used // in processing. In principle a bigger value sounds better when // slowing down tempo, but worse when increasing tempo and vice versa. // // Increasing this value reduces computational burden and vice versa. // DEFAULT_SEQUENCE_MS = 40 DEFAULT_SEQUENCE_MS = 0, // Seeking window default length in milliseconds for algorithm // that finds the best possible overlapping location. This determines // from how wide window the algorithm may look for an optimal joining // location when mixing the sound sequences back together. // // The bigger this window setting is, the higher the possibility // to find a better mixing position will become, but at the same time // large values may cause a "drifting" artifact because consequent // sequences will be taken at more uneven intervals. // // If there's a disturbing artifact that sounds as if a constant // frequency was drifting around, try reducing this setting. // // Increasing this value increases computational burden and vice versa. // DEFAULT_SEEKWINDOW_MS = 15 DEFAULT_SEEKWINDOW_MS = 0, // Overlap length in milliseconds. When the chopped sound sequences // are mixed back together, to form a continuous sound stream, // this parameter defines over how long period the two consecutive // sequences are let to overlap each other. // // This shouldn't be that critical parameter. If you reduce the // DEFAULT_SEQUENCE_MS setting by a large amount, you might wish // to try a smaller value on this. // // Increasing this value increases computational burden and vice versa. DEFAULT_OVERLAP_MS = 8 }; // Sets routine control parameters. // These control are certain time constants defining // how the sound is stretched to the desired duration. // // iSampleRate = sample rate of the sound. // iSequenceMS = one processing sequence length in milliseconds. // iSeekWindowMs = seking window length for scanning the best // verlapping position. // iOverlapMs = oerlapping length. void setParameters( unsigned int iSampleRate, unsigned int iSequenceMs = DEFAULT_SEQUENCE_MS, unsigned int iSeekWindowMs = DEFAULT_SEEKWINDOW_MS, unsigned int iOverlapMs = DEFAULT_OVERLAP_MS); // Get routine control parameters, see setParameters() function. // Any of the parameters to this function can be nullptr in such // case corresponding parameter value isn't returned. void getParameters( unsigned int *piSampleRate, unsigned int *piSequenceMs, unsigned int *piSeekWindowMs, unsigned int *piOverlapMs); // Adds frames of samples into the input buffer. void putFrames(float **ppFrames, unsigned int iFrames); // Output frames from beginning of the sample buffer. // Copies requested frames output buffer and removes them // from the sample buffer. If there are less than frames() // samples in the buffer, returns all that available. unsigned int receiveFrames(float **ppFrames, unsigned int iFrames); // Returns number of frames currently available. unsigned int frames() const; // Flush any last samples that are hiding in the internal processing pipeline. void flushInput(); // Clears the input buffer void clearInput(); // Clears all buffers. void clear(); //---------------------------------------------------------------------- // class qtractorWsolaTimeStretcher::FifoBuffer -- FIFO buffer/cache template declaration. // class FifoBuffer { public: // Constructor. FifoBuffer(unsigned short iChannels = 2); // Destructor. ~FifoBuffer(); // Implementation initializer. void setChannels(unsigned short iChannels); // Implementation properties. unsigned short channels() const { return m_iChannels; } unsigned int bufferSize() const { return m_iBufferSize; } // Write samples/frames to the end of sample frame buffer. unsigned int writeFrames( float **ppFrames, unsigned int iFrames, unsigned int iOffset = 0); // Adjusts the book-keeping to increase number of frames // in the buffer without copying any actual frames. void putFrames( float **ppFrames, unsigned int iFrames, unsigned iOffset = 0); void putFrames(unsigned int iFrames); // Read frames from beginning of the sample buffer. unsigned int readFrames( float **ppFrames, unsigned int iFrames, unsigned int iOffset = 0) const; // Adjusts book-keeping so that given number of frames are removed // from beginning of the sample buffer without copying them anywhere. unsigned int receiveFrames( float **ppFrames, unsigned int iFrames, unsigned iOffset = 0); unsigned int receiveFrames(unsigned int iFrames); // Returns number of frames currently available. unsigned int frames() const { return m_iFrameCount; } // Returns a pointer to the beginning of the output samples. float *ptrBegin(unsigned short iChannel) const { return m_ppBuffer[iChannel] + m_iFramePos; } // Returns a pointer to the end of the used part of the sample buffer. float *ptrEnd(unsigned short iChannel) const { return m_ppBuffer[iChannel] + m_iFramePos + m_iFrameCount; } // Returns nonzero if there aren't any frames available. bool isEmpty() const { return (m_iFrameCount == 0); } // Clears all the buffers. void clear() { m_iFrameCount = m_iFramePos = 0; } // Ensures that the buffer has capacity for at least this many frames. void ensureCapacity(unsigned int iCapacity); protected: // Destroy all current allocated buffers. void clearFifoBuffer(); private: // Buffer instance variables. unsigned short m_iChannels; float **m_ppBuffer; float **m_ppBufferUnaligned; unsigned int m_iBufferSize; unsigned int m_iFrameCount; unsigned int m_iFramePos; }; protected: // Calculates processing sequence length according to tempo setting. void calcSeekWindowLength(); // Calculates overlap period length in frames. void calcOverlapLength(); // Seeks for the optimal overlap-mixing position. unsigned int seekBestOverlapPosition(); // Slopes the amplitude of the mid-buffer samples. void calcCrossCorrReference(); // Clears mid sample frame buffer. void clearMidBuffer(); // Changes the tempo of the given sound sample frames. // Returns amount of framees returned in the output buffer. void processFrames(); private: unsigned short m_iChannels; float m_fTempo; bool m_bQuickSeek; unsigned int m_iSampleRate; unsigned int m_iSequenceMs; unsigned int m_iSeekWindowMs; unsigned int m_iOverlapMs; bool m_bAutoSequenceMs; bool m_bAutoSeekWindowMs; unsigned int m_iFramesReq; float **m_ppMidBuffer; float **m_ppRefMidBuffer; float **m_ppRefMidBufferUnaligned; float **m_ppFrames; unsigned int m_iOverlapLength; unsigned int m_iSeekLength; unsigned int m_iSeekWindowLength; float m_fNominalSkip; float m_fSkipFract; FifoBuffer m_outputBuffer; FifoBuffer m_inputBuffer; bool m_bMidBufferDirty; // Calculates the cross-correlation value over the overlap period. float (*m_pfnCrossCorr)(const float *, const float *, unsigned int); }; #endif // __qtractorWsolaTimeStretcher_h // end of qtractorWsolaTimeStretcher.h qtractor-1.5.11/src/PaxHeaders/qtractor.qrc0000644000000000000000000000013215124701674015660 xustar0030 mtime=1767080892.777862586 30 atime=1767080892.777862586 30 ctime=1767080892.777862586 qtractor-1.5.11/src/qtractor.qrc0000644000175000001440000001446715124701674015664 0ustar00rncbcusers images/qtractor.png images/qtractor.svg images/qtractorConnections.png images/qtractorConnections.svg images/qtractorMidiEditor.png images/qtractorMidiEditor.svg images/qtractorMixer.png images/qtractorMixer.svg images/qtractorPlugin.png images/qtractorPlugin.svg images/qtractorTracks.png images/clipEdit.png images/clipNew.png images/clipRecord.png images/editCopy.png images/editCut.png images/editDelete.png images/editModeDraw.png images/editModeOff.png images/editModeOn.png images/editPaste.png images/editRedo.png images/editSelectClip.png images/editSelectRange.png images/editSelectRect.png images/editSelectCurve.png images/editUndo.png images/fadeIn.png images/fadeOut.png images/fileNew.png images/fileOpen.png images/fileSave.png images/formAccept.png images/formAdd.png images/formColor.png images/formConnect.png images/formCreate.png images/formDisconnect.png images/formDisconnectAll.png images/formEdit.png images/formMoveDown.png images/formMoveUp.png images/formOpen.png images/formRefresh.png images/formReject.png images/formRemove.png images/formSave.png images/helpShortcuts.png images/itemAudioClientIn.png images/itemAudioClientOut.png images/itemAudioFile.png images/itemAudioPortIn.png images/itemAudioPortOut.png images/itemAudioPortPhysIn.png images/itemAudioPortPhysOut.png images/itemBeat.png images/itemCdUp.png images/itemChannel.png images/itemControllers.png images/itemFile.png images/itemGroup.png images/itemGroupOpen.png images/itemHome.png images/itemInstrument.png images/itemLedOff.png images/itemLedOn.png images/itemLedDim.png images/itemMidiClientIn.png images/itemMidiClientOut.png images/itemMidiFile.png images/itemMidiPortIn.png images/itemMidiPortOut.png images/itemNone.png images/itemNotes.png images/itemNrpns.png images/itemPatches.png images/itemProperty.png images/itemClear.png images/itemReset.png images/itemRpns.png images/itemSessionFile.png images/pluginEdit.png images/pluginProperties.png images/pluginSelect.png images/trackAdd.png images/trackAudio.png images/trackCurveCapture.png images/trackCurveEnabled.png images/trackCurveNone.png images/trackCurveProcess.png images/trackIconBass1.png images/trackIconBass2.png images/trackIconDrums1.png images/trackIconDrums2.png images/trackIconGuitar1.png images/trackIconGuitar2.png images/trackIconMicrophone1.png images/trackIconMicrophone2.png images/trackIconPiano1.png images/trackIconPiano2.png images/trackIconSpeaker1.png images/trackIconTrumpet1.png images/trackIconViolin1.png images/trackMidi.png images/trackMidiOff.png images/trackMidiOn.png images/trackProperties.png images/trackRemove.png images/transportAutoBackward.png images/transportBackward.png images/transportContinue.png images/transportCountIn.png images/transportFastForward.png images/transportFollow.png images/transportForward.png images/transportLoop.png images/transportMetro.png images/transportModeNone.png images/transportModeSlave.png images/transportModeMaster.png images/transportModeFull.png images/transportPause.png images/transportPanic.png images/transportPlay.png images/transportPunch.png images/transportRecord.png images/transportRewind.png images/transportStop.png images/viewConnections.png images/viewDrumMode.png images/viewEvents.png images/viewFiles.png images/viewFileSystem.png images/viewMessages.png images/viewMixer.png images/viewPreview.png images/viewZoomIn.png images/viewZoomOut.png images/viewZoomReset.png images/viewZoomTool.png qtractor-1.5.11/src/PaxHeaders/qtractorRubberBand.h0000644000000000000000000000013215124701674017251 xustar0030 mtime=1767080892.799263428 30 atime=1767080892.799263428 30 ctime=1767080892.799263428 qtractor-1.5.11/src/qtractorRubberBand.h0000644000175000001440000000414215124701674017242 0ustar00rncbcusers// qtractorRubberBand.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorRubberBand_h #define __qtractorRubberBand_h #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include #else #include class QWindowsStyle : public QCommonStyle {}; #endif //---------------------------------------------------------------------------- // qtractorRubberBand -- Custom rubber-band widget. class qtractorRubberBand : public QRubberBand { public: // Constructor. qtractorRubberBand(Shape shape, QWidget *widget = nullptr, int thick = 1); // Destructor. ~qtractorRubberBand(); // Rubberband thickness accessor. void setThickness(int thick); int thickness() const; private: // qtractorRubberBandStyle -- Custom rubber-band style. class Style : public QWindowsStyle { public: // Constructor. Style(int thick) : QWindowsStyle(), thickness(thick) {} // Custom virtual override. int styleHint( StyleHint sh, const QStyleOption *opt = 0, const QWidget *widget = 0, QStyleHintReturn *hint = 0 ) const; // Rubberband thickness. int thickness; }; // Local style instance Style *m_pStyle; }; #endif // __qtractorRubberBand_h // end of qtractorRubberBand.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiMeter.cpp0000644000000000000000000000013215124701674017455 xustar0030 mtime=1767080892.794263449 30 atime=1767080892.794263449 30 ctime=1767080892.794263449 qtractor-1.5.11/src/qtractorMidiMeter.cpp0000644000175000001440000004122315124701674017447 0ustar00rncbcusers// qtractorMidiMeter.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorMidiMeter.h" #include "qtractorMidiMonitor.h" #include "qtractorAudioMeter.h" #include "qtractorAudioMonitor.h" #include "qtractorObserverWidget.h" #include #include #include #include #include #include // The decay rates (magic goes here :). // - value decay rate (faster) #define QTRACTOR_MIDI_METER_DECAY_RATE1 (1.0f - 1E-5f) // - peak decay rate (slower) #define QTRACTOR_MIDI_METER_DECAY_RATE2 (1.0f - 1E-6f) // Number of cycles the peak stays on hold before fall-off. #define QTRACTOR_MIDI_METER_PEAK_FALLOFF 16 // Number of cycles the MIDI LED stays on before going off. #define QTRACTOR_MIDI_METER_HOLD_LEDON 4 // MIDI On/Off LED pixmap resource. int qtractorMidiMeterLed::g_iLedRefCount = 0; QPixmap *qtractorMidiMeterLed::g_pLedPixmap[qtractorMidiMeterLed::LedCount]; // MIDI meter color arrays. QColor qtractorMidiMeter::g_defaultColors[ColorCount] = { QColor(160,220, 20), // ColorPeak QColor(160,160, 40), // ColorOver QColor( 20, 40, 20), // ColorBack QColor( 80, 80, 80) // ColorFore }; QColor qtractorMidiMeter::g_currentColors[ColorCount] = { g_defaultColors[ColorPeak], g_defaultColors[ColorOver], g_defaultColors[ColorBack], g_defaultColors[ColorFore] }; //---------------------------------------------------------------------------- // qtractorMidiMeterScale -- Meter bridge scale widget. // Constructor. qtractorMidiMeterScale::qtractorMidiMeterScale ( qtractorMidiMeter *pMidiMeter ) : qtractorMeterScale(pMidiMeter) { // Nothing much to do... } // Actual scale drawing method. void qtractorMidiMeterScale::paintScale ( QPainter *pPainter ) { qtractorMidiMeter *pMidiMeter = static_cast (meter()); if (pMidiMeter == nullptr) return; const int h = QWidget::height() - 4; const int d = (h / 5); int y = h; int n = 100; while (y > 0) { drawLineLabel(pPainter, y, QString::number(n)); y -= d; n -= 20; } } //---------------------------------------------------------------------------- // qtractorMidiMeterValue -- MIDI meter bridge value widget. // Constructor. qtractorMidiMeterValue::qtractorMidiMeterValue ( qtractorMidiMeter *pMidiMeter ) : qtractorMeterValue(pMidiMeter) { // Avoid intensively annoying repaints... QWidget::setAttribute(Qt::WA_StaticContents); QWidget::setAttribute(Qt::WA_OpaquePaintEvent); QWidget::setBackgroundRole(QPalette::NoRole); m_iValue = 0; m_fValueDecay = QTRACTOR_MIDI_METER_DECAY_RATE1; m_iPeak = 0; m_iPeakHold = 0; m_fPeakDecay = QTRACTOR_MIDI_METER_DECAY_RATE2; QWidget::setMinimumWidth(2); QWidget::setMaximumWidth(14); } // Value refreshment. void qtractorMidiMeterValue::refresh ( unsigned long iStamp ) { qtractorMidiMeter *pMidiMeter = static_cast (meter()); if (pMidiMeter == nullptr) return; qtractorMidiMonitor *pMidiMonitor = pMidiMeter->midiMonitor(); if (pMidiMonitor == nullptr) return; const float fValue = pMidiMonitor->value_stamp(iStamp); if (fValue < 0.001f && m_iPeak < 1) return; int iValue = pMidiMeter->scale(fValue); if (iValue < m_iValue) { iValue = int(m_fValueDecay * float(m_iValue)); m_fValueDecay *= m_fValueDecay; } else { m_fValueDecay = QTRACTOR_MIDI_METER_DECAY_RATE1; } int iPeak = m_iPeak; if (iPeak < iValue) { iPeak = iValue; m_iPeakHold = 0; m_fPeakDecay = QTRACTOR_MIDI_METER_DECAY_RATE2; } else if (++m_iPeakHold > pMidiMeter->peakFalloff()) { iPeak = int(m_fPeakDecay * float(iPeak)); if (iPeak < iValue) { iPeak = iValue; } else { m_fPeakDecay *= m_fPeakDecay; } } if (iValue == m_iValue && iPeak == m_iPeak) return; m_iValue = iValue; m_iPeak = iPeak; update(); } // Paint event handler. void qtractorMidiMeterValue::paintEvent ( QPaintEvent * ) { qtractorMidiMeter *pMidiMeter = static_cast (meter()); if (pMidiMeter == nullptr) return; QPainter painter(this); const int w = QWidget::width(); const int h = QWidget::height(); if (isEnabled()) { painter.fillRect(0, 0, w, h, pMidiMeter->color(qtractorMidiMeter::ColorBack)); } else { painter.fillRect(0, 0, w, h, Qt::gray); } painter.drawPixmap(0, h - m_iValue, pMidiMeter->pixmap(), 0, h - m_iValue, w, m_iValue); painter.setPen(pMidiMeter->color(qtractorMidiMeter::ColorPeak)); painter.drawLine(0, h - m_iPeak, w, h - m_iPeak); } // Resize event handler. void qtractorMidiMeterValue::resizeEvent ( QResizeEvent *pResizeEvent ) { m_iPeak = 0; qtractorMeterValue::resizeEvent(pResizeEvent); } //---------------------------------------------------------------------------- // qtractorMidiMeterLed -- MIDI meter bridge LED widget. // Constructor. qtractorMidiMeterLed::qtractorMidiMeterLed ( qtractorMidiMeter *pMidiMeter ) : qtractorMeterValue(pMidiMeter) { if (++g_iLedRefCount == 1) { g_pLedPixmap[LedOff] = new QPixmap(QIcon::fromTheme("trackMidiOff").pixmap(16, 16)); g_pLedPixmap[LedOn] = new QPixmap(QIcon::fromTheme("trackMidiOn").pixmap(16, 16)); } m_iMidiCount = 0; m_pMidiLabel = new QLabel(); m_pMidiLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); m_pMidiLabel->setPixmap(*g_pLedPixmap[LedOff]); QHBoxLayout *pHBoxLayout = new QHBoxLayout(); pHBoxLayout->setContentsMargins(0, 0, 0, 0); pHBoxLayout->setSpacing(0); pHBoxLayout->addWidget(m_pMidiLabel); qtractorMeterValue::setLayout(pHBoxLayout); } // Default destructor. qtractorMidiMeterLed::~qtractorMidiMeterLed (void) { delete m_pMidiLabel; if (--g_iLedRefCount == 0) { delete g_pLedPixmap[LedOff]; delete g_pLedPixmap[LedOn]; } } // Value refreshment. void qtractorMidiMeterLed::refresh ( unsigned long iStamp ) { qtractorMidiMeter *pMidiMeter = static_cast (meter()); if (pMidiMeter == nullptr) return; qtractorMidiMonitor *pMidiMonitor = pMidiMeter->midiMonitor(); if (pMidiMonitor == nullptr) return; // Take care of the MIDI LED status... const bool bMidiOn = (pMidiMonitor->count_stamp(iStamp) > 0); if (bMidiOn) { if (m_iMidiCount == 0) m_pMidiLabel->setPixmap(*g_pLedPixmap[LedOn]); m_iMidiCount = QTRACTOR_MIDI_METER_HOLD_LEDON; } else if (m_iMidiCount > 0) { if (--m_iMidiCount == 0) m_pMidiLabel->setPixmap(*g_pLedPixmap[LedOff]); } } //---------------------------------------------------------------------------- // qtractorMidiMeter -- MIDI meter bridge slot widget. // Constructor. qtractorMidiMeter::qtractorMidiMeter ( qtractorMidiMonitor *pMidiMonitor, QWidget *pParent ) : qtractorMeter(pParent) { m_pMidiMonitor = pMidiMonitor; m_pMidiValue = new qtractorMidiMeterValue(this); boxLayout()->addWidget(m_pMidiValue); setPeakFalloff(QTRACTOR_MIDI_METER_PEAK_FALLOFF); reset(); } // Default destructor. qtractorMidiMeter::~qtractorMidiMeter (void) { // No need to delete child widgets, Qt does it all for us delete m_pMidiValue; } // MIDI monitor reset void qtractorMidiMeter::reset (void) { // Nothing really to do? } // Pixmap accessors. const QPixmap& qtractorMidiMeter::pixmap (void) const { return m_pixmap; } void qtractorMidiMeter::updatePixmap (void) { const int w = QWidget::width(); const int h = QWidget::height(); m_pixmap = QPixmap(w, h); #if 1//def CONFIG_GRADIENT QLinearGradient grad(0, 0, 0, h); grad.setColorAt(0.0f, color(ColorPeak)); grad.setColorAt(0.4f, color(ColorOver)); QPainter(&m_pixmap).fillRect(0, 0, w, h, grad); #else QPainter(&m_pixmap).fillRect(0, 0, w, h, color(ColorOver)); #endif } // Resize event handler. void qtractorMidiMeter::resizeEvent ( QResizeEvent *pResizeEvent ) { qtractorMeter::setScale(float(QWidget::height())); updatePixmap(); qtractorMeter::resizeEvent(pResizeEvent); } // Virtual monitor accessor. void qtractorMidiMeter::setMonitor ( qtractorMonitor *pMonitor ) { setMidiMonitor(static_cast (pMonitor)); } qtractorMonitor *qtractorMidiMeter::monitor (void) const { return midiMonitor(); } // MIDI monitor accessor. void qtractorMidiMeter::setMidiMonitor ( qtractorMidiMonitor *pMidiMonitor ) { m_pMidiMonitor = pMidiMonitor; reset(); } qtractorMidiMonitor *qtractorMidiMeter::midiMonitor (void) const { return m_pMidiMonitor; } // Common resource accessor. void qtractorMidiMeter::setColor ( int iIndex, const QColor& color ) { g_currentColors[iIndex] = color; } const QColor& qtractorMidiMeter::color ( int iIndex ) { return g_currentColors[iIndex]; } const QColor& qtractorMidiMeter::defaultColor ( int iIndex ) { return g_defaultColors[iIndex]; } //---------------------------------------------------------------------------- // qtractorMidiComboMeter -- MIDI meter combo widget. // Constructor. qtractorMidiComboMeter::qtractorMidiComboMeter ( qtractorMidiMonitor *pMidiMonitor, QWidget *pParent ) : QWidget(pParent) { m_pMidiMeter = new qtractorMidiMeter(pMidiMonitor); m_pAudioMeter = nullptr; QHBoxLayout *pHBoxLayout = new QHBoxLayout(); pHBoxLayout->setContentsMargins(0, 0, 0, 0); pHBoxLayout->setSpacing(2); pHBoxLayout->addWidget(m_pMidiMeter); QWidget::setLayout(pHBoxLayout); reset(); } // Default destructor. qtractorMidiComboMeter::~qtractorMidiComboMeter (void) { if (m_pAudioMeter) { // Take care of those audio meters on MIDI tracks... qtractorAudioOutputMonitor *pAudioOutputMonitor = static_cast (m_pAudioMeter->audioMonitor()); if (pAudioOutputMonitor) pAudioOutputMonitor->removeAudioMeter(m_pAudioMeter); // We're probably good now... //delete m_pAudioMeter; } //delete m_pMidiMeter; // No need to delete child widgets, Qt does it all for us } // Regular MIDI meter accessor. qtractorMidiMeter *qtractorMidiComboMeter::midiMeter (void) const { return m_pMidiMeter; } // Combined audio-output meter accessor. qtractorAudioMeter *qtractorMidiComboMeter::audioMeter (void) const { return m_pAudioMeter; } // MIDI monitor reset void qtractorMidiComboMeter::reset (void) { qtractorMidiMonitor *pMidiMonitor = m_pMidiMeter->midiMonitor(); if (pMidiMonitor == nullptr) return; m_pMidiMeter->reset(); if (m_pAudioMeter) m_pAudioMeter->reset(); } // Virtual monitor accessor. void qtractorMidiComboMeter::setMonitor ( qtractorMonitor *pMonitor ) { m_pMidiMeter->setMonitor(pMonitor); } qtractorMonitor *qtractorMidiComboMeter::monitor (void) const { return m_pMidiMeter->monitor(); } // MIDI monitor accessor. void qtractorMidiComboMeter::setMidiMonitor ( qtractorMidiMonitor *pMidiMonitor ) { m_pMidiMeter->setMidiMonitor(pMidiMonitor); reset(); } qtractorMidiMonitor *qtractorMidiComboMeter::midiMonitor (void) const { return m_pMidiMeter->midiMonitor(); } // Audio-output monitor accessor. void qtractorMidiComboMeter::setAudioOutputMonitor ( qtractorAudioOutputMonitor *pAudioOutputMonitor ) { if (m_pAudioMeter) { qtractorAudioOutputMonitor *pOldAudioOutputMonitor = static_cast (m_pAudioMeter->audioMonitor()); if (pOldAudioOutputMonitor) pOldAudioOutputMonitor->removeAudioMeter(m_pAudioMeter); m_pAudioMeter->setAudioMonitor(pAudioOutputMonitor); if (pAudioOutputMonitor) pAudioOutputMonitor->addAudioMeter(m_pAudioMeter); } else if (pAudioOutputMonitor) { m_pAudioMeter = new qtractorAudioMeter(pAudioOutputMonitor); m_pAudioMeter->setScale0dB(0.98f); // HACK: Override the 0dBfs notch. pAudioOutputMonitor->addAudioMeter(m_pAudioMeter); QWidget::layout()->addWidget(m_pAudioMeter); m_pAudioMeter->show(); } reset(); } qtractorAudioOutputMonitor *qtractorMidiComboMeter::audioOutputMonitor (void) const { if (m_pAudioMeter) return static_cast (m_pAudioMeter->audioMonitor()); else return nullptr; } // Resize event handler. void qtractorMidiComboMeter::resizeEvent ( QResizeEvent *pResizeEvent ) { QWidget::resizeEvent(pResizeEvent); } //---------------------------------------------------------------------- // class qtractorMidiMixerMeter::GainSpinBoxInterface -- Observer interface. // // Local converter interface. class qtractorMidiMixerMeter::GainSpinBoxInterface : public qtractorObserverSpinBox::Interface { public: // Formerly Pure virtuals. float scaleFromValue ( float fValue ) const { return 100.0f * fValue; } float valueFromScale ( float fScale ) const { return 0.01f * fScale; } }; //---------------------------------------------------------------------- // class qtractorMidiMixerMeter::GainSliderInterface -- Observer interface. // // Local converter interface. class qtractorMidiMixerMeter::GainSliderInterface : public qtractorObserverSlider::Interface { public: // Formerly Pure virtuals. float scaleFromValue ( float fValue ) const { return 10000.0f * fValue; } float valueFromScale ( float fScale ) const { return 0.0001f * fScale; } }; //---------------------------------------------------------------------------- // qtractorMidiMixerMeter -- MIDI mixer-strip meter bridge widget. // Constructor. qtractorMidiMixerMeter::qtractorMidiMixerMeter ( qtractorMidiMonitor *pMidiMonitor, QWidget *pParent ) : qtractorMixerMeter(pParent) { m_pMidiMeter = new qtractorMidiComboMeter(pMidiMonitor); m_pMidiScale = new qtractorMidiMeterScale(m_pMidiMeter->midiMeter()); m_pMidiLed = new qtractorMidiMeterLed(m_pMidiMeter->midiMeter()); topLayout()->addStretch(); topLayout()->addWidget(m_pMidiLed); topLayout()->addSpacing(2); boxLayout()->addWidget(m_pMidiScale); boxLayout()->addWidget(m_pMidiMeter); gainSlider()->setInterface(new GainSliderInterface()); gainSpinBox()->setInterface(new GainSpinBoxInterface()); gainSpinBox()->setMinimum(0.0f); gainSpinBox()->setMaximum(100.0f); gainSpinBox()->setToolTip(tr("Volume (%)")); gainSpinBox()->setSuffix(tr(" %")); reset(); updatePanning(); updateGain(); } // Default destructor. qtractorMidiMixerMeter::~qtractorMidiMixerMeter (void) { delete m_pMidiLed; delete m_pMidiScale; delete m_pMidiMeter; // No need to delete child widgets, Qt does it all for us } // MIDI monitor reset void qtractorMidiMixerMeter::reset (void) { qtractorMidiMonitor *pMidiMonitor = m_pMidiMeter->midiMonitor(); if (pMidiMonitor == nullptr) return; m_pMidiMeter->reset(); setPanningSubject(pMidiMonitor->panningSubject()); setGainSubject(pMidiMonitor->gainSubject()); } // Virtual monitor accessor. void qtractorMidiMixerMeter::setMonitor ( qtractorMonitor *pMonitor ) { m_pMidiMeter->setMonitor(pMonitor); } qtractorMonitor *qtractorMidiMixerMeter::monitor (void) const { return m_pMidiMeter->monitor(); } // MIDI monitor accessor. void qtractorMidiMixerMeter::setMidiMonitor ( qtractorMidiMonitor *pMidiMonitor ) { m_pMidiMeter->setMidiMonitor(pMidiMonitor); reset(); } qtractorMidiMonitor *qtractorMidiMixerMeter::midiMonitor (void) const { return m_pMidiMeter->midiMonitor(); } // Audio-output monitor accessor. void qtractorMidiMixerMeter::setAudioOutputMonitor ( qtractorAudioOutputMonitor *pAudioOutputMonitor ) { m_pMidiMeter->setAudioOutputMonitor(pAudioOutputMonitor); reset(); } qtractorAudioOutputMonitor *qtractorMidiMixerMeter::audioOutputMonitor (void) const { return m_pMidiMeter->audioOutputMonitor(); } // Pan-slider value change method. void qtractorMidiMixerMeter::updatePanning (void) { // setPanning(m_pMidiMonitor->panning()); panSlider()->setToolTip( tr("Pan: %1").arg(panning(), 0, 'g', 1)); } // Gain-slider value change method. void qtractorMidiMixerMeter::updateGain (void) { // setGain(m_pMidiMonitor->gain()); gainSlider()->setToolTip( tr("Volume: %1%").arg(gainSpinBox()->value(), 0, 'g', 3)); } // Resize event handler. void qtractorMidiMixerMeter::resizeEvent ( QResizeEvent *pResizeEvent ) { // HACK: make so that the MIDI gain slider (volume) // aligns its top at the Audio 0 dB gain level... int iMinHeight = int(0.15f * float(m_pMidiMeter->height())) - 4; if (iMinHeight < 16) iMinHeight = 16; topWidget()->setMinimumHeight(iMinHeight); qtractorMixerMeter::resizeEvent(pResizeEvent); } // end of qtractorMidiMeter.cpp qtractor-1.5.11/src/PaxHeaders/qtractorTrack.h0000644000000000000000000000013215124701674016307 xustar0030 mtime=1767080892.802263416 30 atime=1767080892.802263416 30 ctime=1767080892.802263416 qtractor-1.5.11/src/qtractorTrack.h0000644000175000001440000003231315124701674016301 0ustar00rncbcusers// qtractorTrack.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorTrack_h #define __qtractorTrack_h #include "qtractorList.h" #include "qtractorMidiControl.h" #include // Forward declarations. class qtractorSession; class qtractorDocument; class qtractorInstrumentList; class qtractorPluginList; class qtractorMonitor; class qtractorClip; class qtractorBus; class qtractorSubject; class qtractorMidiControlObserver; class qtractorAudioBufferThread; class qtractorCurveList; class qtractorCurveFile; class qtractorCurve; // Special forward declarations. class QDomElement; class QPainter; class QRect; //------------------------------------------------------------------------- // qtractorTrack -- Track container. class qtractorTrack : public qtractorList::Link { public: // Track type symbology. enum TrackType { None = 0, Audio, Midi }; // Tool button specific types: enum ToolType { Record, Mute, Solo }; // Constructor. qtractorTrack(qtractorSession *pSession, TrackType trackType = None); // Default constructor. ~qtractorTrack(); // Reset track. void clear(); // Track open/close methods. bool open(); void close(); // Session accessor. qtractorSession *session() const; // Track name accessors. void setTrackName(const QString& sTrackName); const QString& trackName() const; QString shortTrackName() const; void updateTrackName(); static QString shortTrackName(const QString& sTrackName); // Track icon (filename) accessors. void setTrackIcon(const QString& sTrackIcon); const QString& trackIcon() const; // Track type accessors. void setTrackType(TrackType trackType); TrackType trackType() const; // Record monitoring state accessors. void setMonitor(bool bMonitor); bool isMonitor() const; // Record status accessors. void setRecord(bool bRecord); bool isRecord() const; // Mute status accessors. void setMute(bool bMute); bool isMute() const; // Solo status accessors. void setSolo(bool bSolo); bool isSolo() const; // Track gain (volume) accessor. void setGain(float fGain); float gain() const; float prevGain() const; // Track stereo-panning accessor. void setPanning(float fPanning); float panning() const; float prevPanning() const; // MIDI specific: track-tag accessors. void setMidiTag(unsigned short iMidiTag); unsigned short midiTag() const; // MIDI specific: omni (capture) mode acessors. void setMidiOmni(bool bMidiOmni); bool isMidiOmni() const; // MIDI specific: channel acessors. void setMidiChannel(unsigned short iMidiChannel); unsigned short midiChannel() const; // MIDI specific: bank select method acessors (optional). void setMidiBankSelMethod(int iMidiBankSelMethod); int midiBankSelMethod() const; // MIDI specific: bank acessors (optional). void setMidiBank(int iMidiBank); int midiBank() const; // MIDI specific: program acessors (optional). void setMidiProg(int iMidiProg); int midiProg() const; // MIDI drum mode (UI). void setMidiDrums(bool bMidiDrums); bool isMidiDrums() const; // MIDI specific: track-minimum note. void setMidiNoteMin(unsigned char note); unsigned char midiNoteMin() const; // MIDI specific: track-maximum note. void setMidiNoteMax(unsigned char note); unsigned char midiNoteMax() const; // MIDI specific volume controller. void setMidiVolume(unsigned char vol, bool bUpdate = false); unsigned char midiVolume() const; // MIDI specific panning controller. void setMidiPanning(unsigned char pan, bool bUpdate = false); unsigned char midiPanning() const; // Assigned bus name accessors. void setInputBusName(const QString& sBusName); const QString& inputBusName() const; void setOutputBusName(const QString& sBusName); const QString& outputBusName() const; // Assigned bus accessors. qtractorBus *inputBus() const; qtractorBus *outputBus() const; // Track monitor accessors. qtractorMonitor *monitor() const; // Track plugin-chain accessor. qtractorPluginList *pluginList() const; // Plugin latency compensation accessors. void setPluginListLatency(bool bPluginListLatency); bool isPluginListLatency() const; // Base height (in pixels). enum { HeightMin = 24, HeightBase = 96 }; // Normalized view height accessors. void updateHeight(); void setHeight(int iHeight); int height() const; // Visual height accessors. void updateZoomHeight(); void setZoomHeight(int iZoomHeight); int zoomHeight() const; int zoomHeightBase() const; // Visual height minimize/toggle. int minimizeZoomHeight(); // Clip list management methods. const qtractorList& clips() const; void addClip(qtractorClip *pClip); void addClipEx(qtractorClip *pClip); void insertClip(qtractorClip *pClip); void removeClipEx(qtractorClip *pClip); void removeClip(qtractorClip *pClip); // Current clip on record (capture). void setClipRecord(qtractorClip *pClipRecord); qtractorClip *clipRecord() const; // Current clip on record absolute start frame (capture). void setClipRecordStart(unsigned long iClipRecordStart); unsigned long clipRecordStart() const; unsigned long clipRecordEnd(unsigned long iFrameTime) const; void setClipRecordEx(bool bClipRecordEx); bool isClipRecordEx() const; // Background color accessors. void setBackground(const QColor& bg); const QColor& background() const; // Foreground color accessors. void setForeground(const QColor& fg); const QColor& foreground() const; // Generate a default track color. static QColor trackColor(int iTrack); // Track special process cycle executive. void process(qtractorClip *pClip, unsigned long iFrameStart, unsigned long iFrameEnd); // Track freewheeling process cycle executive (needed for export). void process_export(qtractorClip *pClip, unsigned long iFrameStart, unsigned long iFrameEnd); // Track special process record executive (audio recording only). void process_record( unsigned long iFrameStart, unsigned long iFrameEnd); // Track special process automation executive. void process_curve(unsigned long iFrame); // Track paint method. void drawTrack(QPainter *pPainter, const QRect& trackRect, unsigned long iTrackStart, unsigned long iTrackEnd, qtractorClip *pClip = nullptr); // MIDI track instrument patching. void setMidiPatch(qtractorInstrumentList *pInstruments); // Track loop point setler. void setLoop(unsigned long iLoopStart, unsigned long iLoopEnd); // Update all clips editors. void updateClipEditors(); // Audio buffer ring-cache (playlist) methods. qtractorAudioBufferThread *syncThread(); // Track state (monitor, record, mute, solo) button setup. qtractorSubject *monitorSubject() const; qtractorSubject *recordSubject() const; qtractorSubject *muteSubject() const; qtractorSubject *soloSubject() const; qtractorMidiControlObserver *monitorObserver() const; qtractorMidiControlObserver *recordObserver() const; qtractorMidiControlObserver *muteObserver() const; qtractorMidiControlObserver *soloObserver() const; // Track state (monitor) notifier (proto-slot). void monitorChangeNotify(bool bOn); // Track state (record, mute, solo) notifier (proto-slot). void stateChangeNotify(ToolType toolType, bool bOn); // Document element methods. bool loadElement(qtractorDocument *pDocument, QDomElement *pElement); bool saveElement(qtractorDocument *pDocument, QDomElement *pElement) const; // Load/save track state (record, mute, solo) controllers (MIDI). void loadControllers(QDomElement *pElement); void saveControllers(qtractorDocument *pDocument, QDomElement *pElement) const; // Map track state (record, mute, solo) controllers (MIDI). void mapControllers(); // Track automation curve list accessor. qtractorCurveList *curveList() const; // Track automation curve serializer accessor. qtractorCurveFile *curveFile() const; // Track automation current curve accessor. void setCurrentCurve(qtractorCurve *pCurrentCurve); qtractorCurve *currentCurve() const; // Track automation curve serialization methods. static void loadCurveFile( QDomElement *pElement, qtractorCurveFile *pCurveFile); void saveCurveFile(qtractorDocument *pDocument, QDomElement *pElement, qtractorCurveFile *pCurveFile) const; void applyCurveFile(qtractorCurveFile *pCurveFile) const; // Track properties structure. struct Properties { // Default constructor. Properties() { clear(); } // Copy constructor. Properties(const Properties& props) { copy(props); } // Assignment operator, Properties& operator=(const Properties& props) { return copy(props); } // Helper copy method. Properties& copy(const Properties& props); // Helper clear/reset method. void clear(); // Members. QString trackName; QString trackIcon; TrackType trackType; bool monitor; bool record; bool mute; bool solo; float gain; float panning; QString inputBusName; QString outputBusName; bool pluginListLatency; bool midiOmni; unsigned short midiChannel; int midiBankSelMethod; int midiBank; int midiProg; bool midiDrums; QColor foreground; QColor background; }; // Alternate properties accessor. void setProperties(const Properties& props); Properties& properties(); // Reset state properties (as needed on copy/duplicate) void resetProperties(); // Track type textual helper methods. static TrackType trackTypeFromText (const QString& sText); static QString textFromTrackType (TrackType trackType); // Take(record) descriptor/id registry methods. class TakeInfo; TakeInfo *takeInfo(int iTakeID) const; int takeInfoId(TakeInfo *pTakeInfo) const; int takeInfoNew(TakeInfo *pTakeInfo) const; void takeInfoAdd(int iTakeID, TakeInfo *pTakeInfo) const; void clearTakeInfo() const; // Update tracks/list-view. void updateTrack(); void updateMidiTrack(); void updateMidiClips(); // Update all plugin forms, if visible. void refreshPluginForms(); // Default track color saturation factor [0..500]. static void setTrackColorSaturation(int iTrackColorSaturation); static int trackColorSaturation(); private: qtractorSession *m_pSession; // Session reference. Properties m_props; // Track properties. qtractorBus *m_pInputBus; // Track assigned input bus. qtractorBus *m_pOutputBus; // Track assigned input bus. qtractorMonitor *m_pMonitor; // Track monitor. unsigned short m_iMidiTag; // MIDI specific: track-tag; unsigned char m_midiNoteMax; // MIDI specific: track-maximum note; unsigned char m_midiNoteMin; // MIDI specific: track-minimum note. unsigned char m_midiVolume; // MIDI specific: track-volume; unsigned char m_midiPanning; // MIDI specific: track-panning. int m_iHeight; // View height (normalized). int m_iHeightBase; // View height (base). int m_iZoomHeight; // View height (zoomed). int m_iZoomHeightBase; // View height (minimize/restore). qtractorList m_clips; // List of clips. qtractorClip *m_pClipRecord; // Current clip on record (capture). unsigned long m_iClipRecordStart; // Current clip on record start frame. bool m_bClipRecordEx; // Current clip on record/overdub flag. qtractorPluginList *m_pPluginList; // Plugin chain (audio). // Audio buffer ring-cache (playlist). qtractorAudioBufferThread *m_pSyncThread; // MIDI track/channel (volume, panning) observers. class MidiVolumeObserver; class MidiPanningObserver; MidiVolumeObserver *m_pMidiVolumeObserver; MidiPanningObserver *m_pMidiPanningObserver; // State (monitor, record, mute, solo) observer stuff. qtractorMidiControlObserver *m_pMonitorObserver; class StateObserver; StateObserver *m_pRecordObserver; StateObserver *m_pMuteObserver; StateObserver *m_pSoloObserver; qtractorSubject *m_pMonitorSubject; qtractorSubject *m_pRecordSubject; qtractorSubject *m_pMuteSubject; qtractorSubject *m_pSoloSubject; qtractorMidiControl::Controllers m_controllers; qtractorCurveFile *m_pCurveFile; // Take(record) descriptor/id registry. mutable QHash m_idtakes; mutable QHash m_takeids; // MIDI bank/program observer. class MidiProgramObserver; MidiProgramObserver *m_pMidiProgramObserver; // Default track color saturation factor [0..500]. static int g_iTrackColorSaturation; }; #endif // __qtractorTrack_h // end of qtractorTrack.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiThumbView.h0000644000000000000000000000013215124701674017760 xustar0030 mtime=1767080892.795263445 30 atime=1767080892.795263445 30 ctime=1767080892.795263445 qtractor-1.5.11/src/qtractorMidiThumbView.h0000644000175000001440000000534015124701674017752 0ustar00rncbcusers// qtractorMidiThumbView.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorMidiThumbView_h #define __qtractorMidiThumbView_h #include // Forward declarations. class qtractorMidiEditor; class qtractorRubberBand; class QPaintEvent; class QResizeEvent; class QMouseEvent; class QKeyEvent; //------------------------------------------------------------------------- // qtractorMidiThumbView -- Session track line thumb view. class qtractorMidiThumbView : public QFrame { Q_OBJECT public: // Constructor. qtractorMidiThumbView(qtractorMidiEditor *pEditor, QWidget *pParent = nullptr); // Update playhead-position. void updatePlayHead(unsigned long iPlayHead); // (Re)create the complete view pixmap. void updateContents(); public slots: // Update thumb-position. void updateThumb(int dx = 0); protected: // Update view-position. void updateView(int dx); // Set playhead-position (indirect). void setPlayHeadX(int iPlayHeadX); // MIDI clip-line paint method. void paintEvent(QPaintEvent *pPaintEvent); // MIDI clip-line paint method. void resizeEvent(QResizeEvent *pResizeEvent); // Handle selection with mouse. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseMoveEvent(QMouseEvent *pMouseEvent); void mouseReleaseEvent(QMouseEvent *pMouseEvent); // Reset drag state. void resetDragState(); // Keyboard event handler. void keyPressEvent(QKeyEvent *pKeyEvent); private: // LOcal MIDI editor instance. qtractorMidiEditor *m_pEditor; // Local double-buffering pixmap. QPixmap m_pixmap; // Local playhead positioning. int m_iPlayHeadX; // The thumb rubber-band widget. qtractorRubberBand *m_pRubberBand; // Thumb drag-states. enum { DragNone = 0, DragStart, DragMove, DragClick } m_dragState; QPoint m_posDrag; }; #endif // __qtractorMidiThumbView_h // end of qtractorMidiThumbView.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiFile.h0000644000000000000000000000013215124701674016725 xustar0030 mtime=1767080892.794263449 30 atime=1767080892.794263449 30 ctime=1767080892.794263449 qtractor-1.5.11/src/qtractorMidiFile.h0000644000175000001440000000761615124701674016727 0ustar00rncbcusers// qtractorMidiFile.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorMidiFile_h #define __qtractorMidiFile_h #include "qtractorMidiSequence.h" #include "qtractorMidiFileTempo.h" class qtractorTimeScale; //---------------------------------------------------------------------- // class qtractorMidiFile -- A SMF (Standard MIDI File) class. // class qtractorMidiFile { public: // Constructor. qtractorMidiFile(); // Destructor. ~qtractorMidiFile(); // Basic file open mode. enum { None = 0, Read = 1, Write = 2 }; // Open file methods. bool open(const QString& sFilename, int iMode = Read); void close(); // Open file property accessors. const QString& filename() const { return m_sFilename; } int mode() const { return m_iMode; } // Header accessors. unsigned short format() const { return m_iFormat; } unsigned short tracks() const { return m_iTracks; } unsigned short ticksPerBeat() const { return m_iTicksPerBeat; } // Tempo/time-signature map accessor. qtractorMidiFileTempo *tempoMap() const { return m_pTempoMap; } // Sequence/track readers. bool readTracks(qtractorMidiSequence **ppSeqs, unsigned short iSeqs, unsigned short iTrackChannel = 0); bool readTrack(qtractorMidiSequence *pSeq, unsigned short iTrackChannel); // Sequence/track/channel duration reader helper. unsigned long readTrackDuration(unsigned short iTrackChannel); // Header writer. bool writeHeader(unsigned short iFormat, unsigned short iTracks, unsigned short iTicksPerBeat); // Sequence/track writers. bool writeTracks(qtractorMidiSequence **ppSeqs, unsigned short iSeqs); bool writeTrack (qtractorMidiSequence *pSeq); // All-in-one SMF file writer/creator method. static bool saveCopyFile(const QString& sNewFilename, const QString& sOldFilename, unsigned short iTrackChannel, unsigned short iFormat, qtractorMidiSequence *pSeq, qtractorTimeScale *pTimeScale = nullptr, unsigned long iTimeOffset = 0); // Create filename revision. static QString createFilePathRevision( const QString& sFilename, int iRevision = 0); protected: // Read methods. int readInt (unsigned short n = 0); int readData (unsigned char *pData, unsigned short n); // Write methods. int writeInt (int val, unsigned short n = 0); int writeData (unsigned char *pData, unsigned short n); // Write tempo-time-signature node. void writeNode( qtractorMidiFileTempo::Node *pNode, unsigned long iLastTime); // Write location marker. void writeMarker( qtractorMidiFileTempo::Marker *pMarker, unsigned long iLastTime); private: // SMF instance variables. QString m_sFilename; int m_iMode; FILE *m_pFile; unsigned long m_iOffset; // Header informational data. unsigned short m_iFormat; unsigned short m_iTracks; unsigned short m_iTicksPerBeat; // Track info map. struct TrackInfo { unsigned int length; unsigned long offset; } *m_pTrackInfo; // Special tempo/time-signature map. qtractorMidiFileTempo *m_pTempoMap; }; #endif // __qtractorMidiFile_h // end of qtractorMidiFile.h qtractor-1.5.11/src/PaxHeaders/qtractorCurveSelect.h0000644000000000000000000000013215124701674017467 xustar0030 mtime=1767080892.783263496 30 atime=1767080892.783263496 30 ctime=1767080892.783263496 qtractor-1.5.11/src/qtractorCurveSelect.h0000644000175000001440000000570015124701674017461 0ustar00rncbcusers// qtractorCurveSelect.h // /**************************************************************************** Copyright (C) 2005-2019, 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 __qtractorCurveSelect_h #define __qtractorCurveSelect_h #include "qtractorCurve.h" #include #include //------------------------------------------------------------------------- // qtractorCurveSelect -- MIDI event selection capsule. class qtractorCurveSelect { public: // Constructor. qtractorCurveSelect(); // Default destructor. ~qtractorCurveSelect(); // Selection item struct. struct Item { // Item constructor. Item(const QRect& rect): rectNode(rect), flags(1) {} // Item members. QRect rectNode; unsigned int flags; }; typedef QHash ItemList; // Event selection item lookup. Item *findItem(qtractorCurve::Node *pNode); // Event insertion method. void addItem(qtractorCurve::Node *pNode, const QRect& rectNode); // Event removal method. void removeItem(qtractorCurve::Node *pNode); // Event selection method. void selectItem(qtractorCurve *pCurve, qtractorCurve::Node *pNode, const QRect& rectNode, bool bSelect = true, bool bToggle = false); // The united selection rectangle. const QRect& rect() const { return m_rect; } // Selection list accessor. const ItemList& items() const { return m_items; } // Selection update method. void update(bool bCommit); // Selection commit method. void commit(); // Reset event selection. void clear(); // Selection curve accessors. void setCurve ( qtractorCurve *pCurve ) { m_pCurve = pCurve; } qtractorCurve *curve() const { return m_pCurve; } qtractorCurve::Node *anchorNode() const { return m_pAnchorNode; } // Selection curve testimony. bool isCurrentCurve ( qtractorCurve *pCurve ) const { return (m_pCurve && m_pCurve == pCurve); } private: // The node selection list. ItemList m_items; // The united selection rectangle. QRect m_rect; // There must be only one curve. qtractorCurve *m_pCurve; // The most probable anchor node. qtractorCurve::Node *m_pAnchorNode; }; #endif // __qtractorCurveSelect_h // end of qtractorCurveSelect.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiControlPluginWidget.ui0000644000000000000000000000013215124701674022177 xustar0030 mtime=1767080892.790263466 30 atime=1767080892.790263466 30 ctime=1767080892.790263466 qtractor-1.5.11/src/qtractorMidiControlPluginWidget.ui0000644000175000001440000001120315124701674022164 0ustar00rncbcusers rncbc aka Rui Nuno Capela qtractor - An Audio/MIDI multi-track sequencer. Copyright (C) 2005-2025, 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. qtractorMidiControlPluginWidget &Type: Qt::AlignRight|Qt::AlignVCenter ControlTypeComboBox MIDI event type Qt::Horizontal 8 8 Cha&nnel: Qt::AlignRight|Qt::AlignVCenter ControlChannelSpinBox MIDI channel false 1 16 &Parameter: Qt::AlignRight|Qt::AlignVCenter ControlParamComboBox MIDI parameter &Logarithmic In&vert &Bipolar Qt::Horizontal 20 20 ControlTypeComboBox ControlChannelSpinBox ControlParamComboBox ControlLogarithmicCheckBox ControlInvertCheckBox ControlBipolarCheckBox qtractor-1.5.11/src/PaxHeaders/qtractorMainForm.h0000644000000000000000000000013215124701674016753 xustar0030 mtime=1767080892.788263475 30 atime=1767080892.788263475 30 ctime=1767080892.788263475 qtractor-1.5.11/src/qtractorMainForm.h0000644000175000001440000003607115124701674016752 0ustar00rncbcusers// qtractorMainForm.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorMainForm_h #define __qtractorMainForm_h #include "ui_qtractorMainForm.h" // Forward declarations... class qtractorOptions; class qtractorSession; class qtractorSessionEvent; class qtractorSyncEvent; class qtractorTracks; class qtractorThumbView; class qtractorMessageList; class qtractorFileSystem; class qtractorFiles; class qtractorMessages; class qtractorConnections; class qtractorMixer; class qtractorMmcEvent; class qtractorCtlEvent; class qtractorMidiControl; class qtractorTimeSpinBox; class qtractorTempoSpinBox; class qtractorTempoCursor; class qtractorInstrumentMenu; class qtractorMidiEditorForm; class qtractorMidiEditor; class qtractorMidiManager; class qtractorAudioFileFactory; class qtractorPluginFactory; class qtractorActionControl; class qtractorMidiControlObserver; class qtractorSubject; class qtractorNsmClient; class QLabel; class QComboBox; class QProgressBar; class QSocketNotifier; class QActionGroup; class QToolButton; class QPalette; //---------------------------------------------------------------------------- // qtractorMainForm -- UI wrapper form. class qtractorMainForm : public QMainWindow { Q_OBJECT public: // Constructor. qtractorMainForm(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()); // Destructor. ~qtractorMainForm(); static qtractorMainForm *getInstance(); void setup(qtractorOptions *pOptions); qtractorTracks *tracks() const; qtractorFileSystem *fileSystem() const; qtractorFiles *files() const; qtractorConnections *connections() const; qtractorMixer *mixer() const; qtractorThumbView *thumbView() const; QString sessionName(const QString& sFilename) const; QString untitledName(void) const; void clearFilename(); void updateTransportTime(unsigned long iPlayHead); void appendMessages(const QString& s); void appendMessagesColor(const QString& s, const QColor& rgb); void appendMessagesText(const QString& s); void appendMessagesError(const QString& s); void addEditorForm(qtractorMidiEditorForm *pEditorForm); void removeEditorForm(qtractorMidiEditorForm *pEditorForm); QMenu *editMenu() const { return m_ui.editMenu; } QMenu *trackMenu() const { return m_ui.trackMenu; } QMenu *trackCurveMenu() const { return m_ui.trackCurveMenu; } QMenu *trackCurveModeMenu() const { return m_ui.trackCurveModeMenu; } QProgressBar *progressBar() const { return m_pProgressBar; } void contextMenuEvent(QContextMenuEvent *pEvent); void dropEvent(QDropEvent *pDropEvent); int rolling() const; void addAudioFile(const QString& sFilename); void addMidiFile(const QString& sFilename); static QString styleSheet(const QString& sFilename); public slots: void fileNew(); void fileOpen(); void fileOpenRecent(); void fileSave(); void fileSaveAs(); void fileProperties(); void fileExit(); void editUndo(); void editRedo(); void editCut(); void editCopy(); void editPaste(); void editPasteRepeat(); void editDelete(); void editSelectModeClip(); void editSelectModeRange(); void editSelectModeRect(); void editSelectModeCurve(); void editSelectAll(); void editSelectNone(); void editSelectInvert(); void editSelectTrack(); void editSelectTrackRange(); void editSelectRange(); void editInsertRange(); void editInsertTrackRange(); void editRemoveRange(); void editRemoveTrackRange(); void editSplit(); void trackAdd(); void trackRemove(); void trackDuplicate(); void trackProperties(); void trackInputs(); void trackOutputs(); void trackStateRecord(bool bOn); void trackStateMute(bool bOn); void trackStateSolo(bool bOn); void trackStateMonitor(bool bOn); void trackNavigateFirst(); void trackNavigatePrev(); void trackNavigateNext(); void trackNavigateLast(); void trackNavigateNone(); void trackMoveTop(); void trackMoveUp(); void trackMoveDown(); void trackMoveBottom(); void trackHeightIncrease(); void trackHeightDecrease(); void trackHeightMinimize(); void trackHeightReset(); void trackAutoMonitor(bool bOn); void trackAutoDeactivate(bool bOn); void trackImportAudio(); void trackImportMidi(); void trackExportAudio(); void trackExportMidi(); void trackCurveSelect(bool On); void trackCurveSelect(QAction *pAction, bool bOn = true); void trackCurveMode(QAction *pAction); void trackCurveProcess(bool bOn); void trackCurveCapture(bool bOn); void trackCurveLocked(bool bOn); void trackCurveLogarithmic(bool bOn); void trackCurveColor(); void trackCurveClear(); void trackCurveProcessAll(bool bOn); void trackCurveCaptureAll(bool bOn); void trackCurveLockedAll(bool bOn); void trackCurveClearAll(); void clipNew(); void clipEdit(); void clipMute(); void clipUnlink(); void clipRecordEx(bool bOn); void clipSplit(); void clipMerge(); void clipNormalize(); void clipTempoAdjust(); void clipCrossFade(); void clipRangeSet(); void clipLoopSet(); void clipImport(); void clipExport(); void clipToolsQuantize(); void clipToolsTranspose(); void clipToolsNormalize(); void clipToolsRandomize(); void clipToolsResize(); void clipToolsRescale(); void clipToolsTimeshift(); void clipToolsTemporamp(); void clipTakeSelect(QAction *pAction); void clipTakeFirst(); void clipTakePrev(); void clipTakeNext(); void clipTakeLast(); void clipTakeReset(); void clipTakeRange(); void viewMenubar(bool bOn); void viewStatusbar(bool bOn); void viewToolbarFile(bool bOn); void viewToolbarEdit(bool bOn); void viewToolbarTrack(bool bOn); void viewToolbarView(bool bOn); void viewToolbarOptions(bool bOn); void viewToolbarTransport(bool bOn); void viewToolbarTime(bool bOn); void viewToolbarThumb(bool bOn); void viewFileSystem(bool bOn); void viewFiles(bool bOn); void viewMessages(bool bOn); void viewConnections(bool bOn); void viewMixer(bool bOn); void viewZoomIn(); void viewZoomOut(); void viewZoomReset(); void viewZoomHorizontal(); void viewZoomVertical(); void viewZoomAll(); void viewSnapZebra(bool bOn); void viewSnapGrid(bool bOn); void viewToolTips(bool bOn); void viewSnap(); void viewRefresh(); void viewInstruments(); void viewControllers(); void viewBuses(); void viewTempoMap(); void viewOptions(); void transportBackward(); void transportRewind(); void transportFastForward(); void transportForward(); void transportStepBackward(); void transportStepForward(); void transportLoop(); void transportLoopSet(); void transportStop(); void transportPlay(); void transportRecord(); void transportPunch(); void transportPunchSet(); void transportCountIn(); void transportMetro(); void transportFollow(); void transportAutoBackward(); void transportContinue(); void transportModeNone(); void transportModeSlave(); void transportModeMaster(); void transportModeFull(); void transportPanic(); void helpShortcuts(); void helpAbout(); void helpAboutQt(); void stabilizeForm(); void selectionNotifySlot(qtractorMidiEditor *pMidiEditor); void changeNotifySlot(qtractorMidiEditor *pMidiEditor); void updateNotifySlot(unsigned int flags); void dirtyNotifySlot(); void autoSaveAsap(); protected slots: void fastTimerSlot(); void slowTimerSlot(); void alsaNotify(); void audioPeakNotify(); void audioShutNotify(); void audioXrunNotify(); void audioPortNotify(); void audioBuffNotify(unsigned int iBufferSize); void audioSessNotify(void *pvSessionArg); void audioSyncNotify(unsigned long iPlayHead, bool bPlaying); void audioPropNotify(); void audioSelfNotify(); void midiMmcNotify(const qtractorMmcEvent& mmce); void midiCtlNotify(const qtractorCtlEvent& ctle); void midiSppNotify(int iSppCmd, unsigned short iSongPos); void midiClkNotify(float fTempo); void midiInpNotify(unsigned short flags); void updateRecentFilesMenu(); void updateTrackMenu(); void updateCurveMenu(); void updateCurveModeMenu(); void updateClipMenu(); void updateTakeMenu(); void updateTakeSelectMenu(); void updateExportMenu(); void updateZoomMenu(); void updateSnapMenu(); void updateTrackInstrumentMenu(); void selectAudioFile(const QString& sFilename, int iTrackChannel, bool bSelect); void activateAudioFile(const QString& sFilename, int iTrackChannel = -1); void selectMidiFile(const QString& sFilename, int iTrackChannel, bool bSelect); void activateMidiFile(const QString& sFilename, int iTrackChannel = -1); void activateFile(const QString& sFilename); void trackSelectionChanged(); void mixerSelectionChanged(); void transportTimeFormatChanged(int iDisplayFormat); void transportTimeChanged(unsigned long iPlayHead); void transportTimeFinished(); void transportTempoChanged(float fTempo, unsigned short iBeatsPerBar, unsigned short iBeatDivisor); void transportTempoFinished(); void snapPerBeatChanged(int iSnap); void contentsChanged(); void transportTempoContextMenu(const QPoint& pos); void handle_sigusr1(); void handle_sigterm(); void openNsmSession(); void saveNsmSession(); void showNsmSession(); void hideNsmSession(); protected: void closeEvent(QCloseEvent *pCloseEvent); void dragEnterEvent(QDragEnterEvent *pDragEnterEvent); void showEvent(QShowEvent *pShowEvent); void hideEvent(QHideEvent *pHideEvent); bool queryClose(); bool newSession(); bool openSession(); bool saveSession(bool bPrompt); bool editSession(); bool closeSession(); bool loadSessionFile(const QString& sFilename); bool loadSessionFileEx(const QStringList& files, int iFlags, bool bUpdate); bool saveSessionFileEx(const QString& sFilename, int iFlags, bool bUpdate); QString sessionBackupPath(const QString& sFilename) const; QString sessionArchivePath(const QString& sFilename) const; bool startSession(); bool checkRestartSession(); bool setPlaying(bool bPlaying); bool setPlayingEx(bool bPlaying); bool setRecording(bool bRecording); int setRolling(int iRolling); void setLocate(unsigned int iLocate); void setShuttle(float fShuttle); void setStep(int iStep); void setTrack(int scmd, int iTrack, bool bOn); void setSongPos(unsigned int iSongPos); void updateSessionPre(); void updateSessionPost(); void updateRecentFiles(const QString& sFilename); void updatePeakAutoRemove(); void updateMessagesFont(); void updateMessagesLimit(); void updateMessagesCapture(); void updateDisplayFormat(); void updateTransportModePre(); void updateTransportModePost(); void updateTimebase(); void updateMidiControlModes(); void updateAudioPlayer(); void updateMidiQueueTimer(); void updateMidiDriftCorrect(); void updateMidiPlayer(); void updateMidiControl(); void updateAudioMetronome(); void updateMidiMetronome(); void updateSyncViewHold(); void updateCustomColorTheme(); void updateCustomStyleTheme(); void updateCustomStyleSheet(); void updateEditorForms(); void updateContents(qtractorMidiEditor *pMidiEditor, bool bRefresh); void updateDirtyCount(bool bDirtyCount); void trackCurveSelectMenuAction(QMenu *pMenu, qtractorMidiControlObserver *pObserver, qtractorSubject *pCurrentSubject) const; bool trackCurveSelectMenuReset(QMenu *pMenu) const; bool trackCurveModeMenuReset(QMenu *pMenu) const; void openNsmSessionEx(bool bOpenReply); void saveNsmSessionEx(bool bSaveReply); bool autoSaveOpen(); void autoSaveReset(); void autoSaveSession(); void autoSaveClose(); QString sessionDir(const QString& sFilename) const; private: // The Qt-designer UI struct... Ui::qtractorMainForm m_ui; // Instance variables... qtractorOptions *m_pOptions; qtractorSession *m_pSession; qtractorFileSystem *m_pFileSystem; qtractorFiles *m_pFiles; qtractorMessages *m_pMessages; qtractorConnections *m_pConnections; qtractorMixer *m_pMixer; qtractorTracks *m_pTracks; qtractorMessageList *m_pMessageList; qtractorAudioFileFactory *m_pAudioFileFactory; qtractorPluginFactory *m_pPluginFactory; QString m_sFilename; int m_iUntitled; int m_iDirtyCount; int m_iBackupCount; QSocketNotifier *m_pSigusr1Notifier; QSocketNotifier *m_pSigtermNotifier; QActionGroup *m_pSelectModeActionGroup; QToolButton *m_pSelectModeToolButton; QActionGroup *m_pTransportModeActionGroup; QToolButton *m_pTransportModeToolButton; qtractorTimeSpinBox *m_pTimeSpinBox; qtractorTempoSpinBox *m_pTempoSpinBox; QComboBox *m_pSnapPerBeatComboBox; QProgressBar *m_pProgressBar; qtractorThumbView *m_pThumbView; qtractorInstrumentMenu *m_pInstrumentMenu; qtractorActionControl *m_pActionControl; qtractorMidiControl *m_pMidiControl; qtractorNsmClient *m_pNsmClient; QString m_sNsmFile; QString m_sNsmExt; bool m_bNsmDirty; unsigned long m_iPlayHead; int m_iTransportUpdate; int m_iTransportRolling; bool m_bTransportPlaying; float m_fTransportShuttle; int m_iTransportStep; int m_iXrunCount; int m_iXrunSkip; int m_iXrunTimer; int m_iAudioPeakTimer; int m_iAudioRefreshTimer; int m_iMidiRefreshTimer; int m_iPlayerTimer; int m_iAutoSaveTimer; int m_iAutoSavePeriod; int m_iAudioPropertyChange; int m_iAudioSelfConnected; int m_iStabilizeTimer; qtractorTempoCursor *m_pTempoCursor; // Status bar item indexes enum { StatusName = 0, // Active session track caption. StatusMod = 1, // Current session modification state. StatusRec = 2, // Current session recording state. StatusMute = 3, // Current session muting state. StatusSolo = 4, // Current session soloing state. StatusLoop = 5, // Current session looping state. StatusXrun = 6, // Current session XRUN count. StatusTime = 7, // Current session length time. StatusSize = 8, // Current session buffer size. StatusRate = 9, // Current session sample rate. StatusItems = 10 // Number of status items. }; QLabel *m_statusItems[StatusItems]; // Palette status item indexes enum { PaletteNone = 0, // Default background color. PaletteRed = 1, // Current session recording state (red). PaletteYellow = 2, // Current session muting state (yellow). PaletteCyan = 3, // Current session soloing state (cyan). PaletteGreen = 4, // Current session looping state (green). PaletteItems = 5 // Number of palette items. }; QPalette *m_paletteItems[PaletteItems]; // View/Snap-to-beat actions (for shortcuts access) QList m_snapPerBeatActions; // Name says it all... QList m_editors; // Kind-of singleton reference. static qtractorMainForm *g_pMainForm; }; #endif // __qtractorMainForm_h // end of qtractorMainForm.h qtractor-1.5.11/src/PaxHeaders/qtractorInstrumentMenu.cpp0000644000000000000000000000013215124701674020573 xustar0030 mtime=1767080892.786263483 30 atime=1767080892.786263483 30 ctime=1767080892.786263483 qtractor-1.5.11/src/qtractorInstrumentMenu.cpp0000644000175000001440000003541215124701674020570 0ustar00rncbcusers// qtractorInstrumentMenu.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorInstrumentMenu.h" #include "qtractorInstrument.h" #include "qtractorTrack.h" #include "qtractorTrackCommand.h" #include "qtractorSession.h" #include "qtractorMidiEngine.h" #include "qtractorMidiManager.h" #include #include //---------------------------------------------------------------------- // class qtractorInstrumentMenu -- instrument definition data classes. // // Constructor. qtractorInstrumentMenu::qtractorInstrumentMenu ( QObject *pParent ) : QObject(pParent), m_pTrack(nullptr) { } // Main accessor initiator. void qtractorInstrumentMenu::updateTrackMenu ( qtractorTrack *pTrack, QMenu *pMenu ) { if (pTrack == nullptr) return; m_pTrack = pTrack; trackMenuReset(pMenu); } bool qtractorInstrumentMenu::trackMenuReset ( QMenu *pMenu ) const { pMenu->clear(); // pMenu->setStyle(scrollableMenuStyle()); if (m_pTrack == nullptr) return false; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return false; qtractorInstrumentList *pInstruments = pSession->instruments(); if (pInstruments == nullptr) return false; QString sCurrentName; qtractorMidiBus *pMidiBus = static_cast (m_pTrack->outputBus()); if (pMidiBus) { const unsigned short iChannel = m_pTrack->midiChannel(); const qtractorMidiBus::Patch& patch = pMidiBus->patch(iChannel); sCurrentName = patch.instrumentName; } // Instrument sub-menu... const QIcon& icon = QIcon::fromTheme("itemInstrument"); trackMenuAdd(pMenu, icon, (m_pTrack->pluginList())->midiManager(), sCurrentName); if (pMidiBus && pMidiBus->pluginList_out()) { trackMenuAdd(pMenu, icon, (pMidiBus->pluginList_out())->midiManager(), sCurrentName); } qtractorInstrumentList::ConstIterator iter = pInstruments->constBegin(); const qtractorInstrumentList::ConstIterator& iter_end = pInstruments->constEnd(); for ( ; iter != iter_end; ++iter) { const qtractorInstrument& instr = iter.value(); const QString& sInstrumentName = instr.instrumentName(); if (sInstrumentName.isEmpty()) continue; QMenu *pBankMenu = pMenu->addMenu(icon, sInstrumentName); QAction *pAction = pBankMenu->menuAction(); pAction->setCheckable(true); pAction->setChecked(sInstrumentName == sCurrentName); QObject::connect(pBankMenu, SIGNAL(aboutToShow()), SLOT(updateBankMenu())); } return true; } bool qtractorInstrumentMenu::trackMenuAdd ( QMenu *pMenu, const QIcon& icon, qtractorMidiManager *pMidiManager, const QString& sCurrentName ) const { if (pMidiManager == nullptr) return false; pMidiManager->updateInstruments(); // Instrument sub-menu... const qtractorInstrumentList& instruments = pMidiManager->instruments(); qtractorInstrumentList::ConstIterator iter = instruments.constBegin(); const qtractorInstrumentList::ConstIterator& iter_end = instruments.constEnd(); for ( ; iter != iter_end; ++iter) { const qtractorInstrument& instr = iter.value(); const QString& sInstrumentName = instr.instrumentName(); if (sInstrumentName.isEmpty()) continue; QMenu *pBankMenu = pMenu->addMenu(icon, sInstrumentName); QAction *pAction = pBankMenu->menuAction(); pAction->setCheckable(true); pAction->setChecked(sInstrumentName == sCurrentName); QObject::connect(pBankMenu, SIGNAL(aboutToShow()), SLOT(updateBankMenu())); } return true; } // Track/Instrument bank sub-menu stabilizers. void qtractorInstrumentMenu::updateBankMenu (void) { QMenu *pBankMenu = qobject_cast (sender()); if (pBankMenu) bankMenuReset(pBankMenu); } bool qtractorInstrumentMenu::bankMenuReset ( QMenu *pBankMenu ) const { pBankMenu->clear(); // pBankMenu->setStyle(scrollableMenuStyle()); if (m_pTrack == nullptr) return false; const QString sInstrumentName = pBankMenu->title().remove('&'); const int iCurrentBank = m_pTrack->midiBank(); // Instrument plug-in banks sub-menu... const QIcon& icon = QIcon::fromTheme("itemPatches"); if (bankMenuAdd(pBankMenu, icon, (m_pTrack->pluginList())->midiManager(), sInstrumentName, iCurrentBank)) return true; qtractorMidiBus *pMidiBus = static_cast (m_pTrack->outputBus()); if (pMidiBus && pMidiBus->pluginList_out() && bankMenuAdd(pBankMenu, icon, (pMidiBus->pluginList_out())->midiManager(), sInstrumentName, iCurrentBank)) return true; // Instrument bank/patches sub-menu... qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return false; qtractorInstrumentList *pInstruments = pSession->instruments(); if (pInstruments == nullptr) return false; if (pInstruments->contains(sInstrumentName)) { const qtractorInstrument& instr = pInstruments->value(sInstrumentName); const qtractorInstrumentPatches& patches = instr.patches(); qtractorInstrumentPatches::ConstIterator patch_iter = patches.constBegin(); const qtractorInstrumentPatches::ConstIterator& patch_end = patches.constEnd(); for ( ; patch_iter != patch_end; ++patch_iter) { const int iBank = patch_iter.key(); const qtractorInstrumentData& patch = patch_iter.value(); const QString& sBankName = patch.name(); if (iBank < 0 || sBankName.isEmpty()) continue; QMenu *pProgMenu = pBankMenu->addMenu(icon, sBankName); QAction *pAction = pProgMenu->menuAction(); pAction->setCheckable(true); pAction->setChecked(iBank == iCurrentBank); pAction->setData(iBank); QObject::connect(pProgMenu, SIGNAL(aboutToShow()), SLOT(updateProgMenu())); } } // There should always be a dummy bank (-1)... if (pBankMenu->actions().count() > 0) pBankMenu->addSeparator(); QMenu *pProgMenu = pBankMenu->addMenu(tr("(None)")); QAction *pAction = pProgMenu->menuAction(); pAction->setCheckable(true); pAction->setChecked(iCurrentBank < 0); pAction->setData(-1); QObject::connect(pProgMenu, SIGNAL(aboutToShow()), SLOT(updateProgMenu())); return true; } bool qtractorInstrumentMenu::bankMenuAdd ( QMenu *pBankMenu, const QIcon& icon, qtractorMidiManager *pMidiManager, const QString& sInstrumentName, int iCurrentBank ) const { if (pMidiManager == nullptr) return false; // Instrument plug-in banks sub-menu... const qtractorInstrumentList& instruments = pMidiManager->instruments(); if (!instruments.contains(sInstrumentName)) return false; const qtractorInstrument& instr = instruments.value(sInstrumentName); const qtractorInstrumentPatches& patches = instr.patches(); qtractorInstrumentPatches::ConstIterator patch_iter = patches.constBegin(); const qtractorInstrumentPatches::ConstIterator& patch_end = patches.constEnd(); for ( ; patch_iter != patch_end; ++patch_iter) { const int iBank = patch_iter.key(); const qtractorInstrumentData& patch = patch_iter.value(); const QString& sBankName = patch.name(); if (iBank < 0 || sBankName.isEmpty()) continue; QMenu *pProgMenu = pBankMenu->addMenu(icon, sBankName); QAction *pAction = pProgMenu->menuAction(); pAction->setCheckable(true); pAction->setChecked(iBank == iCurrentBank); pAction->setData(iBank); QObject::connect(pProgMenu, SIGNAL(aboutToShow()), SLOT(updateProgMenu())); } // There should always be a dummy bank (-1)... if (pBankMenu->actions().count() > 0) pBankMenu->addSeparator(); QMenu *pProgMenu = pBankMenu->addMenu(tr("(None)")); QAction *pAction = pProgMenu->menuAction(); pAction->setCheckable(true); pAction->setChecked(iCurrentBank < 0); pAction->setData(-1); QObject::connect(pProgMenu, SIGNAL(aboutToShow()), SLOT(updateProgMenu())); return true; } // Track/Instrument bank sub-menu stabilizers. void qtractorInstrumentMenu::updateProgMenu (void) { QMenu *pProgMenu = qobject_cast (sender()); if (pProgMenu) progMenuReset(pProgMenu); } bool qtractorInstrumentMenu::progMenuReset ( QMenu *pProgMenu ) const { pProgMenu->clear(); pProgMenu->setStyle(scrollableMenuStyle()); if (m_pTrack == nullptr) return false; QAction *pBankAction = pProgMenu->menuAction(); if (pBankAction == nullptr) return false; QMenu *pBankMenu = nullptr; #if QT_VERSION >= QT_VERSION_CHECK(6, 3, 2) QListIterator iter(pBankAction->associatedObjects()); #else QListIterator iter(pBankAction->associatedWidgets()); #endif while (iter.hasNext() && pBankMenu == nullptr) pBankMenu = qobject_cast (iter.next()); if (pBankMenu == nullptr) return false; const QString sInstrumentName = pBankMenu->title().remove('&'); const int iBank = pBankAction->data().toInt(); const int iCurrentProg = m_pTrack->midiProg(); // Instrument plugin programs sub-menu... const QIcon& icon = QIcon::fromTheme("itemChannel"); if (progMenuAdd(pProgMenu, icon, (m_pTrack->pluginList())->midiManager(), sInstrumentName, iBank, iCurrentProg)) return true; qtractorMidiBus *pMidiBus = static_cast (m_pTrack->outputBus()); if (pMidiBus && pMidiBus->pluginList_out() && progMenuAdd(pProgMenu, icon, (pMidiBus->pluginList_out())->midiManager(), sInstrumentName, iBank, iCurrentProg)) return true; // Instrument programs sub-menu... qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return false; qtractorInstrumentList *pInstruments = pSession->instruments(); if (pInstruments == nullptr) return false; if (pInstruments->contains(sInstrumentName)) { const qtractorInstrument& instr = pInstruments->value(sInstrumentName); const qtractorInstrumentPatches& patches = instr.patches(); const qtractorInstrumentData& patch = patches[iBank]; qtractorInstrumentData::ConstIterator prog_iter = patch.constBegin(); const qtractorInstrumentData::ConstIterator& prog_end = patch.constEnd(); for ( ; prog_iter != prog_end; ++prog_iter) { const int iProg = prog_iter.key(); if (iProg < 0) continue; const QString& sProgName = prog_iter.value(); QAction *pAction = pProgMenu->addAction(icon, sProgName); pAction->setCheckable(true); pAction->setChecked(iProg == iCurrentProg); pAction->setData(iProg); QObject::connect(pAction, SIGNAL(triggered(bool)), SLOT(progActionTriggered(bool))); } } // There should always be a dummy program (-1)... if (pProgMenu->actions().count() > 0) pProgMenu->addSeparator(); QAction *pAction = pProgMenu->addAction(tr("(None)")); pAction->setCheckable(true); pAction->setChecked(iCurrentProg < 0); pAction->setData(-1); QObject::connect(pAction, SIGNAL(triggered(bool)), SLOT(progActionTriggered(bool))); return true; } bool qtractorInstrumentMenu::progMenuAdd ( QMenu *pProgMenu, const QIcon& icon, qtractorMidiManager *pMidiManager, const QString& sInstrumentName, int iBank, int iCurrentProg ) const { if (pMidiManager == nullptr) return false; // Instrument plugin programs sub-menu... const qtractorInstrumentList& instruments = pMidiManager->instruments(); if (!instruments.contains(sInstrumentName)) return false; const QString sProg("%1 - %2"); const qtractorInstrument& instr = instruments.value(sInstrumentName); const qtractorInstrumentPatches& patches = instr.patches(); const qtractorInstrumentData& patch = patches[iBank]; qtractorInstrumentData::ConstIterator prog_iter = patch.constBegin(); const qtractorInstrumentData::ConstIterator& prog_end = patch.constEnd(); for ( ; prog_iter != prog_end; ++prog_iter) { const int iProg = prog_iter.key(); if (iProg < 0) continue; const QString& sProgName = sProg.arg(iProg).arg(prog_iter.value()); QAction *pAction = pProgMenu->addAction(icon, sProgName); pAction->setCheckable(true); pAction->setChecked(iProg == iCurrentProg); pAction->setData(iProg); QObject::connect(pAction, SIGNAL(triggered(bool)), SLOT(progActionTriggered(bool))); } // There should always be a dummy program (-1)... if (pProgMenu->actions().count() > 0) pProgMenu->addSeparator(); QAction *pAction = pProgMenu->addAction(tr("(None)")); pAction->setCheckable(true); pAction->setChecked(iCurrentProg < 0); pAction->setData(-1); QObject::connect(pAction, SIGNAL(triggered(bool)), SLOT(progActionTriggered(bool))); return true; } // Track/Instrument patch selection. void qtractorInstrumentMenu::progActionTriggered ( bool /*bOn*/ ) { if (m_pTrack == nullptr) return; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; QAction *pProgAction = qobject_cast (sender()); if (pProgAction) { const int iProg = pProgAction->data().toInt(); #if QT_VERSION >= QT_VERSION_CHECK(6, 3, 2) QListIterator bank_iter(pProgAction->associatedObjects()); #else QListIterator bank_iter(pProgAction->associatedWidgets()); #endif while (bank_iter.hasNext()) { QMenu *pBankMenu = qobject_cast (bank_iter.next()); if (pBankMenu == nullptr) continue; QAction *pBankAction = pBankMenu->menuAction(); if (pBankAction) { const int iBank = pBankAction->data().toInt(); #if QT_VERSION >= QT_VERSION_CHECK(6, 3, 2) QListIterator iter(pBankAction->associatedObjects()); #else QListIterator iter(pBankAction->associatedWidgets()); #endif while (iter.hasNext()) { QMenu *pMenu = qobject_cast (iter.next()); if (pMenu) { const QString sInstrumentName = pMenu->title().remove('&'); // Make it an undoable command... pSession->execute( new qtractorTrackInstrumentCommand( m_pTrack, sInstrumentName, iBank, iProg)); // Done. m_pTrack = nullptr; break; } } } // Bail out. break; } } } // A custom scrollable menu style (static). QStyle *qtractorInstrumentMenu::scrollableMenuStyle (void) { class ScrollableMenuStyle : public QProxyStyle { protected: int styleHint(StyleHint shint, const QStyleOption *sopt, const QWidget *widget, QStyleHintReturn * hret) const { if (shint == QStyle::SH_Menu_Scrollable) return 1; else return QProxyStyle::styleHint(shint, sopt, widget, hret); } }; static ScrollableMenuStyle s_scrollableMenuStyle; return &s_scrollableMenuStyle; } // end of qtractorInstrumentMenu.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiSysexForm.h0000644000000000000000000000013215124701674020005 xustar0030 mtime=1767080892.795263445 30 atime=1767080892.795263445 30 ctime=1767080892.795263445 qtractor-1.5.11/src/qtractorMidiSysexForm.h0000644000175000001440000000534015124701674017777 0ustar00rncbcusers// qtractorMidiSysexForm.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorMidiSysexForm_h #define __qtractorMidiSysexForm_h #include "ui_qtractorMidiSysexForm.h" // Forward declarations... class qtractorMidiSysexList; //---------------------------------------------------------------------------- // qtractorMidiSysexForm -- UI wrapper form. class qtractorMidiSysexForm : public QDialog { Q_OBJECT public: // Constructor. qtractorMidiSysexForm(QWidget *pParent = nullptr); // Destructor. ~qtractorMidiSysexForm(); // SysEx list accessors. void setSysexList(qtractorMidiSysexList *pSysexList); qtractorMidiSysexList *sysexList() const; protected slots: void click(QAbstractButton *); void accept(); void reject(); void importSlot(); void exportSlot(); void moveUpSlot(); void moveDownSlot(); void nameChanged(const QString& sName); void textChanged(); void openSlot(); void loadSlot(int iName); void saveSlot(); void deleteSlot(); void addSlot(); void updateSlot(); void removeSlot(); void clearSlot(); void refreshForm(); void stabilizeForm(); protected: // SysEx file i/o methods. bool loadSysexItems( QList& items, const QString& sFilename); bool saveSysexItems( const QList& items, const QString& sFilename) const; void loadSysexFile(const QString& sFilename); void saveSysexFile(const QString& sFilename); // Refresh SysEx names (presets). void refreshSysex(); // SysEx preset group path name. static QString sysexGroup(); private: // The Qt-designer UI struct... Ui::qtractorMidiSysexForm m_ui; // Main editable data structure. qtractorMidiSysexList *m_pSysexList; // Instance variables... int m_iDirtyCount; int m_iDirtyItem; int m_iDirtySysex; int m_iUpdateSysex; }; #endif // __qtractorMidiSysexForm_h // end of qtractorMidiSysexForm.h qtractor-1.5.11/src/PaxHeaders/qtractorAudioIOMatrixForm.ui0000644000000000000000000000013215124701674020733 xustar0030 mtime=1767080892.779263512 30 atime=1767080892.779263512 30 ctime=1767080892.779263512 qtractor-1.5.11/src/qtractorAudioIOMatrixForm.ui0000644000175000001440000000447015124701674020730 0ustar00rncbcusers Copyright (C) 2005-2025, 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. qtractorAudioIOMatrixForm 0 0 320 240 0 0 Aux-Send I/O Matrix 4 4 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorAudioIOMatrixForm::TableWidget QTableWidget
qtractorAudioIOMatrixForm.h
TableWidget DialogButtons
qtractor-1.5.11/src/PaxHeaders/qtractorTrackForm.h0000644000000000000000000000013215124701674017133 xustar0030 mtime=1767080892.802263416 30 atime=1767080892.802263416 30 ctime=1767080892.802263416 qtractor-1.5.11/src/qtractorTrackForm.h0000644000175000001440000001141515124701674017125 0ustar00rncbcusers// qtractorTrackForm.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorTrackForm_h #define __qtractorTrackForm_h #include "ui_qtractorTrackForm.h" #include "qtractorTrack.h" #include // Forward declarations... class qtractorMidiBus; class qtractorMidiManager; class qtractorCommand; class QMenu; //---------------------------------------------------------------------------- // qtractorTrackForm -- UI wrapper form. class qtractorTrackForm : public QDialog { Q_OBJECT public: // Constructor. qtractorTrackForm(QWidget *pParent = nullptr); // Destructor. ~qtractorTrackForm(); void setTrack(qtractorTrack *pTrack); qtractorTrack *track() const; const qtractorTrack::Properties& properties() const; qtractorTrack::TrackType trackType() const; void setMidiProgram(int iBank, int iProg); protected slots: void accept(); void reject(); void stabilizeForm(); void changed(); void pluginListChanged(); void trackTypeChanged(); void inputBusNameChanged(int iInputBusName); void outputBusNameChanged(int iOutputBusName); void busNameClicked(); void channelChanged(int iChannel); void instrumentChanged(int iInstrument); void bankSelMethodChanged(int iBankSelMethod); void bankChanged(); void progChanged(); void foregroundColorChanged(const QString& sText); void backgroundColorChanged(const QString& sText); void autoBackgroundColorChanged(); void selectForegroundColor(); void selectBackgroundColor(); void addPlugin(); void removePlugin(); void moveUpPlugin(); void moveDownPlugin(); void updatePluginListLatency(); void trackIconAction(); void trackIconClicked(); void trackIconChanged(); protected: qtractorMidiBus *midiBus() const; int midiBank() const; int midiProg() const; void updateInstruments(); void updateInstrumentsAdd( const QIcon& icon, qtractorMidiManager *pMidiManager); void updateTrackType(qtractorTrack::TrackType trackType); void updateChannel(int iChannel, int iBankSelMethod, int iBank, int iProg); void updateBanks(const QString& sInstrumentName, int iBankSelMethod, int iBank, int iProg); void updatePrograms(const QString& sInstrumentName, int iBank, int iProg); bool updateBanksAdd(const QIcon& icon, qtractorMidiManager *pMidiManager, const QString& sInstrumentName, int iBank, int& iBankIndex); bool updateProgramsAdd(const QIcon& icon, qtractorMidiManager *pMidiManager, const QString& sInstrumentName, int iBank, int iProg, int& iProgIndex); void updateColorItem(QComboBox *pComboBox, const QColor& color); void updateColorText(QComboBox *pComboBox, const QColor& color); QColor colorItem(QComboBox *pComboBox); // Update/reset output bus name... void updateOutputBusName(const QString& sBusName); // Save/load default bus names... void loadDefaultBusNames(qtractorTrack::TrackType trackType); void saveDefaultBusNames(qtractorTrack::TrackType trackType) const; // Track icon generic/standard action setup. void addIconMenuAction(const QString& sText, const QString& sTrackIcon); private: // The Qt-designer UI struct... Ui::qtractorTrackForm m_ui; // Instance variables... qtractorTrack *m_pTrack; qtractorTrack::Properties m_props; qtractorMidiBus *m_pMidiBus; QMap m_banks; QMap m_progs; int m_iDirtySetup; int m_iDirtyCount; int m_iDirtyPatch; QString m_sOldOutputBusName; qtractorMidiBus *m_pOldMidiBus; int m_iOldChannel; QString m_sOldInstrumentName; int m_iOldBankSelMethod; int m_iOldBank; int m_iOldProg; // Keep last acceptable command. qtractorCommand *m_pLastCommand; // Track icon menu. QMenu *m_pIconMenu; // MIDI bank/program observer. class MidiProgramObserver; MidiProgramObserver *m_pMidiProgramObserver; // Default bus names... static QString g_sAudioInputBusName; static QString g_sAudioOutputBusName; static QString g_sMidiInputBusName; static QString g_sMidiOutputBusName; }; #endif // __qtractorTrackForm_h // end of qtractorTrackForm.h qtractor-1.5.11/src/PaxHeaders/qtractorTakeRangeForm.h0000644000000000000000000000013215124701674017730 xustar0030 mtime=1767080892.800263424 30 atime=1767080892.800263424 30 ctime=1767080892.800263424 qtractor-1.5.11/src/qtractorTakeRangeForm.h0000644000175000001440000000377515124701674017734 0ustar00rncbcusers// qtractorTakeRangeForm.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorTakeRangeForm_h #define __qtractorTakeRangeForm_h #include "ui_qtractorTakeRangeForm.h" // Forward declarations. class qtractorTimeScale; class qtractorClip; //---------------------------------------------------------------------------- // qtractorTakeRangeForm -- UI wrapper form. class qtractorTakeRangeForm : public QDialog { Q_OBJECT public: // Constructor. qtractorTakeRangeForm(QWidget *pParent = nullptr); // Destructor. ~qtractorTakeRangeForm(); // Setup accessors. void setClip(qtractorClip *pClip); qtractorClip *clip() const; // Result accessors. unsigned long takeStart() const; unsigned long takeEnd() const; int currentTake() const; protected slots: void rangeChanged(); void formatChanged(int); void valueChanged(); void updateCurrentTake(); void stabilizeForm(); private: // The Qt-designer UI struct... Ui::qtractorTakeRangeForm m_ui; // Instance variables... qtractorTimeScale *m_pTimeScale; qtractorClip *m_pClip; }; #endif // __qtractorTakeRangeForm_h // end of qtractorTakeRangeForm.h qtractor-1.5.11/src/PaxHeaders/qtractorCtlEvent.h0000644000000000000000000000013015124701674016765 xustar0030 mtime=1767080892.783263496 28 atime=1767080892.7822635 30 ctime=1767080892.783263496 qtractor-1.5.11/src/qtractorCtlEvent.h0000644000175000001440000000410515124701674016757 0ustar00rncbcusers// qtractorCtlEvent.h // /**************************************************************************** Copyright (C) 2010, 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 __qtractorCtlEvent_h #define __qtractorCtlEvent_h #include "qtractorMidiEvent.h" //---------------------------------------------------------------------- // qtractorCtlEvent - MIDI Control custom event. // class qtractorCtlEvent { public: // Contructor. qtractorCtlEvent(qtractorMidiEvent::EventType ctype = qtractorMidiEvent::CONTROLLER, unsigned short iChannel = 0, unsigned short iParam = 0, unsigned short iValue = 0) : m_ctype(ctype), m_channel(iChannel), m_param(iParam), m_value(iValue) {} // Copy constructor. qtractorCtlEvent(const qtractorCtlEvent& ctle) : m_ctype(ctle.m_ctype), m_channel(ctle.m_channel), m_param(ctle.m_param), m_value(ctle.m_value) {} // Accessors. qtractorMidiEvent::EventType type() const { return m_ctype; } unsigned short channel() const { return m_channel; } unsigned short param() const { return m_param; } unsigned short value() const { return m_value; } private: // Instance variables. qtractorMidiEvent::EventType m_ctype; unsigned short m_channel; unsigned short m_param; unsigned short m_value; }; #endif // __qtractorCtlEvent_h // end of qtractorCtlEvent.h qtractor-1.5.11/src/PaxHeaders/qtractorSessionForm.cpp0000644000000000000000000000013215124701674020045 xustar0030 mtime=1767080892.800263424 30 atime=1767080892.800263424 30 ctime=1767080892.800263424 qtractor-1.5.11/src/qtractorSessionForm.cpp0000644000175000001440000003334115124701674020041 0ustar00rncbcusers// qtractorSessionForm.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorSessionForm.h" #include "qtractorAbout.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include #include #include #include #include #include #include //---------------------------------------------------------------------------- // qtractorSessionForm -- UI wrapper form. // Constructor. qtractorSessionForm::qtractorSessionForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); #if QT_VERSION < QT_VERSION_CHECK(6, 1, 0) QDialog::setWindowIcon(QIcon(":/images/qtractor.png")); #endif // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); // Initialize conveniency options... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { m_ui.AutoSessionDirCheckBox->setChecked(pOptions->bAutoSessionDir); pOptions->loadComboBoxHistory(m_ui.SessionDirComboBox); } const QFont& font = QDialog::font(); const QFont font2(font.family(), font.pointSize() - 2); m_ui.AutoSessionDirCheckBox->setFont(font2); m_ui.AutoSessionDirCheckBox->setMaximumHeight( QFontMetrics(font2).height()); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) // Some conveniency cleaner helper... m_ui.SessionDirComboBox->lineEdit()->setClearButtonEnabled(true); #endif // Setup some specific validators. m_ui.SampleRateComboBox->setValidator( new QIntValidator(m_ui.SampleRateComboBox)); // Fill-up snap-per-beat items... const QIcon& snapIcon = QIcon::fromTheme("itemBeat"); const QStringList& snapItems = qtractorTimeScale::snapItems(); QStringListIterator snapIter(snapItems); m_ui.SnapPerBeatComboBox->clear(); m_ui.SnapPerBeatComboBox->setIconSize(QSize(8, 16)); // snapIter.toFront(); if (snapIter.hasNext()) m_ui.SnapPerBeatComboBox->addItem( QIcon::fromTheme("itemNone"), snapIter.next()); while (snapIter.hasNext()) m_ui.SnapPerBeatComboBox->addItem(snapIcon, snapIter.next()); // m_ui.SnapPerBeatComboBox->insertItems(0, snapItems); // Initialize dirty control state. m_bNewSession = false; m_iDirtyCount = 0; // Try to restore old window positioning. adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.SessionNameLineEdit, SIGNAL(textChanged(const QString&)), SLOT(changeSessionName(const QString&))); QObject::connect(m_ui.AutoSessionDirCheckBox, SIGNAL(toggled(bool)), SLOT(changeAutoSessionDir(bool))); QObject::connect(m_ui.SessionDirComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changeSessionDir(const QString&))); QObject::connect(m_ui.SessionDirToolButton, SIGNAL(clicked()), SLOT(browseSessionDir())); QObject::connect(m_ui.DescriptionTextEdit, SIGNAL(textChanged()), SLOT(changed())); QObject::connect(m_ui.SampleRateComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.TempoSpinBox, SIGNAL(valueChanged(float, unsigned short, unsigned short)), SLOT(changed())); QObject::connect(m_ui.TicksPerBeatSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.SnapPerBeatComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.PixelsPerBeatSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.HorizontalZoomSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.VerticalZoomSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); } // Destructor. qtractorSessionForm::~qtractorSessionForm (void) { } // Populate (setup) dialog controls from settings descriptors. void qtractorSessionForm::setSession ( qtractorSession *pSession, bool bSessionDir ) { // Session properties cloning... m_props = pSession->properties(); // HACK: Fix for an initial session directory proposal... m_bNewSession = m_props.sessionName.isEmpty() && bSessionDir; if (m_bNewSession) { QDir dir(m_props.sessionDir); QStringList filters; filters << dir.dirName() + '*'; if (!dir.entryList(filters, QDir::Files).isEmpty()) { const QFileInfo info(dir.absolutePath()); m_props.sessionDir = info.absolutePath(); } } // HACK: Remember current session-dir.... m_bSessionDir = bSessionDir; m_sSessionDir = m_props.sessionDir; // Initialize dialog widgets... m_ui.SessionNameLineEdit->setText(m_props.sessionName); m_ui.SessionDirComboBox->setEditText(m_props.sessionDir); m_ui.DescriptionTextEdit->setPlainText(m_props.description); // Time properties... m_ui.SampleRateComboBox->setEditText( QString::number(m_props.timeScale.sampleRate())); m_ui.SampleRateTextLabel->setEnabled(!pSession->isActivated()); m_ui.SampleRateComboBox->setEnabled(!pSession->isActivated()); m_ui.TempoSpinBox->setTempo(m_props.timeScale.tempo(), false); m_ui.TempoSpinBox->setBeatsPerBar(m_props.timeScale.beatsPerBar(), false); m_ui.TempoSpinBox->setBeatDivisor(m_props.timeScale.beatDivisor(), false); m_ui.TicksPerBeatSpinBox->setValue(int(m_props.timeScale.ticksPerBeat())); // View properties... m_ui.SnapPerBeatComboBox->setCurrentIndex( qtractorTimeScale::indexFromSnap(m_props.timeScale.snapPerBeat())); m_ui.PixelsPerBeatSpinBox->setValue(int(m_props.timeScale.pixelsPerBeat())); m_ui.HorizontalZoomSpinBox->setValue(int(m_props.timeScale.horizontalZoom())); m_ui.VerticalZoomSpinBox->setValue(int(m_props.timeScale.verticalZoom())); // Start editing session name, if empty... m_ui.SessionNameLineEdit->setEnabled(m_bSessionDir); m_ui.SessionDirComboBox->setEnabled(m_bSessionDir); m_ui.SessionDirToolButton->setEnabled(m_bSessionDir); m_ui.AutoSessionDirCheckBox->setEnabled(m_bNewSession); m_ui.AutoSessionDirCheckBox->setVisible(m_bNewSession); if (m_bNewSession) m_ui.SessionNameLineEdit->setFocus(); // Backup clean. m_iDirtyCount = 0; // Done. stabilizeForm(); } // Retrieve the accepted session properties, if the case arises. const qtractorSession::Properties& qtractorSessionForm::properties (void) { return m_props; } // Accept settings (OK button slot). void qtractorSessionForm::accept (void) { // Check if session directory is new... QDir dir; const QString& sSessionDir = m_ui.SessionDirComboBox->currentText(); while (!dir.exists(sSessionDir)) { // Ask user... if (QMessageBox::warning(this, tr("Warning"), tr("Session directory does not exist:\n\n" "\"%1\"\n\n" "Do you want to create it?").arg(sSessionDir), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; // Proceed... dir.mkpath(sSessionDir); } // Save options... if (m_iDirtyCount > 0) { // Make changes permanent... if (m_bSessionDir) { m_props.sessionName = m_ui.SessionNameLineEdit->text().trimmed(); m_props.sessionDir = m_ui.SessionDirComboBox->currentText(); } m_props.description = m_ui.DescriptionTextEdit->toPlainText().trimmed(); // Time properties... m_props.timeScale.setSampleRate( m_ui.SampleRateComboBox->currentText().toUInt()); m_props.timeScale.setTempo(m_ui.TempoSpinBox->tempo()); m_props.timeScale.setBeatType(2); m_props.timeScale.setBeatsPerBar(m_ui.TempoSpinBox->beatsPerBar()); m_props.timeScale.setBeatDivisor(m_ui.TempoSpinBox->beatDivisor()); m_props.timeScale.setTicksPerBeat(m_ui.TicksPerBeatSpinBox->value()); // View properties... m_props.timeScale.setSnapPerBeat(qtractorTimeScale::snapFromIndex( m_ui.SnapPerBeatComboBox->currentIndex())); m_props.timeScale.setPixelsPerBeat(m_ui.PixelsPerBeatSpinBox->value()); m_props.timeScale.setHorizontalZoom(m_ui.HorizontalZoomSpinBox->value()); m_props.timeScale.setVerticalZoom(m_ui.VerticalZoomSpinBox->value()); // Reset dirty flag. m_iDirtyCount = 0; } // Save other conveniency options... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && m_bSessionDir) { pOptions->bAutoSessionDir = m_ui.AutoSessionDirCheckBox->isChecked(); pOptions->saveComboBoxHistory(m_ui.SessionDirComboBox); } // Just go with dialog acceptance. QDialog::accept(); } // Reject settings (Cancel button slot). void qtractorSessionForm::reject (void) { bool bReject = true; // Check if there's any pending changes... if (m_iDirtyCount > 0) { QMessageBox::StandardButtons buttons = QMessageBox::Discard | QMessageBox::Cancel; if (m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->isEnabled()) buttons |= QMessageBox::Apply; switch (QMessageBox::warning(this, tr("Warning"), tr("Some settings have been changed.\n\n" "Do you want to apply the changes?"), buttons)) { case QMessageBox::Apply: accept(); return; case QMessageBox::Discard: break; default: // Cancel. bReject = false; } } if (bReject) QDialog::reject(); } // Dirty up settings. void qtractorSessionForm::changed (void) { ++m_iDirtyCount; stabilizeForm(); } // Session name in-flight change. void qtractorSessionForm::changeSessionName ( const QString& sSessionName ) { if (m_bNewSession && m_ui.AutoSessionDirCheckBox->isChecked()) { QFileInfo fi(m_sSessionDir); fi.setFile(QDir(fi.filePath()), sSessionName); const bool bBlockSignals = m_ui.SessionDirComboBox->blockSignals(true); m_ui.SessionDirComboBox->setEditText(fi.absoluteFilePath()); m_ui.SessionDirComboBox->blockSignals(bBlockSignals); } changed(); } // Session directory auto-name change. void qtractorSessionForm::changeAutoSessionDir ( bool bOn ) { if (bOn && m_bNewSession) { QFileInfo fi(m_sSessionDir); const QString& sSessionName = m_ui.SessionNameLineEdit->text(); fi.setFile(QDir(fi.filePath()), sSessionName); const bool bBlockSignals = m_ui.SessionDirComboBox->blockSignals(true); m_ui.SessionDirComboBox->setEditText(fi.absoluteFilePath()); m_ui.SessionDirComboBox->blockSignals(bBlockSignals); } } // Session directory in-flight change. void qtractorSessionForm::changeSessionDir ( const QString& sSessionDir ) { if (sSessionDir.isEmpty()) m_ui.SessionDirComboBox->setEditText(m_sSessionDir); else if (m_bNewSession && m_ui.AutoSessionDirCheckBox->isChecked()) { const QFileInfo fi(sSessionDir); if (fi.absolutePath() == m_sSessionDir) { const QString& sSessionName = fi.fileName(); if (!sSessionName.isEmpty()) { const bool bBlockSignals = m_ui.SessionNameLineEdit->blockSignals(true); m_ui.SessionNameLineEdit->setText(sSessionName); m_ui.SessionNameLineEdit->blockSignals(bBlockSignals); } } } changed(); } // Browse for session directory. void qtractorSessionForm::browseSessionDir (void) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; const QString& sTitle = tr("Session Directory"); QWidget *pParentWidget = nullptr; QFileDialog::Options options = QFileDialog::ShowDirsOnly; if (pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) QString sSessionDir = QFileDialog::getExistingDirectory(pParentWidget, sTitle, m_ui.SessionDirComboBox->currentText(), options); #else // Construct open-directory dialog... QFileDialog fileDialog(pParentWidget, sTitle, m_ui.SessionDirComboBox->currentText()); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::DirectoryOnly); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (!fileDialog.exec()) return; QString sSessionDir = fileDialog.selectedFiles().first(); #endif if (sSessionDir.isEmpty()) return; m_sSessionDir = sSessionDir; if (m_bNewSession && m_ui.AutoSessionDirCheckBox->isChecked()) { const QString& sSessionName = m_ui.SessionNameLineEdit->text(); QFileInfo fi(sSessionDir); fi.setFile(QDir(fi.filePath()), sSessionName); sSessionDir = fi.absoluteFilePath(); } const bool bBlockSignals = m_ui.SessionDirComboBox->blockSignals(true); m_ui.SessionDirComboBox->setEditText(sSessionDir); m_ui.SessionDirComboBox->blockSignals(bBlockSignals); m_ui.SessionDirComboBox->setFocus(); changed(); } // Stabilize current form state. void qtractorSessionForm::stabilizeForm (void) { const QString& sSessionDir = m_ui.SessionDirComboBox->currentText(); bool bValid = !m_ui.SessionNameLineEdit->text().isEmpty(); bValid = bValid && !sSessionDir.isEmpty(); if (bValid) { QFileInfo fi(sSessionDir); bValid = bValid && (fi.canonicalFilePath() != QDir::homePath()); while (bValid && !fi.exists()) fi.setFile(fi.path()); bValid = bValid && fi.isDir() && fi.isReadable() && fi.isWritable(); } // bValid = bValid && !m_ui.DescriptionTextEdit->text().isEmpty(); m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(bValid); } // end of qtractorSessionForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorMidiToolsForm.cpp0000644000000000000000000000013215124701674020325 xustar0030 mtime=1767080892.795263445 30 atime=1767080892.795263445 30 ctime=1767080892.795263445 qtractor-1.5.11/src/qtractorMidiToolsForm.cpp0000644000175000001440000016151615124701674020327 0ustar00rncbcusers// qtractorMidiToolsForm.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorMidiToolsForm.h" #include "qtractorAbout.h" #include "qtractorMidiEditor.h" #include "qtractorMidiClip.h" #include "qtractorMidiEditCommand.h" #include "qtractorTimeScaleCommand.h" #include "qtractorOptions.h" #include "qtractorSession.h" #include #include #include #include #include #include // This shall hold the default preset name. static QString g_sDefPreset; class TimeshiftCurve : public QWidget { public: // Constructor. TimeshiftCurve(QWidget *pParent = nullptr) : QWidget(pParent), m_p(0.0f) {} // Accessors. void setTimeshift(float p) { m_p = p; update(); } // Characteristic method. static float timeshift(float t, float p) { #if 0//TIMESHIFT_LOGSCALE if (p > 0.0f) t = ::sqrtf(t * ::powf(1.0f - (10.0f * ::logf(t) / p), 0.1f * p)); else if (p < 0.0f) t = ::sqrtf(1.0f - ((1.0f - t) * ::powf(1.0f + (::logf(1.0f - t) / p), -p))); #else if (p > 0.0f) t = 1.0f - ::powf(1.0f - t, 1.0f / (1.0f - 0.01f * (p + 1e-9f))); else if (p < 0.0f) t = 1.0f - ::powf(1.0f - t, 1.0f + 0.01f * p); #endif return t; } protected: // Paint event method. void paintEvent(QPaintEvent */*pPaintEvent*/) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); const int w = QWidget::width(); const int h = QWidget::height(); const int x0 = w >> 1; const int y0 = h >> 1; QPen pen(Qt::gray); painter.setPen(pen); painter.drawLine(x0, 0, x0, h); painter.drawLine(0, y0, w, y0); QPainterPath path; path.moveTo(0, h); for (int x = 4; x < w; x += 4) { const float t = float(x) / float(w); path.lineTo(x, h - int(timeshift(t, m_p) * float(h))); } path.lineTo(w, 0); pen.setColor(Qt::red); pen.setWidth(2); painter.setPen(pen); painter.drawPath(path); } private: // Instance variables float m_p; }; //---------------------------------------------------------------------------- // qtractorMidiToolsForm -- UI wrapper form. // Constructor. qtractorMidiToolsForm::qtractorMidiToolsForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); m_pTimeScale = nullptr; m_iDirtyCount = 0; m_iUpdate = 0; // Special timeshift characteristic curve display... m_pTimeshiftCurve = new TimeshiftCurve(); QVBoxLayout *pFrameLayout = new QVBoxLayout(); pFrameLayout->setContentsMargins(1, 1, 1, 1); pFrameLayout->addWidget(m_pTimeshiftCurve); m_ui.TimeshiftFrame->setLayout(pFrameLayout); m_ui.PresetNameComboBox->setValidator( new QRegularExpressionValidator( QRegularExpression("[\\w-]+"), m_ui.PresetNameComboBox)); m_ui.PresetNameComboBox->setInsertPolicy(QComboBox::NoInsert); if (g_sDefPreset.isEmpty()) g_sDefPreset = tr("(default)"); // Set some time spin-box specialties... m_ui.TransposeTimeSpinBox->setDeltaValue(true); m_ui.ResizeDurationSpinBox->setDeltaValue(true); // Reinitialize random seed. ::srand(::time(nullptr)); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { // Copy from global time-scale instance... m_pTimeScale = new qtractorTimeScale(*pSession->timeScale()); m_ui.TransposeTimeSpinBox->setTimeScale(m_pTimeScale); m_ui.ResizeDurationSpinBox->setTimeScale(m_pTimeScale); // Fill-up snap-per-beat items... const QIcon& snapIcon = QIcon::fromTheme("itemBeat"); const QSize snapIconSize(8, 16); const QStringList& snapItems = qtractorTimeScale::snapItems(); QStringListIterator snapIter(snapItems); m_ui.QuantizeTimeComboBox->clear(); m_ui.QuantizeTimeComboBox->setIconSize(snapIconSize); // snapIter.toFront(); snapIter.next(); while (snapIter.hasNext()) m_ui.QuantizeTimeComboBox->addItem(snapIcon, snapIter.next()); // m_ui.QuantizeTimeComboBox->insertItems(0, snapItems); m_ui.QuantizeDurationComboBox->clear(); m_ui.QuantizeDurationComboBox->setIconSize(snapIconSize); snapIter.toFront(); snapIter.next(); while (snapIter.hasNext()) m_ui.QuantizeDurationComboBox->addItem(snapIcon, snapIter.next()); // m_ui.QuantizeDurationComboBox->insertItems(0, snapItems); m_ui.QuantizeSwingComboBox->clear(); m_ui.QuantizeSwingComboBox->setIconSize(snapIconSize); snapIter.toFront(); snapIter.next(); while (snapIter.hasNext()) m_ui.QuantizeSwingComboBox->addItem(snapIcon, snapIter.next()); // m_ui.QuantizeSwingComboBox->insertItems(0, snapItems); m_ui.ResizeLegatoLengthComboBox->clear(); m_ui.ResizeLegatoLengthComboBox->setIconSize(snapIconSize); snapIter.toFront(); snapIter.next(); while (snapIter.hasNext()) m_ui.ResizeLegatoLengthComboBox->addItem(snapIcon, snapIter.next()); // m_ui.ResizeLegatoLengthComboBox->insertItems(0, snapItems); m_ui.ResizeSplitLengthComboBox->clear(); m_ui.ResizeSplitLengthComboBox->setIconSize(snapIconSize); snapIter.toFront(); snapIter.next(); while (snapIter.hasNext()) m_ui.ResizeSplitLengthComboBox->addItem(snapIcon, snapIter.next()); // m_ui.ResizeSplitLengthComboBox->insertItems(0, snapItems); // Default quantization value... unsigned short iSnapPerBeat = m_pTimeScale->snapPerBeat(); if (iSnapPerBeat > 0) --iSnapPerBeat; const int iSnapIndex = qtractorTimeScale::indexFromSnap(iSnapPerBeat); m_ui.QuantizeTimeComboBox->setCurrentIndex(iSnapIndex); m_ui.QuantizeDurationComboBox->setCurrentIndex(iSnapIndex); m_ui.QuantizeSwingComboBox->setCurrentIndex(0); m_ui.QuantizeSwingTypeComboBox->setCurrentIndex(0); m_ui.ResizeLegatoLengthComboBox->setCurrentIndex(iSnapIndex); m_ui.ResizeSplitLengthComboBox->setCurrentIndex(iSnapIndex); // Initial tempo-ramp range... if (pSession->editHead() < pSession->editTail()) { qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(pSession->editHead()); const float T1 = pNode->tempo; const unsigned short iBeatsPerBar1 = pNode->beatsPerBar; const unsigned short iBeatDivisor1 = pNode->beatDivisor; qtractorTimeScale::Node *pPrev = pNode->prev(); const float T0 = pPrev ? pPrev->tempo : T1; const unsigned short iBeatsPerBar0 = pPrev ? pPrev->beatsPerBar : iBeatsPerBar1; const unsigned short iBeatDivisor0 = pPrev ? pPrev->beatDivisor : iBeatDivisor1; m_ui.TemporampFromSpinBox->setTempo(T0); m_ui.TemporampFromSpinBox->setBeatsPerBar(iBeatsPerBar0); m_ui.TemporampFromSpinBox->setBeatDivisor(iBeatDivisor0); m_ui.TemporampToSpinBox->setTempo(T1); m_ui.TemporampToSpinBox->setBeatsPerBar(iBeatsPerBar1); m_ui.TemporampToSpinBox->setBeatDivisor(iBeatDivisor1); } else { m_ui.Timeshift->setEnabled(false); m_ui.Temporamp->setEnabled(false); } } // Scale-quantize stuff... m_ui.QuantizeScaleKeyComboBox->clear(); m_ui.QuantizeScaleKeyComboBox->insertItems(0, qtractorMidiEditor::scaleKeyNames()); m_ui.QuantizeScaleKeyComboBox->setCurrentIndex(0); m_ui.QuantizeScaleComboBox->clear(); m_ui.QuantizeScaleComboBox->insertItems(0, qtractorMidiEditor::scaleTypeNames()); m_ui.QuantizeScaleComboBox->setCurrentIndex(0); // Choose BBT to be default format here. formatChanged(qtractorTimeScale::BBT); // Load initial preset names; loadPreset(g_sDefPreset); timeshiftSpinBoxChanged(m_ui.TimeshiftSpinBox->value()); refreshPresets(); // Try to restore old window positioning. // adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.PresetNameComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(presetChanged(const QString&))); QObject::connect(m_ui.PresetNameComboBox, SIGNAL(activated(int)), SLOT(presetActivated(int))); QObject::connect(m_ui.PresetSaveToolButton, SIGNAL(clicked()), SLOT(presetSave())); QObject::connect(m_ui.PresetDeleteToolButton, SIGNAL(clicked()), SLOT(presetDelete())); QObject::connect(m_ui.QuantizeCheckBox, SIGNAL(toggled(bool)), SLOT(stabilizeForm())); QObject::connect(m_ui.QuantizeTimeCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.QuantizeTimeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.QuantizeTimeSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.QuantizeDurationCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.QuantizeDurationComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.QuantizeDurationSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.QuantizeSwingCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.QuantizeSwingComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.QuantizeSwingSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.QuantizeSwingTypeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.QuantizeSwingSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.QuantizeScaleCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.QuantizeScaleKeyComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.QuantizeScaleComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.TransposeCheckBox, SIGNAL(toggled(bool)), SLOT(stabilizeForm())); QObject::connect(m_ui.TransposeNoteCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.TransposeNoteSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.TransposeTimeCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.TransposeTimeSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(changed())); QObject::connect(m_ui.TransposeFormatComboBox, SIGNAL(activated(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.TransposeReverseCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.NormalizeCheckBox, SIGNAL(toggled(bool)), SLOT(stabilizeForm())); QObject::connect(m_ui.NormalizePercentCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.NormalizePercentSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.NormalizeValueCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.NormalizeValueSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.NormalizeCompressCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.RandomizeCheckBox, SIGNAL(toggled(bool)), SLOT(stabilizeForm())); QObject::connect(m_ui.RandomizeNoteCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.RandomizeNoteSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.RandomizeTimeCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.RandomizeTimeSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.RandomizeDurationCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.RandomizeDurationSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.RandomizeValueCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.RandomizeValueSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.ResizeCheckBox, SIGNAL(toggled(bool)), SLOT(stabilizeForm())); QObject::connect(m_ui.ResizeDurationCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.ResizeDurationSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(changed())); QObject::connect(m_ui.ResizeDurationFormatComboBox, SIGNAL(activated(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.ResizeValueCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.ResizeValueSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.ResizeValue2ComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.ResizeValue2SpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.ResizeLegatoCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.ResizeLegatoTypeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.ResizeLegatoLengthComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.ResizeLegatoModeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.ResizeJoinCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.ResizeSplitCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.ResizeSplitLengthComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.ResizeSplitOffsetComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.RescaleCheckBox, SIGNAL(toggled(bool)), SLOT(stabilizeForm())); QObject::connect(m_ui.RescaleTimeCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.RescaleTimeSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.RescaleDurationCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.RescaleDurationSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.RescaleValueCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.RescaleValueSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.RescaleInvertCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.TimeshiftCheckBox, SIGNAL(toggled(bool)), SLOT(stabilizeForm())); QObject::connect(m_ui.TimeshiftSpinBox, SIGNAL(valueChanged(double)), SLOT(timeshiftSpinBoxChanged(double))); QObject::connect(m_ui.TimeshiftSlider, SIGNAL(valueChanged(int)), SLOT(timeshiftSliderChanged(int))); QObject::connect(m_ui.TimeshiftDurationCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.TemporampCheckBox, SIGNAL(toggled(bool)), SLOT(stabilizeForm())); QObject::connect(m_ui.TemporampFromSpinBox, SIGNAL(valueChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.TemporampToSpinBox, SIGNAL(valueChanged(const QString&)), SLOT(changed())); QObject::connect(m_ui.TemporampDurationCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); } // Destructor. qtractorMidiToolsForm::~qtractorMidiToolsForm (void) { qDeleteAll(m_timeScaleNodeCommands); m_timeScaleNodeCommands.clear(); // Don't forget to get rid of local time-scale instance... if (m_pTimeScale) delete m_pTimeScale; } // Set (initial) tool page. void qtractorMidiToolsForm::setToolIndex ( int iToolIndex ) { // Set the proper tool page. m_ui.ToolTabWidget->setCurrentIndex(iToolIndex); switch (iToolIndex) { case qtractorMidiEditor::Quantize: m_ui.QuantizeCheckBox->setChecked(true); break; case qtractorMidiEditor::Transpose: m_ui.TransposeCheckBox->setChecked(true); break; case qtractorMidiEditor::Normalize: m_ui.NormalizeCheckBox->setChecked(true); break; case qtractorMidiEditor::Randomize: m_ui.RandomizeCheckBox->setChecked(true); break; case qtractorMidiEditor::Resize: m_ui.ResizeCheckBox->setChecked(true); break; case qtractorMidiEditor::Rescale: m_ui.RescaleCheckBox->setChecked(true); break; case qtractorMidiEditor::Timeshift: m_ui.TimeshiftCheckBox->setChecked(true); break; case qtractorMidiEditor::Temporamp: m_ui.TemporampCheckBox->setChecked(true); break; default: break; } // Done. stabilizeForm(); } // Retrieve the current export type, if the case arises. int qtractorMidiToolsForm::toolIndex (void) const { return m_ui.ToolTabWidget->currentIndex(); } // Preset management methods... void qtractorMidiToolsForm::loadPreset ( const QString& sPreset ) { // An existing preset is about to be loaded... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { QList vlist; QSettings& settings = pOptions->settings(); // Get the preset entry... settings.beginGroup("/MidiTools"); if (!sPreset.isEmpty() && sPreset != g_sDefPreset) settings.beginGroup('/' + sPreset); // Quantize tool... vlist = settings.value("/Quantize").toList(); if (vlist.count() > 4) { // m_ui.QuantizeCheckBox->setChecked(vlist[0].toBool()); m_ui.QuantizeTimeCheckBox->setChecked(vlist[1].toBool()); m_ui.QuantizeTimeComboBox->setCurrentIndex(vlist[2].toInt()); m_ui.QuantizeDurationCheckBox->setChecked(vlist[3].toBool()); m_ui.QuantizeDurationComboBox->setCurrentIndex(vlist[4].toInt()); } // Swing-quantize tool... if (vlist.count() > 8) { m_ui.QuantizeSwingCheckBox->setChecked(vlist[5].toBool()); m_ui.QuantizeSwingComboBox->setCurrentIndex(vlist[6].toInt()); m_ui.QuantizeSwingSpinBox->setValue(vlist[7].toDouble()); m_ui.QuantizeSwingTypeComboBox->setCurrentIndex(vlist[8].toInt()); } // Percent-quantize tool... if (vlist.count() > 10) { m_ui.QuantizeTimeSpinBox->setValue(vlist[9].toDouble()); m_ui.QuantizeDurationSpinBox->setValue(vlist[10].toDouble()); } // Scale-quantize tool... if (vlist.count() > 13) { m_ui.QuantizeScaleCheckBox->setChecked(vlist[11].toBool()); m_ui.QuantizeScaleKeyComboBox->setCurrentIndex(vlist[12].toInt()); m_ui.QuantizeScaleComboBox->setCurrentIndex(vlist[13].toInt()); } // Transpose tool... vlist = settings.value("/Transpose").toList(); if (vlist.count() > 4) { // m_ui.TransposeCheckBox->setChecked(vlist[0].toBool()); m_ui.TransposeNoteCheckBox->setChecked(vlist[1].toBool()); m_ui.TransposeNoteSpinBox->setValue(vlist[2].toInt()); m_ui.TransposeTimeCheckBox->setChecked(vlist[3].toBool()); m_ui.TransposeTimeSpinBox->setValue(vlist[4].toUInt()); } // Transpose/reverse tool... if (vlist.count() > 5) m_ui.TransposeReverseCheckBox->setChecked(vlist[5].toBool()); // Normalize tool... vlist = settings.value("/Normalize").toList(); if (vlist.count() > 4) { // m_ui.NormalizeCheckBox->setChecked(vlist[0].toBool()); m_ui.NormalizePercentCheckBox->setChecked(vlist[1].toBool()); m_ui.NormalizePercentSpinBox->setValue(vlist[2].toDouble()); m_ui.NormalizeValueCheckBox->setChecked(vlist[3].toBool()); m_ui.NormalizeValueSpinBox->setValue(vlist[4].toInt()); } // Normalize/compress tool... if (vlist.count() > 5) m_ui.NormalizeCompressCheckBox->setChecked(vlist[5].toBool()); // Randomize tool... vlist = settings.value("/Randomize").toList(); if (vlist.count() > 8) { // m_ui.RandomizeCheckBox->setChecked(vlist[0].toBool()); m_ui.RandomizeNoteCheckBox->setChecked(vlist[1].toBool()); m_ui.RandomizeNoteSpinBox->setValue(vlist[2].toDouble()); m_ui.RandomizeTimeCheckBox->setChecked(vlist[3].toBool()); m_ui.RandomizeTimeSpinBox->setValue(vlist[4].toDouble()); m_ui.RandomizeDurationCheckBox->setChecked(vlist[5].toBool()); m_ui.RandomizeDurationSpinBox->setValue(vlist[6].toDouble()); m_ui.RandomizeValueCheckBox->setChecked(vlist[7].toBool()); m_ui.RandomizeValueSpinBox->setValue(vlist[8].toDouble()); } // Resize tool... vlist = settings.value("/Resize").toList(); if (vlist.count() > 4) { // m_ui.ResizeCheckBox->setChecked(vlist[0].toBool()); m_ui.ResizeValueCheckBox->setChecked(vlist[1].toBool()); m_ui.ResizeValueSpinBox->setValue(vlist[2].toInt()); m_ui.ResizeDurationCheckBox->setChecked(vlist[3].toBool()); m_ui.ResizeDurationSpinBox->setValue(vlist[4].toUInt()); } // Resize value mode tool... if (vlist.count() > 6) { m_ui.ResizeValue2ComboBox->setCurrentIndex(vlist[5].toInt()); m_ui.ResizeValue2SpinBox->setValue(vlist[6].toInt()); } // Resize legato mode tool... if (vlist.count() > 10) { m_ui.ResizeLegatoCheckBox->setChecked(vlist[7].toBool()); m_ui.ResizeLegatoTypeComboBox->setCurrentIndex(vlist[8].toInt()); m_ui.ResizeLegatoLengthComboBox->setCurrentIndex(vlist[9].toInt()); m_ui.ResizeLegatoModeComboBox->setCurrentIndex(vlist[10].toInt()); } // Resize join/split tool... if (vlist.count() > 14) { m_ui.ResizeJoinCheckBox->setChecked(vlist[11].toBool()); m_ui.ResizeSplitCheckBox->setChecked(vlist[12].toBool()); m_ui.ResizeSplitLengthComboBox->setCurrentIndex(vlist[13].toInt()); m_ui.ResizeSplitOffsetComboBox->setCurrentIndex(vlist[14].toInt()); } // Rescale tool... vlist = settings.value("/Rescale").toList(); if (vlist.count() > 6) { // m_ui.RescaleCheckBox->setChecked(vlist[0].toBool()); m_ui.RescaleTimeCheckBox->setChecked(vlist[1].toBool()); m_ui.RescaleTimeSpinBox->setValue(vlist[2].toDouble()); m_ui.RescaleDurationCheckBox->setChecked(vlist[3].toBool()); m_ui.RescaleDurationSpinBox->setValue(vlist[4].toDouble()); m_ui.RescaleValueCheckBox->setChecked(vlist[5].toBool()); m_ui.RescaleValueSpinBox->setValue(vlist[6].toDouble()); } // Rescale/invert tool... if (vlist.count() > 7) m_ui.RescaleInvertCheckBox->setChecked(vlist[7].toBool()); // Timeshift tool... vlist = settings.value("/Timeshift").toList(); if (vlist.count() > 2) { // m_ui.TimeshiftCheckBox->setChecked(vlist[0].toBool()); m_ui.TimeshiftSpinBox->setValue(vlist[1].toDouble()); m_ui.TimeshiftDurationCheckBox->setChecked(vlist[2].toBool()); } // Temporamp tool... vlist = settings.value("/Temporamp").toList(); if (vlist.count() > 3) { // m_ui.TemporampCheckBox->setChecked(vlist[0].toBool()); // m_ui.TemporampFromSpinBox->setTempo(vlist[1].toDouble()); m_ui.TemporampToSpinBox->setTempo(float(vlist[2].toDouble())); m_ui.TemporampDurationCheckBox->setChecked(float(vlist[3].toBool())); } // All loaded. if (!sPreset.isEmpty() && sPreset != g_sDefPreset) settings.endGroup(); settings.endGroup(); } } void qtractorMidiToolsForm::savePreset ( const QString& sPreset ) { // The current state preset is about to be saved... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { QList vlist; QSettings& settings = pOptions->settings(); // Set preset entry... settings.beginGroup("/MidiTools"); if (!sPreset.isEmpty() && sPreset != g_sDefPreset) settings.beginGroup('/' + sPreset); // Quantize tool... vlist.clear(); vlist.append(m_ui.QuantizeCheckBox->isChecked()); vlist.append(m_ui.QuantizeTimeCheckBox->isChecked()); vlist.append(m_ui.QuantizeTimeComboBox->currentIndex()); vlist.append(m_ui.QuantizeDurationCheckBox->isChecked()); vlist.append(m_ui.QuantizeDurationComboBox->currentIndex()); vlist.append(m_ui.QuantizeSwingCheckBox->isChecked()); vlist.append(m_ui.QuantizeSwingComboBox->currentIndex()); vlist.append(m_ui.QuantizeSwingSpinBox->value()); vlist.append(m_ui.QuantizeSwingTypeComboBox->currentIndex()); vlist.append(m_ui.QuantizeTimeSpinBox->value()); vlist.append(m_ui.QuantizeDurationSpinBox->value()); vlist.append(m_ui.QuantizeScaleCheckBox->isChecked()); vlist.append(m_ui.QuantizeScaleKeyComboBox->currentIndex()); vlist.append(m_ui.QuantizeScaleComboBox->currentIndex()); settings.setValue("/Quantize", vlist); // Transpose tool... vlist.clear(); vlist.append(m_ui.TransposeCheckBox->isChecked()); vlist.append(m_ui.TransposeNoteCheckBox->isChecked()); vlist.append(m_ui.TransposeNoteSpinBox->value()); vlist.append(m_ui.TransposeTimeCheckBox->isChecked()); vlist.append((unsigned int) m_ui.TransposeTimeSpinBox->value()); vlist.append(m_ui.TransposeReverseCheckBox->isChecked()); settings.setValue("/Transpose", vlist); // Normalize tool... vlist.clear(); vlist.append(m_ui.NormalizeCheckBox->isChecked()); vlist.append(m_ui.NormalizePercentCheckBox->isChecked()); vlist.append(m_ui.NormalizePercentSpinBox->value()); vlist.append(m_ui.NormalizeValueCheckBox->isChecked()); vlist.append(m_ui.NormalizeValueSpinBox->value()); vlist.append(m_ui.NormalizeCompressCheckBox->isChecked()); settings.setValue("/Normalize", vlist); // Randomize tool... vlist.clear(); vlist.append(m_ui.RandomizeCheckBox->isChecked()); vlist.append(m_ui.RandomizeNoteCheckBox->isChecked()); vlist.append(m_ui.RandomizeNoteSpinBox->value()); vlist.append(m_ui.RandomizeTimeCheckBox->isChecked()); vlist.append(m_ui.RandomizeTimeSpinBox->value()); vlist.append(m_ui.RandomizeDurationCheckBox->isChecked()); vlist.append(m_ui.RandomizeDurationSpinBox->value()); vlist.append(m_ui.RandomizeValueCheckBox->isChecked()); vlist.append(m_ui.RandomizeValueSpinBox->value()); settings.setValue("/Randomize", vlist); // Resize tool... vlist.clear(); vlist.append(m_ui.ResizeCheckBox->isChecked()); vlist.append(m_ui.ResizeValueCheckBox->isChecked()); vlist.append(m_ui.ResizeValueSpinBox->value()); vlist.append(m_ui.ResizeDurationCheckBox->isChecked()); vlist.append((unsigned int) m_ui.ResizeDurationSpinBox->value()); vlist.append(m_ui.ResizeValue2ComboBox->currentIndex()); vlist.append(m_ui.ResizeValue2SpinBox->value()); vlist.append(m_ui.ResizeLegatoCheckBox->isChecked()); vlist.append(m_ui.ResizeLegatoTypeComboBox->currentIndex()); vlist.append(m_ui.ResizeLegatoLengthComboBox->currentIndex()); vlist.append(m_ui.ResizeLegatoModeComboBox->currentIndex()); vlist.append(m_ui.ResizeJoinCheckBox->isChecked()); vlist.append(m_ui.ResizeSplitCheckBox->isChecked()); vlist.append(m_ui.ResizeSplitLengthComboBox->currentIndex()); vlist.append(m_ui.ResizeSplitOffsetComboBox->currentIndex()); settings.setValue("/Resize", vlist); // Rescale tool... vlist.clear(); vlist.append(m_ui.RescaleCheckBox->isChecked()); vlist.append(m_ui.RescaleTimeCheckBox->isChecked()); vlist.append(m_ui.RescaleTimeSpinBox->value()); vlist.append(m_ui.RescaleDurationCheckBox->isChecked()); vlist.append(m_ui.RescaleDurationSpinBox->value()); vlist.append(m_ui.RescaleValueCheckBox->isChecked()); vlist.append(m_ui.RescaleValueSpinBox->value()); vlist.append(m_ui.RescaleInvertCheckBox->isChecked()); settings.setValue("/Rescale", vlist); // Timeshift tool... vlist.clear(); vlist.append(m_ui.TimeshiftCheckBox->isChecked()); vlist.append(m_ui.TimeshiftSpinBox->value()); vlist.append(m_ui.TimeshiftDurationCheckBox->isChecked()); settings.setValue("/Timeshift", vlist); // Temporamp tool... vlist.clear(); vlist.append(m_ui.TemporampCheckBox->isChecked()); vlist.append(m_ui.TemporampFromSpinBox->tempo()); vlist.append(m_ui.TemporampToSpinBox->tempo()); vlist.append(m_ui.TemporampDurationCheckBox->isChecked()); settings.setValue("/Temporamp", vlist); // All saved. if (!sPreset.isEmpty() && sPreset != g_sDefPreset) settings.endGroup(); settings.endGroup(); } } // Preset list loader. void qtractorMidiToolsForm::refreshPresets (void) { ++m_iUpdate; const QString sOldPreset = m_ui.PresetNameComboBox->currentText(); m_ui.PresetNameComboBox->clear(); qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { QSettings& settings = pOptions->settings(); settings.beginGroup("/MidiTools"); m_ui.PresetNameComboBox->insertItems(0, settings.childGroups()); m_ui.PresetNameComboBox->model()->sort(0); settings.endGroup(); } m_ui.PresetNameComboBox->addItem(g_sDefPreset); m_ui.PresetNameComboBox->setEditText(sOldPreset); m_iDirtyCount = 0; --m_iUpdate; stabilizeForm(); } // Preset management slots... void qtractorMidiToolsForm::presetChanged ( const QString& sPreset ) { if (m_iUpdate > 0) return; if (!sPreset.isEmpty() && m_ui.PresetNameComboBox->findText(sPreset) >= 0) ++m_iDirtyCount; stabilizeForm(); } void qtractorMidiToolsForm::presetActivated ( int iPreset ) { ++m_iUpdate; loadPreset(m_ui.PresetNameComboBox->itemText(iPreset)); m_iDirtyCount = 0; --m_iUpdate; stabilizeForm(); } void qtractorMidiToolsForm::presetSave (void) { savePreset(m_ui.PresetNameComboBox->currentText()); refreshPresets(); } void qtractorMidiToolsForm::presetDelete (void) { if (m_iUpdate > 0) return; const QString& sPreset = m_ui.PresetNameComboBox->currentText(); if (sPreset.isEmpty() || sPreset == g_sDefPreset) return; // A preset entry is about to be deleted... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { // Prompt user if he/she's sure about this... if (pOptions->bConfirmRemove) { if (QMessageBox::warning(this, tr("Warning"), tr("About to delete preset:\n\n" "\"%1\"\n\n" "Are you sure?") .arg(sPreset), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } // Go ahead... QSettings& settings = pOptions->settings(); settings.beginGroup("/MidiTools"); settings.remove(sPreset); settings.endGroup(); refreshPresets(); } } // Create edit command based on given selection. qtractorMidiEditCommand *qtractorMidiToolsForm::midiEditCommand ( qtractorMidiClip *pMidiClip, qtractorMidiEditSelect *pSelect, unsigned long iTimeOffset, unsigned long iTimeStart, unsigned long iTimeEnd ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return nullptr; // Create command, it will be handed over... qtractorMidiEditCommand *pMidiEditCommand = new qtractorMidiEditCommand(pMidiClip, tr("none")); // Set composite command title. QStringList tools; if (m_ui.QuantizeCheckBox->isChecked()) tools.append(tr("quantize")); if (m_ui.TransposeCheckBox->isChecked()) tools.append(tr("transpose")); if (m_ui.NormalizeCheckBox->isChecked()) tools.append(tr("normalize")); if (m_ui.RandomizeCheckBox->isChecked()) tools.append(tr("randomize")); if (m_ui.ResizeCheckBox->isChecked()) tools.append(tr("resize")); if (m_ui.RescaleCheckBox->isChecked()) tools.append(tr("rescale")); if (m_ui.TimeshiftCheckBox->isChecked()) tools.append(tr("timeshift")); if (m_ui.TemporampCheckBox->isChecked()) tools.append(tr("temporamp")); pMidiEditCommand->setName(tools.join(", ")); QList items = pSelect->items().keys(); if (m_ui.ResizeCheckBox->isChecked() && m_ui.ResizeLegatoCheckBox->isChecked()) { // Sort events in reverse event time... struct ReverseEventTime { bool operator() (qtractorMidiEvent *ev1, qtractorMidiEvent *ev2) const { return (ev1->time() > ev2->time()); } }; std::sort(items.begin(), items.end(), ReverseEventTime()); } QList::ConstIterator iter = items.constBegin(); const QList::ConstIterator& iter_end = items.constEnd(); // Seed time range with a value from the list of selected events. long iMinTime = iTimeOffset; long iMaxTime = iTimeOffset; if (pSelect->anchorEvent()) iMinTime = iMaxTime = pSelect->anchorEvent()->time() + iTimeOffset; long iMinTime2 = iMinTime; long iMaxTime2 = iMaxTime; if (iTimeStart < iTimeEnd) { iMinTime += long(iTimeStart); iMaxTime += long(iTimeEnd); } // First scan pass for the normalize and resize value ramp tools: // find maximum and minimum times and values from the selection... int iMaxValue = 0; int iMinValue = 0; if (m_ui.NormalizeCheckBox->isChecked() || (m_ui.TransposeCheckBox->isChecked() && m_ui.TransposeReverseCheckBox->isChecked()) || (m_ui.ResizeCheckBox->isChecked() && m_ui.ResizeValueCheckBox->isChecked() && m_ui.ResizeValue2ComboBox->currentIndex() > 0)) { // Make it through one time... for (int i = 0 ; iter != iter_end; ++i, ++iter) { qtractorMidiEvent *pEvent = *iter; const long iTime = pEvent->time() + iTimeOffset; const long iTime2 = iTime + pEvent->duration(); if (iMinTime > iTime) iMinTime = iTime; if (iMaxTime < iTime) iMaxTime = iTime; if (iMinTime2 > iTime || i == 0) iMinTime2 = iTime; if (iMaxTime2 < iTime2) iMaxTime2 = iTime2; const bool bPitchBend = (pEvent->type() == qtractorMidiEvent::PITCHBEND); const int iValue = (bPitchBend ? pEvent->pitchBend() : pEvent->value()); if (iMinValue > iValue || i == 0) iMinValue = iValue; if (iMaxValue < iValue) iMaxValue = iValue; } // Get it back to front... iter = items.constBegin(); } // Go for the main pass... qtractorTimeScale::Cursor cursor(m_pTimeScale); // Resize/Legato: to track last event... qtractorMidiEvent *pLastEvent = nullptr; // Resize/Legato/Join: to track note first and last events... QHash notes1; QHash notes2; for ( ; iter != iter_end; ++iter) { qtractorMidiEvent *pEvent = *iter; int iNote = int(pEvent->note()); long iTime = pEvent->time() + iTimeOffset; long iDuration = long(pEvent->duration()); const bool bPitchBend = ( pEvent->type() == qtractorMidiEvent::PITCHBEND); const bool b14bit = ( pEvent->type() == qtractorMidiEvent::CONTROL14 || pEvent->type() == qtractorMidiEvent::REGPARAM || pEvent->type() == qtractorMidiEvent::NONREGPARAM); int iValue = (bPitchBend ? pEvent->pitchBend() : pEvent->value()); qtractorTimeScale::Node *pNode = cursor.seekTick(iTime); // Quantize tool... if (m_ui.QuantizeCheckBox->isChecked()) { // Swing quantize... if (m_ui.QuantizeSwingCheckBox->isChecked()) { const unsigned short p = qtractorTimeScale::snapFromIndex( m_ui.QuantizeSwingComboBox->currentIndex() + 1); const unsigned long q = pNode->ticksPerBeat / p; if (q > 0) { const unsigned long t0 = q * (iTime / q); float d0 = 0.0f; if ((iTime / q) % 2) d0 = float(long(t0 + q) - long(iTime)); else d0 = float(long(iTime) - long(t0)); float ds = 0.01f * float(m_ui.QuantizeSwingSpinBox->value()); ds = ds * d0; const int n = m_ui.QuantizeSwingTypeComboBox->currentIndex(); for (int i = 0; i < n; ++i) // 0=Linear; 1=Quadratic; 2=Cubic. ds = (ds * d0) / float(q); iTime += long(ds); if (iTime < long(iTimeOffset)) iTime = long(iTimeOffset); } } // Time quantize... if (m_ui.QuantizeTimeCheckBox->isChecked()) { const unsigned short p = qtractorTimeScale::snapFromIndex( m_ui.QuantizeTimeComboBox->currentIndex() + 1); const unsigned long q = pNode->ticksPerBeat / p; iTime = q * ((iTime + (q >> 1)) / q); // Time percent quantize... const float delta = 0.01f * (100.0f - float(m_ui.QuantizeTimeSpinBox->value())) * float(long(pEvent->time() + iTimeOffset) - iTime); iTime += long(delta); if (iTime < long(iTimeOffset)) iTime = long(iTimeOffset); } // Duration quantize... if (m_ui.QuantizeDurationCheckBox->isChecked() && pEvent->type() == qtractorMidiEvent::NOTEON) { const unsigned short p = qtractorTimeScale::snapFromIndex( m_ui.QuantizeDurationComboBox->currentIndex() + 1); const unsigned long q = pNode->ticksPerBeat / p; iDuration = q * ((iDuration + q - 1) / q); // Duration percent quantize... const float delta = 0.01f * (100.0f - float(m_ui.QuantizeDurationSpinBox->value())) * float(long(pEvent->duration()) - iDuration); iDuration += long(delta); if (iDuration < 0) iDuration = 0; } // Scale quantize... if (m_ui.QuantizeScaleCheckBox->isChecked()) { iNote = qtractorMidiEditor::snapToScale(iNote, m_ui.QuantizeScaleKeyComboBox->currentIndex(), m_ui.QuantizeScaleComboBox->currentIndex()); } } // Transpose tool... if (m_ui.TransposeCheckBox->isChecked()) { if (m_ui.TransposeNoteCheckBox->isChecked() && pEvent->type() == qtractorMidiEvent::NOTEON) { iNote += m_ui.TransposeNoteSpinBox->value(); if (iNote < 0) iNote = 0; else if (iNote > 127) iNote = 127; } if (m_ui.TransposeTimeCheckBox->isChecked()) { iTime = pNode->tickFromFrame(pNode->frameFromTick(iTime) + m_ui.TransposeTimeSpinBox->value()); if (iTime < long(iTimeOffset)) iTime = long(iTimeOffset); } if (m_ui.TransposeReverseCheckBox->isChecked()) { iTime = iMinTime2 + iMaxTime2 - iTime - iDuration; if (iTime < long(iTimeOffset)) iTime = long(iTimeOffset); } } // Normalize tool... if (m_ui.NormalizeCheckBox->isChecked()) { float p, q = float(iMaxValue); if (m_ui.NormalizeValueCheckBox->isChecked()) p = float(m_ui.NormalizeValueSpinBox->value()); else if (b14bit) p = 16384.0f; else if (bPitchBend) p = 8192.0f; else p = 128.0f; if (m_ui.NormalizeCompressCheckBox->isChecked()) { float percent = 100.0f; if (m_ui.NormalizePercentCheckBox->isChecked()) percent = float(m_ui.NormalizePercentSpinBox->value()); float ratio = 0.0f; if (p > q && p > 0.0f) ratio = (q / p); else if (percent > 0.0f) ratio = (100.0f / percent); if (ratio > 0.0f) iValue = int(p + (float(iValue) - q) / ratio); } else { if (m_ui.NormalizePercentCheckBox->isChecked()) { p *= float(m_ui.NormalizePercentSpinBox->value()); q *= 100.0f; } if (q > 0.0f) iValue = int((p * float(iValue)) / q); } if (bPitchBend) { if (iValue > +8191) iValue = +8191; else if (iValue < -8191) iValue = -8191; } else { if (iValue > 16383 && b14bit) iValue = 16383; else if (iValue > 127) iValue = 127; else if (iValue < 0) iValue = 0; } } // Randomize tool... if (m_ui.RandomizeCheckBox->isChecked()) { float p; int q; if (m_ui.RandomizeNoteCheckBox->isChecked() && pEvent->type() == qtractorMidiEvent::NOTEON) { p = 0.01f * float(m_ui.RandomizeNoteSpinBox->value()); q = 127; if (p > 0.0f) { iNote += int(p * float(q - (::rand() % (q << 1)))); if (iNote > 127) iNote = 127; else if (iNote < 0) iNote = 0; } } if (m_ui.RandomizeTimeCheckBox->isChecked()) { p = 0.01f * float(m_ui.RandomizeTimeSpinBox->value()); q = pNode->ticksPerBeat; if (p > 0.0f) { iTime += long(p * float(q - (::rand() % (q << 1)))); if (iTime < long(iTimeOffset)) iTime = long(iTimeOffset); } } if (m_ui.RandomizeDurationCheckBox->isChecked()) { p = 0.01f * float(m_ui.RandomizeDurationSpinBox->value()); q = pNode->ticksPerBeat; if (p > 0.0f) { iDuration += long(p * float(q - (::rand() % (q << 1)))); if (iDuration < 0) iDuration = 0; } } if (m_ui.RandomizeValueCheckBox->isChecked()) { p = 0.01f * float(m_ui.RandomizeValueSpinBox->value()); q = (bPitchBend ? 8192 : 128); if (p > 0.0f) { iValue += int(p * float(q - (::rand() % (q << 1)))); if (bPitchBend) { if (iValue > +8191) iValue = +8191; else if (iValue < -8191) iValue = -8191; } else { if (iValue > 16383 && b14bit) iValue = 16383; else if (iValue > 127 && !b14bit) iValue = 127; else if (iValue < 0) iValue = 0; } } } } // Resize tool... if (m_ui.ResizeCheckBox->isChecked()) { if (m_ui.ResizeDurationCheckBox->isChecked() && pEvent->type() == qtractorMidiEvent::NOTEON) { const float T0 // @ iFrame == 0 = m_pTimeScale->nodes().first()->tempo; const float T1 = pNode->tempo; const unsigned long iFrames = qtractorTimeScale::uroundf( T0 * float(m_ui.ResizeDurationSpinBox->value()) / T1); iDuration = pNode->tickFromFrame( pNode->frameFromTick(iTime) + iFrames) - iTime; } if (m_ui.ResizeValueCheckBox->isChecked()) { const int p = (bPitchBend && iValue < 0 ? -1 : 1); // sign iValue = p * m_ui.ResizeValueSpinBox->value(); if (bPitchBend) iValue <<= 6; // *128 if (m_ui.ResizeValue2ComboBox->currentIndex() > 0) { int iValue2 = p * m_ui.ResizeValue2SpinBox->value(); if (bPitchBend) iValue2 <<= 6; // *128 const int iDeltaValue = iValue2 - iValue; const long iDeltaTime = iMaxTime - iMinTime; if (iDeltaTime > 0) iValue += iDeltaValue * (iTime - iMinTime) / iDeltaTime; } } if (m_ui.ResizeLegatoCheckBox->isChecked() && pEvent->type() == qtractorMidiEvent::NOTEON) { if (pLastEvent) { long d2 = long(float(pLastEvent->time() - pEvent->time())); const int i = m_ui.ResizeLegatoTypeComboBox->currentIndex(); if (i > 0) { const unsigned short p = qtractorTimeScale::snapFromIndex( m_ui.ResizeLegatoLengthComboBox->currentIndex() + 1); const unsigned long q = pNode->ticksPerBeat / p; d2 += (i == 1 ? -long(q) : +long(q)); } if (m_ui.ResizeLegatoModeComboBox->currentIndex() > 0) { if (iDuration < d2 && d2 > 0) iDuration = d2; } else if (d2 > 0) iDuration = d2; } pLastEvent = pEvent; } if (m_ui.ResizeJoinCheckBox->isChecked() && pEvent->type() == qtractorMidiEvent::NOTEON) { qtractorMidiEvent *pEvent1 = notes1.value(iNote, nullptr); qtractorMidiEvent *pEvent2 = notes2.value(iNote, nullptr); if (pEvent1 && pEvent2) { if (pEvent->time() < pEvent1->time()) { if (pEvent1 != pEvent2) pMidiEditCommand->removeEvent(pEvent1); notes1.insert(iNote, pEvent); } else if (pEvent->time() > pEvent2->time()) { if (pEvent2 != pEvent1) pMidiEditCommand->removeEvent(pEvent2); notes2.insert(iNote, pEvent); } else { pMidiEditCommand->removeEvent(pEvent); } } else { notes1.insert(iNote, pEvent); notes2.insert(iNote, pEvent); } } if (m_ui.ResizeSplitCheckBox->isChecked() && pEvent->type() == qtractorMidiEvent::NOTEON) { const unsigned short p = qtractorTimeScale::snapFromIndex( m_ui.ResizeSplitLengthComboBox->currentIndex() + 1); const unsigned long q = pNode->ticksPerBeat / p; if (q < iDuration) { unsigned long t0 = iTime; if (m_ui.ResizeSplitOffsetComboBox->currentIndex() > 0) t0 = q * ((t0 + (q >> 1)) / q); if (t0 < iTime) t0 += q; const unsigned long d0 = (t0 > iTime ? t0 - iTime : q); for (unsigned long d = d0; d < iDuration; d += q) { qtractorMidiEvent *pSplitEvent = new qtractorMidiEvent( iTime - iTimeOffset + d, pEvent->type(), iNote, iValue, iDuration < (d + q) ? iDuration - d : q); pMidiEditCommand->insertEvent(pSplitEvent); } iDuration = d0; } } } // Rescale tool... if (m_ui.RescaleCheckBox->isChecked()) { float p; if (m_ui.RescaleTimeCheckBox->isChecked()) { p = 0.01f * float(m_ui.RescaleTimeSpinBox->value()); iTime = iMinTime + long(p * float(iTime - iMinTime)); if (iTime < long(iTimeOffset)) iTime = long(iTimeOffset); } if (m_ui.RescaleDurationCheckBox->isChecked()) { p = 0.01f * float(m_ui.RescaleDurationSpinBox->value()); iDuration = long(p * float(iDuration)); if (iDuration < 0) iDuration = 0; } if (m_ui.RescaleValueCheckBox->isChecked()) { p = 0.01f * float(m_ui.RescaleValueSpinBox->value()); if (m_ui.RescaleInvertCheckBox->isChecked()) { if (bPitchBend) iValue = -iValue; else if (b14bit) iValue = 16384 - iValue; else iValue = 128 - iValue; } iValue = int(p * float(iValue)); if (bPitchBend) { if (iValue > +8191) iValue = +8191; else if (iValue < -8191) iValue = -8191; } else { if (iValue > 16383 && b14bit) iValue = 16383; else if (iValue > 127 && !b14bit) iValue = 127; else if (iValue < 0) iValue = 0; } } } // Timeshift tool... if (m_ui.TimeshiftCheckBox->isChecked()) { const unsigned long iEditHeadTime = pSession->tickFromFrame(pSession->editHead()); const unsigned long iEditTailTime = pSession->tickFromFrame(pSession->editTail()); const float d = float(iEditTailTime - iEditHeadTime); const float p = float(m_ui.TimeshiftSpinBox->value()); if ((p < -1e-6f || p > 1e-6f) && (d > 0.0f)) { const float t = float(iTime - iEditHeadTime); float t1 = t / d; if (t1 > 0.0f && t1 < 1.0f) t1 = TimeshiftCurve::timeshift(t1, p); iTime = t1 * d + float(iEditHeadTime); if (m_ui.TimeshiftDurationCheckBox->isChecked()) { float t2 = (t + float(iDuration)) / d; if (t2 > 0.0f && t2 < 1.0f) t2 = TimeshiftCurve::timeshift(t2, p); iDuration = t2 * d + float(iEditHeadTime) - iTime; } } } // Temporamp tool... if (m_ui.TemporampCheckBox->isChecked()) { const unsigned long iEditHeadTime = pSession->tickFromFrame(pSession->editHead()); const unsigned long iEditTailTime = pSession->tickFromFrame(pSession->editTail()); const float d = float(iEditTailTime - iEditHeadTime); const float T0 = m_ui.TemporampFromSpinBox->tempo(); const float T1 = m_ui.TemporampToSpinBox->tempo(); float t2 = float(iTime - iEditHeadTime) / d; const float s2 = (T1 - T0) / (T0 > T1 ? T0 : T1) * (1.0f - t2 * t2); if (m_ui.TemporampDurationCheckBox->isChecked()) { const float d2 = s2 * float(iDuration) / d; iDuration += qtractorTimeScale::uroundf(d2 * d); } t2 += s2 * t2 * ::expf(- t2 * M_PI); iTime = iEditHeadTime + qtractorTimeScale::uroundf(t2 * d); } // Make it to the event... pMidiEditCommand->updateEvent(pEvent, iNote, iTime - iTimeOffset, iDuration, iValue); } // Resize/Join: to track note first and last events... if (m_ui.ResizeCheckBox->isChecked() && m_ui.ResizeJoinCheckBox->isChecked()) { QHash::ConstIterator iter1 = notes1.constBegin(); const QHash::ConstIterator& iter1_end = notes1.constEnd(); for ( ; iter1 != iter1_end; ++iter1) { const int iNote = iter1.key(); qtractorMidiEvent *pEvent1 = iter1.value(); qtractorMidiEvent *pEvent2 = notes2.value(iNote, nullptr); if (pEvent2 && pEvent2 != pEvent1) { const unsigned long iTime = pEvent1->time(); const unsigned long iDuration = pEvent2->time() + pEvent2->duration() - iTime; pMidiEditCommand->resizeEventTime(pEvent1, iTime, iDuration); pMidiEditCommand->removeEvent(pEvent2); } } } // HACK: Add time-scale node for tempo ramp target, // iif not the same to current edit-head's tempo. // FIXME: conditional check-box at the UI level? if (m_ui.TemporampCheckBox->isChecked()) { qtractorTimeScale *pTimeScale = pSession->timeScale(); qtractorTimeScale::Cursor cursor(pTimeScale); const unsigned long iEditHead = pSession->editHead(); qtractorTimeScale::Node *pNode = cursor.seekFrame(iEditHead); const float T1 = m_ui.TemporampToSpinBox->tempo(); if (qAbs(T1 - pNode->tempo) > 0.05f) { const unsigned short iBeatsPerBar1 = m_ui.TemporampToSpinBox->beatsPerBar(); const unsigned short iBeatDivisor1 = m_ui.TemporampToSpinBox->beatDivisor(); m_timeScaleNodeCommands.append( new qtractorTimeScaleAddNodeCommand( pTimeScale, iEditHead, T1, pNode->beatType, iBeatsPerBar1, iBeatDivisor1)); } } // Done. return pMidiEditCommand; } // Common change slot. void qtractorMidiToolsForm::changed (void) { if (m_iUpdate > 0) return; ++m_iDirtyCount; stabilizeForm(); } // Accept settings (OK button slot). void qtractorMidiToolsForm::accept (void) { // Save as default preset... savePreset(g_sDefPreset); // Just go with dialog acceptance. QDialog::accept(); } // Reject settings (Cancel button slot). void qtractorMidiToolsForm::reject (void) { // Bail out... QDialog::reject(); } // Display format has changed. void qtractorMidiToolsForm::formatChanged ( int iDisplayFormat ) { const qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(iDisplayFormat); m_ui.TransposeFormatComboBox->setCurrentIndex(iDisplayFormat); m_ui.ResizeDurationFormatComboBox->setCurrentIndex(iDisplayFormat); if (m_pTimeScale) { // Set from local time-scale instance... //m_pTimeScale->setDisplayFormat(displayFormat); m_ui.TransposeTimeSpinBox->setDisplayFormat(displayFormat); m_ui.ResizeDurationSpinBox->setDisplayFormat(displayFormat); } stabilizeForm(); } // Stabilize current form state. void qtractorMidiToolsForm::stabilizeForm (void) { int iEnabled = 0; bool bEnabled; bool bEnabled2; // Preset status... const QString& sPreset = m_ui.PresetNameComboBox->currentText(); const bool bExists = (m_ui.PresetNameComboBox->findText(sPreset) >= 0); bEnabled = (!sPreset.isEmpty() && sPreset != g_sDefPreset); m_ui.PresetSaveToolButton->setEnabled(bEnabled && (!bExists || m_iDirtyCount > 0)); m_ui.PresetDeleteToolButton->setEnabled(bEnabled && bExists); // Quantize tool... bEnabled = m_ui.QuantizeCheckBox->isChecked(); m_ui.QuantizeTimeCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.QuantizeTimeCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.QuantizeTimeComboBox->setEnabled(bEnabled2); m_ui.QuantizeTimeSpinBox->setEnabled(bEnabled2); m_ui.QuantizeDurationCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.QuantizeDurationCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.QuantizeDurationComboBox->setEnabled(bEnabled2); m_ui.QuantizeDurationSpinBox->setEnabled(bEnabled2); // if (bEnabled) // bEnabled = m_ui.QuantizeTimeCheckBox->isChecked(); m_ui.QuantizeSwingCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.QuantizeSwingCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.QuantizeSwingComboBox->setEnabled(bEnabled2); m_ui.QuantizeSwingSpinBox->setEnabled(bEnabled2); m_ui.QuantizeSwingTypeComboBox->setEnabled(bEnabled2); m_ui.QuantizeScaleCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.QuantizeScaleCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.QuantizeScaleKeyComboBox->setEnabled(bEnabled2); m_ui.QuantizeScaleComboBox->setEnabled(bEnabled2); // Transpose tool... bEnabled = m_ui.TransposeCheckBox->isChecked(); m_ui.TransposeNoteCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.TransposeNoteCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.TransposeNoteSpinBox->setEnabled(bEnabled2); m_ui.TransposeTimeCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.TransposeTimeCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.TransposeTimeSpinBox->setEnabled(bEnabled2); m_ui.TransposeFormatComboBox->setEnabled(bEnabled2); m_ui.TransposeReverseCheckBox->setEnabled(bEnabled2); bEnabled2 = bEnabled2 && m_ui.TransposeReverseCheckBox->isChecked(); if (bEnabled2) ++iEnabled; // Normalize tool... bEnabled = m_ui.NormalizeCheckBox->isChecked(); m_ui.NormalizePercentCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.NormalizePercentCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.NormalizePercentSpinBox->setEnabled(bEnabled2); m_ui.NormalizeValueCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.NormalizeValueCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.NormalizeValueSpinBox->setEnabled(bEnabled2); m_ui.NormalizeCompressCheckBox->setEnabled(bEnabled); // Randomize tool... bEnabled = m_ui.RandomizeCheckBox->isChecked(); m_ui.RandomizeNoteCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.RandomizeNoteCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.RandomizeNoteSpinBox->setEnabled(bEnabled2); m_ui.RandomizeTimeCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.RandomizeTimeCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.RandomizeTimeSpinBox->setEnabled(bEnabled2); m_ui.RandomizeDurationCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.RandomizeDurationCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.RandomizeDurationSpinBox->setEnabled(bEnabled2); m_ui.RandomizeValueCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.RandomizeValueCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.RandomizeValueSpinBox->setEnabled(bEnabled2); // Resize tool... bEnabled = m_ui.ResizeCheckBox->isChecked(); m_ui.ResizeDurationCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.ResizeDurationCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.ResizeDurationSpinBox->setEnabled(bEnabled2); m_ui.ResizeDurationFormatComboBox->setEnabled(bEnabled2); m_ui.ResizeValueCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.ResizeValueCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.ResizeValueSpinBox->setEnabled(bEnabled2); m_ui.ResizeValue2ComboBox->setEnabled(bEnabled2); if (bEnabled2) bEnabled2 = (m_ui.ResizeValue2ComboBox->currentIndex() > 0); m_ui.ResizeValue2SpinBox->setEnabled(bEnabled2); m_ui.ResizeLegatoCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.ResizeLegatoCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.ResizeLegatoTypeComboBox->setEnabled(bEnabled2); m_ui.ResizeLegatoLengthComboBox->setEnabled(bEnabled2 && m_ui.ResizeLegatoTypeComboBox->currentIndex() > 0); m_ui.ResizeLegatoModeComboBox->setEnabled(bEnabled2); m_ui.ResizeJoinCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.ResizeJoinCheckBox->isChecked(); if (bEnabled2) { const bool bBlockSplit = m_ui.ResizeSplitCheckBox->blockSignals(true); m_ui.ResizeSplitCheckBox->setEnabled(false); m_ui.ResizeSplitCheckBox->setChecked(false); m_ui.ResizeSplitCheckBox->blockSignals(bBlockSplit); bEnabled2 = false; ++iEnabled; } else { m_ui.ResizeSplitCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.ResizeSplitCheckBox->isChecked(); if (bEnabled2) { const bool bBlockJoin = m_ui.ResizeJoinCheckBox->blockSignals(true); m_ui.ResizeJoinCheckBox->setEnabled(false); m_ui.ResizeJoinCheckBox->setChecked(false); m_ui.ResizeJoinCheckBox->blockSignals(bBlockJoin); ++iEnabled; } } m_ui.ResizeSplitLengthComboBox->setEnabled(bEnabled2); m_ui.ResizeSplitOffsetComboBox->setEnabled(bEnabled2); // Rescale tool... bEnabled = m_ui.RescaleCheckBox->isChecked(); m_ui.RescaleTimeCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.RescaleTimeCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.RescaleTimeSpinBox->setEnabled(bEnabled2); m_ui.RescaleDurationCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.RescaleDurationCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.RescaleDurationSpinBox->setEnabled(bEnabled2); m_ui.RescaleValueCheckBox->setEnabled(bEnabled); bEnabled2 = bEnabled && m_ui.RescaleValueCheckBox->isChecked(); if (bEnabled2) ++iEnabled; m_ui.RescaleValueSpinBox->setEnabled(bEnabled2); m_ui.RescaleInvertCheckBox->setEnabled(bEnabled2); bEnabled2 = bEnabled2 && m_ui.RescaleInvertCheckBox->isChecked(); if (bEnabled2) ++iEnabled; // Timeshift tool... bEnabled = m_ui.TimeshiftCheckBox->isChecked(); if (bEnabled) ++iEnabled; m_ui.TimeshiftLabel->setEnabled(bEnabled); m_ui.TimeshiftSpinBox->setEnabled(bEnabled); m_ui.TimeshiftSlider->setEnabled(bEnabled); m_ui.TimeshiftText->setEnabled(bEnabled); m_ui.TimeshiftDurationCheckBox->setEnabled(bEnabled); m_pTimeshiftCurve->setVisible(bEnabled); // Temporamp tool... bEnabled = m_ui.TemporampCheckBox->isChecked(); if (bEnabled) ++iEnabled; m_ui.TemporampFromLabel->setEnabled(bEnabled); m_ui.TemporampFromSpinBox->setEnabled(false); m_ui.TemporampToLabel->setEnabled(bEnabled); m_ui.TemporampToSpinBox->setEnabled(bEnabled); m_ui.TemporampText->setEnabled(bEnabled); m_ui.TemporampDurationCheckBox->setEnabled(bEnabled); if (bEnabled && qAbs( m_ui.TemporampFromSpinBox->tempo() - m_ui.TemporampToSpinBox->tempo()) < 0.01f) iEnabled = 0; m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(iEnabled > 0); } // Timeshift characteristic stuff. void qtractorMidiToolsForm::timeshiftSpinBoxChanged ( double p ) { if (m_iUpdate > 0) return; ++m_iUpdate; #if 0//TIMESHIFT_LOGSCALE int i = 0; if (p > +0.001) i = + int(2000.0f * ::log10f(1000.0f * float(+ p))); else if (p < -0.001) i = - int(2000.0f * ::log10f(1000.0f * float(- p))); #else const int i = int(100.0f * float(p)); #endif #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiToolsForm::timeshiftSpinBoxChanged(%g) i=%d", float(p), i); #endif m_ui.TimeshiftSlider->setValue(i); m_pTimeshiftCurve->setTimeshift(float(p)); --m_iUpdate; changed(); } void qtractorMidiToolsForm::timeshiftSliderChanged ( int i ) { if (m_iUpdate > 0) return; ++m_iUpdate; #if 0//TIMESHIFT_LOGSCALE float p = 0.0f; if (i > 0) p = + 0.001f * ::powf(10.0f, (0.0005f * float(+ i))); else if (i < 0) p = - 0.001f * ::powf(10.0f, (0.0005f * float(- i))); #else const float p = 0.01f * float(i); #endif #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiToolsForm::timeshiftSliderChanged(%d) p=%g", i, p); #endif m_ui.TimeshiftSpinBox->setValue(double(p)); m_pTimeshiftCurve->setTimeshift(p); --m_iUpdate; changed(); } // Special tempo ramp tool helper... qtractorTimeScaleNodeCommand *qtractorMidiToolsForm::timeScaleNodeCommand (void) { if (m_timeScaleNodeCommands.isEmpty()) return nullptr; qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand = m_timeScaleNodeCommands.at(0); m_timeScaleNodeCommands.removeAt(0); return pTimeScaleNodeCommand; } // end of qtractorMidiToolsForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorShortcutForm.cpp0000644000000000000000000000013215124701674020235 xustar0030 mtime=1767080892.800263424 30 atime=1767080892.800263424 30 ctime=1767080892.800263424 qtractor-1.5.11/src/qtractorShortcutForm.cpp0000644000175000001440000005354515124701674020241 0ustar00rncbcusers// qtractorShortcutForm.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorShortcutForm.h" #include "qtractorMidiControlObserverForm.h" #include "qtractorOptions.h" #include #include #include #include #include #include #include #include #include //------------------------------------------------------------------------- // qtractorShortcutTableItemEdit // Shortcut key to text event translation. void qtractorShortcutTableItemEdit::keyPressEvent ( QKeyEvent *pKeyEvent ) { const Qt::KeyboardModifiers modifiers = pKeyEvent->modifiers(); int iKey = pKeyEvent->key(); if (modifiers == Qt::NoModifier) { switch (iKey) { case Qt::Key_Return: emit editingFinished(); return; case Qt::Key_Escape: emit editingCanceled(); return; } } if (iKey >= Qt::Key_Shift && iKey < Qt::Key_F1) { QLineEdit::keyPressEvent(pKeyEvent); return; } if (modifiers & Qt::ShiftModifier) iKey |= Qt::SHIFT; if (modifiers & Qt::ControlModifier) iKey |= Qt::CTRL; if (modifiers & Qt::AltModifier) iKey |= Qt::ALT; if (modifiers & Qt::MetaModifier) iKey |= Qt::META; QLineEdit::setText(QKeySequence(iKey).toString()); } //------------------------------------------------------------------------- // qtractorShortcutTableItemEditor qtractorShortcutTableItemEditor::qtractorShortcutTableItemEditor ( QWidget *pParent ) : QWidget(pParent) { m_pItemEdit = new qtractorShortcutTableItemEdit(/*this*/); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) // Some conveniency cleaner helper... m_pItemEdit->setClearButtonEnabled(true); #endif m_pToolButton = new QToolButton(/*this*/); m_pToolButton->setToolButtonStyle(Qt::ToolButtonIconOnly); // m_pToolButton->setIconSize(QSize(18, 18)); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) m_pToolButton->setIcon(QIcon::fromTheme("itemReset")); #else m_pToolButton->setIcon(QIcon::fromTheme("itemClear")); #endif QHBoxLayout *pLayout = new QHBoxLayout(); pLayout->setSpacing(0); pLayout->setContentsMargins(0, 0, 0, 0); pLayout->addWidget(m_pItemEdit); pLayout->addWidget(m_pToolButton); QWidget::setLayout(pLayout); QWidget::setFocusPolicy(Qt::StrongFocus); QWidget::setFocusProxy(m_pItemEdit); QObject::connect(m_pItemEdit, SIGNAL(editingFinished()), SLOT(finish())); QObject::connect(m_pItemEdit, SIGNAL(editingCanceled()), SLOT(cancel())); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) QObject::connect(m_pItemEdit, SIGNAL(textChanged(const QString&)), SLOT(changed(const QString&))); #endif QObject::connect(m_pToolButton, SIGNAL(clicked()), SLOT(clear())); } // Shortcut text accessors. void qtractorShortcutTableItemEditor::setText ( const QString& sText ) { m_pItemEdit->setText(sText); } QString qtractorShortcutTableItemEditor::text (void) const { return m_pItemEdit->text(); } // Default (initial) shortcut text accessors. void qtractorShortcutTableItemEditor::setDefaultText ( const QString& sDefaultText ) { m_sDefaultText = sDefaultText; changed(text()); } const QString& qtractorShortcutTableItemEditor::defaultText(void) const { return m_sDefaultText; } // Shortcut text clear/toggler. void qtractorShortcutTableItemEditor::clear (void) { if (m_pItemEdit->text() == m_sDefaultText) m_pItemEdit->clear(); else m_pItemEdit->setText(m_sDefaultText); m_pItemEdit->setFocus(); } // Shortcut text finish notification. void qtractorShortcutTableItemEditor::finish (void) { const bool bBlockSignals = m_pItemEdit->blockSignals(true); emit editingFinished(); m_index = QModelIndex(); m_sDefaultText.clear(); m_pItemEdit->blockSignals(bBlockSignals); } // Shortcut text cancel notification. void qtractorShortcutTableItemEditor::cancel (void) { const bool bBlockSignals = m_pItemEdit->blockSignals(true); m_index = QModelIndex(); m_sDefaultText.clear(); emit editingFinished(); m_pItemEdit->blockSignals(bBlockSignals); } // Shortcut text change notification. void qtractorShortcutTableItemEditor::changed ( const QString& ) { #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) if (m_sDefaultText.isEmpty()) { m_pToolButton->setVisible(false); m_pToolButton->setEnabled(false); } else { m_pToolButton->setVisible(true); m_pToolButton->setEnabled(m_sDefaultText != text()); } #endif } //------------------------------------------------------------------------- // qtractorShortcutTableItemDelegate qtractorShortcutTableItemDelegate::qtractorShortcutTableItemDelegate ( qtractorShortcutForm *pShortcutForm ) : QItemDelegate(pShortcutForm->tableWidget()), m_pShortcutForm(pShortcutForm) { } // Overridden paint method. void qtractorShortcutTableItemDelegate::paint ( QPainter *pPainter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { // Special treatment for action icon+text... if (index.column() == 0) { pPainter->save(); if (option.state & QStyle::State_Selected) { const QPalette& pal = option.palette; pPainter->fillRect(option.rect, pal.highlight().color()); pPainter->setPen(pal.highlightedText().color()); } // Draw the icon... QRect rect = option.rect; const QSize iconSize(16, 16); const QIcon& icon = index.model()->data(index, Qt::DecorationRole).value(); pPainter->drawPixmap(1, rect.top() + ((rect.height() - iconSize.height()) >> 1), icon.pixmap(iconSize)); // Draw the text... rect.setLeft(iconSize.width() + 2); pPainter->drawText(rect, Qt::TextShowMnemonic | Qt::AlignLeft | Qt::AlignVCenter, index.model()->data(index, Qt::DisplayRole).toString()); pPainter->restore(); } else { // Others do as default... QItemDelegate::paint(pPainter, option, index); } } QWidget *qtractorShortcutTableItemDelegate::createEditor ( QWidget *pParent, const QStyleOptionViewItem& /*option*/, const QModelIndex& index ) const { // Keyboard shortcut. qtractorShortcutTableItemEditor *pItemEditor = new qtractorShortcutTableItemEditor(pParent); pItemEditor->setIndex(index); pItemEditor->setDefaultText( index.model()->data(index, Qt::DisplayRole).toString()); QObject::connect(pItemEditor, SIGNAL(editingFinished()), SLOT(commitEditor())); return pItemEditor; } void qtractorShortcutTableItemDelegate::setEditorData ( QWidget *pEditor, const QModelIndex& index ) const { qtractorShortcutTableItemEditor *pItemEditor = qobject_cast (pEditor); pItemEditor->setText( index.model()->data(index, Qt::DisplayRole).toString()); } void qtractorShortcutTableItemDelegate::setModelData ( QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex& index ) const { qtractorShortcutTableItemEditor *pItemEditor = qobject_cast (pEditor); pModel->setData(index, pItemEditor->text()); } void qtractorShortcutTableItemDelegate::commitEditor (void) { qtractorShortcutTableItemEditor *pItemEditor = qobject_cast (sender()); if (m_pShortcutForm->commitEditor(pItemEditor)) emit commitData(pItemEditor); emit closeEditor(pItemEditor); } QSize qtractorShortcutTableItemDelegate::sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const { return QItemDelegate::sizeHint(option, index) + QSize(4, 4); } //------------------------------------------------------------------------- // qtractorShortcutForm // Constructor. qtractorShortcutForm::qtractorShortcutForm ( const QList& actions, QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). //QDialog::setWindowModality(Qt::ApplicationModal); m_pActionControl = nullptr; m_pActionControlItem = nullptr; m_actions = actions; QListIterator iter(m_actions); while (iter.hasNext()) { QAction *pAction = iter.next(); if (pAction->objectName().isEmpty()) continue; const QKeySequence& shortcut = pAction->shortcut(); const QString& sShortcutText = shortcut.toString(); if (!sShortcutText.isEmpty()) m_shortcuts.insert(sShortcutText, pAction); } // m_ui.ShortcutTable->setIconSize(QSize(16, 16)); m_ui.ShortcutTable->setItemDelegate( new qtractorShortcutTableItemDelegate(this)); QHeaderView *pHeaderView = m_ui.ShortcutTable->header(); pHeaderView->setStretchLastSection(true); pHeaderView->setDefaultAlignment(Qt::AlignLeft); pHeaderView->resizeSection(0, 180); pHeaderView->resizeSection(1, 320); // pHeaderView->hideSection(3); // Custom context menu... m_ui.ShortcutTable->setContextMenuPolicy(Qt::CustomContextMenu); // Restore last seen form position and extents... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { pOptions->loadWidgetGeometry(this, true); pOptions->loadComboBoxHistory(m_ui.ShortcutSearchComboBox); m_ui.ShortcutSearchComboBox->setEditText(QString()); } #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) // Some conveniency cleaner helper... m_ui.ShortcutSearchComboBox->lineEdit()->setClearButtonEnabled(true); m_ui.ShortcutSearchComboBox->lineEdit()->setPlaceholderText( tr("Search shortcuts")); #endif QObject::connect(m_ui.ShortcutSearchComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(refresh())); QObject::connect(m_ui.ShortcutTable, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), SLOT(actionShortcutActivated(QTreeWidgetItem *, int))); QObject::connect(m_ui.ShortcutTable, SIGNAL(itemChanged(QTreeWidgetItem *, int)), SLOT(actionShortcutChanged(QTreeWidgetItem *, int))); QObject::connect(m_ui.ShortcutTable, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(actionControlMenuRequested(const QPoint&))); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); refresh(); } qtractorShortcutForm::~qtractorShortcutForm (void) { // Store form position and extents... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { pOptions->saveComboBoxHistory(m_ui.ShortcutSearchComboBox); pOptions->saveWidgetGeometry(this, true); } } // Action shortcut/control table widget accessor. QTreeWidget *qtractorShortcutForm::tableWidget (void) const { return m_ui.ShortcutTable; } // MIDI Controller manager accessor. void qtractorShortcutForm::setActionControl ( qtractorActionControl *pActionControl ) { m_pActionControl = pActionControl; QHeaderView *pHeaderView = m_ui.ShortcutTable->header(); if (m_pActionControl) { pHeaderView->showSection(3); } else { pHeaderView->hideSection(3); } refresh(); } qtractorActionControl *qtractorShortcutForm::actionControl (void) const { return m_pActionControl; } // Action shortcut/control dirty-flag accessors. bool qtractorShortcutForm::isDirtyActionShortcuts (void) const { return !m_dirty_shortcuts.isEmpty(); } bool qtractorShortcutForm::isDirtyActionControls (void) const { return !m_dirty_controls.isEmpty();; } // Shortcut action finder & settler. bool qtractorShortcutForm::commitEditor ( qtractorShortcutTableItemEditor *pItemEditor ) { const QModelIndex& index = pItemEditor->index(); if (!index.isValid()) return false; const QString& sShortcutText = pItemEditor->text(); const QString& sDefaultText = pItemEditor->defaultText(); if (sShortcutText == sDefaultText) return false; QTreeWidgetItem *pItem = nullptr; if (!sShortcutText.isEmpty()) { QAction *pAction = m_shortcuts.value(sShortcutText, nullptr); if (pAction) pItem = m_action_items.value(pAction, nullptr); if (pItem) { QMessageBox::warning(this, tr("Warning"), tr("Keyboard shortcut (%1) already assigned (%2).") .arg(sShortcutText) .arg(pItem->text(0).remove('&')), QMessageBox::Cancel); pItemEditor->clear(); return false; } } pItem = m_ui.ShortcutTable->topLevelItem(index.row()); if (pItem) { QAction *pAction = m_item_actions.value(pItem, nullptr); if (pAction) m_dirty_shortcuts.insert(pAction, sShortcutText); } return true; } void qtractorShortcutForm::actionShortcutActivated ( QTreeWidgetItem *pItem, int iColumn ) { switch (iColumn) { case 3: // MIDI Controller... actionControlActivated(); break; case 2: default: // Keyboard shortcut... m_ui.ShortcutTable->editItem(pItem, 2); break; } } void qtractorShortcutForm::actionShortcutChanged ( QTreeWidgetItem *pItem, int iColumn ) { if (iColumn == 2) { const QString& sShortcutText = QKeySequence(pItem->text(2).trimmed()).toString(); pItem->setText(2, sShortcutText); } stabilizeForm(); } void qtractorShortcutForm::refresh (void) { m_pActionControlItem = nullptr; m_ui.ShortcutTable->clear(); m_item_actions.clear(); m_action_items.clear(); QString sSearch = m_ui.ShortcutSearchComboBox->currentText().simplified(); const QRegularExpression rx(sSearch.replace( QRegularExpression("[\\s]+"), ".*"), QRegularExpression::CaseInsensitiveOption); QList items; QListIterator iter(m_actions); while (iter.hasNext()) { QAction *pAction = iter.next(); if (pAction->objectName().isEmpty()) continue; const QString& sActionText = qtractorActionControl::menuActionText(pAction, pAction->text()); if (!rx.pattern().isEmpty() && !rx.match(sActionText).hasMatch() && !rx.match(pAction->statusTip()).hasMatch()) continue; QString sShortcutText; if (m_dirty_shortcuts.contains(pAction)) sShortcutText = m_dirty_shortcuts.value(pAction); else sShortcutText = pAction->shortcut().toString(); QString sControlText; if (m_dirty_controls.contains(pAction)) sControlText = m_dirty_controls.value(pAction); else sControlText = actionControlText(pAction); QTreeWidgetItem *pItem = new QTreeWidgetItem(); pItem->setIcon(0, pAction->icon()); pItem->setText(0, sActionText); pItem->setText(1, pAction->statusTip()); pItem->setText(2, sShortcutText); pItem->setText(3, sControlText); pItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable); m_item_actions.insert(pItem, pAction); m_action_items.insert(pAction, pItem); items.append(pItem); } m_ui.ShortcutTable->addTopLevelItems(items); m_ui.ShortcutTable->expandAll(); stabilizeForm(); } void qtractorShortcutForm::accept (void) { // Apply keyboard shortcuts... QHash::ConstIterator iter1 = m_dirty_shortcuts.constBegin(); const QHash::ConstIterator& iter1_end = m_dirty_shortcuts.constEnd(); for ( ; iter1 != iter1_end; ++iter1) iter1.key()->setShortcut(QKeySequence(iter1.value())); // Free old/cloned MIDI observers... QHash::ConstIterator iter2 = m_dirty_observers.constBegin(); const QHash::ConstIterator& iter2_end = m_dirty_observers.constEnd(); for ( ; iter2 != iter2_end; ++iter2) { MidiObserver *pMidiObserver = iter2.value(); if (pMidiObserver) delete pMidiObserver; } QDialog::accept(); } void qtractorShortcutForm::reject (void) { bool bReject = true; // Check if there's any pending changes... if (isDirtyActionShortcuts()) { QMessageBox::StandardButtons buttons = QMessageBox::Discard | QMessageBox::Cancel; if (m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->isEnabled()) buttons |= QMessageBox::Apply; switch (QMessageBox::warning(this, tr("Warning"), tr("Keyboard shortcuts have been changed.\n\n" "Do you want to apply the changes?"), buttons)) { case QMessageBox::Apply: accept(); return; case QMessageBox::Discard: break; default: // Cancel. bReject = false; } } else if (isDirtyActionControls()) { QMessageBox::StandardButtons buttons = QMessageBox::Discard | QMessageBox::Cancel; if (m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->isEnabled()) buttons |= QMessageBox::Apply; switch (QMessageBox::warning(this, tr("Warning"), tr("MIDI Controller shortcuts have been changed.\n\n" "Do you want to apply the changes?"), buttons)) { case QMessageBox::Apply: accept(); return; case QMessageBox::Discard: break; default: // Cancel. bReject = false; } } qtractorMidiControl *pMidiControl = nullptr; if (bReject && m_pActionControl) pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) { QHash::ConstIterator iter = m_dirty_observers.constBegin(); const QHash::ConstIterator& iter_end = m_dirty_observers.constEnd(); for ( ; iter != iter_end; ++iter) { QAction *pMidiObserverAction = iter.key(); MidiObserver *pMidiObserver = iter.value(); MidiObserver *pNewMidiObserver = m_pActionControl->getMidiObserver(pMidiObserverAction); if (pNewMidiObserver) { pMidiControl->unmapMidiObserver(pNewMidiObserver); m_pActionControl->removeMidiObserver(pMidiObserverAction); } if (pMidiObserver) { pNewMidiObserver = m_pActionControl->addMidiObserver(pMidiObserverAction); pNewMidiObserver->setType(pMidiObserver->type()); pNewMidiObserver->setChannel(pMidiObserver->channel()); pNewMidiObserver->setParam(pMidiObserver->param()); pNewMidiObserver->setLogarithmic(pMidiObserver->isLogarithmic()); pNewMidiObserver->setFeedback(pMidiObserver->isFeedback()); pNewMidiObserver->setInvert(pMidiObserver->isInvert()); pNewMidiObserver->setHook(pMidiObserver->isHook()); pNewMidiObserver->setLatch(pMidiObserver->isLatch()); pMidiControl->mapMidiObserver(pNewMidiObserver); delete pMidiObserver; } } } if (bReject) QDialog::reject(); } void qtractorShortcutForm::stabilizeForm (void) { const bool bValid = (isDirtyActionShortcuts() || isDirtyActionControls()); m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(bValid); } void qtractorShortcutForm::actionControlMenuRequested ( const QPoint& pos ) { if (m_pActionControl == nullptr) return; QMenu menu(this); menu.addAction( QIcon::fromTheme("itemControllers"), tr("&MIDI Controller..."), this, SLOT(actionControlActivated())); menu.exec(m_ui.ShortcutTable->viewport()->mapToGlobal(pos)); } void qtractorShortcutForm::actionControlActivated (void) { if (m_pActionControl == nullptr) return; m_pActionControlItem = m_ui.ShortcutTable->currentItem(); if (m_pActionControlItem == nullptr) return; QAction *pMidiObserverAction = m_item_actions.value(m_pActionControlItem, nullptr); if (pMidiObserverAction == nullptr) return; qtractorMidiControlObserverForm::showInstance(pMidiObserverAction, this); qtractorMidiControlObserverForm *pMidiObserverForm = qtractorMidiControlObserverForm::getInstance(); if (pMidiObserverForm) { if (!m_dirty_observers.contains(pMidiObserverAction)) { MidiObserver *pMidiObserver = m_pActionControl->getMidiObserver(pMidiObserverAction); if (pMidiObserver) { MidiObserver *pNewMidiObserver = new MidiObserver(pMidiObserverAction); pNewMidiObserver->setType(pMidiObserver->type()); pNewMidiObserver->setChannel(pMidiObserver->channel()); pNewMidiObserver->setParam(pMidiObserver->param()); pNewMidiObserver->setLogarithmic(pMidiObserver->isLogarithmic()); pNewMidiObserver->setFeedback(pMidiObserver->isFeedback()); pNewMidiObserver->setInvert(pMidiObserver->isInvert()); pNewMidiObserver->setHook(pMidiObserver->isHook()); pNewMidiObserver->setLatch(pMidiObserver->isLatch()); pMidiObserver = pNewMidiObserver; } m_dirty_observers.insert(pMidiObserverAction, pMidiObserver); } QObject::connect(pMidiObserverForm, SIGNAL(accepted()), SLOT(actionControlAccepted())); QObject::connect(pMidiObserverForm, SIGNAL(rejected()), SLOT(actionControlRejected())); } } void qtractorShortcutForm::actionControlAccepted (void) { if (m_pActionControl == nullptr) return; if (m_pActionControlItem) { QAction *pMidiObserverAction = m_item_actions.value(m_pActionControlItem, nullptr); if (pMidiObserverAction) { const QString& sControlText = actionControlText(pMidiObserverAction); m_pActionControlItem->setText(3, sControlText); m_dirty_controls.insert(pMidiObserverAction, sControlText); } m_pActionControlItem = nullptr; } stabilizeForm(); } void qtractorShortcutForm::actionControlRejected (void) { if (m_pActionControl == nullptr) return; if (m_pActionControlItem) { QAction *pMidiObserverAction = m_item_actions.value(m_pActionControlItem, nullptr); if (pMidiObserverAction) { qtractorActionControl::MidiObserver *pMidiObserver = m_dirty_observers.value(pMidiObserverAction, nullptr); if (pMidiObserver) delete pMidiObserver; m_dirty_observers.remove(pMidiObserverAction); } m_pActionControlItem = nullptr; } stabilizeForm(); } QString qtractorShortcutForm::actionControlText ( QAction *pAction ) const { QString sActionControlText; if (m_pActionControl) { qtractorActionControl::MidiObserver *pMidiObserver = m_pActionControl->getMidiObserver(pAction); if (pMidiObserver) { QStringList clist; clist.append(qtractorMidiControl::nameFromType(pMidiObserver->type())); clist.append(QString::number(pMidiObserver->channel() + 1)); clist.append(QString::number(pMidiObserver->param())); sActionControlText = clist.join(", "); } } return sActionControlText; } // end of qtractorShortcutForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorObserver.cpp0000644000000000000000000000013215124701674017365 xustar0030 mtime=1767080892.796263441 30 atime=1767080892.796263441 30 ctime=1767080892.796263441 qtractor-1.5.11/src/qtractorObserver.cpp0000644000175000001440000001124415124701674017357 0ustar00rncbcusers// qtractorObserver.cpp // /**************************************************************************** Copyright (C) 2005-2022, 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 "qtractorAbout.h" #include "qtractorObserver.h" //--------------------------------------------------------------------------- // qtractorSubjectQueue - Update/notify subject queue. class qtractorSubjectQueue { public: struct QueueItem { qtractorSubject *subject; qtractorObserver *sender; float value; }; qtractorSubjectQueue ( unsigned int iQueueSize = 1024 ) : m_iQueueIndex(0), m_iQueueSize(0), m_pQueueItems(nullptr) { resize(iQueueSize); } ~qtractorSubjectQueue () { clear(); delete [] m_pQueueItems; } void clear() { m_iQueueIndex = 0; } bool push ( qtractorSubject *pSubject, qtractorObserver *pSender, float fValue ) { if (m_iQueueIndex >= m_iQueueSize) return false; pSubject->setQueued(true); QueueItem *pItem = &m_pQueueItems[m_iQueueIndex++]; pItem->subject = pSubject; pItem->sender = pSender; pItem->value = fValue; return true; } bool pop (bool bUpdate) { if (m_iQueueIndex == 0) return false; QueueItem *pItem = &m_pQueueItems[--m_iQueueIndex]; qtractorSubject *pSubject = pItem->subject; pSubject->notify(pItem->sender, pItem->value, bUpdate); pSubject->setQueued(false); return true; } bool flush (bool bUpdate) { int i = 0; while (pop(bUpdate)) ++i; return (i > 0); } void reset () { while (m_iQueueIndex > 0) { QueueItem *pItem = &m_pQueueItems[--m_iQueueIndex]; (pItem->subject)->setQueued(false); } clear(); } void resize ( unsigned int iNewSize ) { QueueItem *pOldItems = m_pQueueItems; QueueItem *pNewItems = new QueueItem [iNewSize]; if (pOldItems) { unsigned int iOldSize = m_iQueueIndex; if (iOldSize > iNewSize) iOldSize = iNewSize; if (iOldSize > 0) ::memcpy(pNewItems, pOldItems, iOldSize * sizeof(QueueItem)); m_iQueueSize = iNewSize; m_pQueueItems = pNewItems; delete [] pOldItems; } else { m_iQueueSize = iNewSize; m_pQueueItems = pNewItems; } } bool isEmpty() const { return (m_iQueueIndex == 0); } private: unsigned int m_iQueueIndex; unsigned int m_iQueueSize; QueueItem *m_pQueueItems; }; // The local subject queue singleton. static qtractorSubjectQueue g_subjectQueue; //--------------------------------------------------------------------------- // qtractorSubject - Scalar parameter value model. // Constructor. qtractorSubject::qtractorSubject ( float fValue, float fDefaultValue ) : m_fValue(fValue), m_bQueued(false), m_fPrevValue(fValue), m_fLastValue(fValue), m_fMinValue(0.0f), m_fMaxValue(1.0f), m_fDefaultValue(fDefaultValue), m_bToggled(false), m_bInteger(false), m_pCurve(nullptr) { } // Destructor. qtractorSubject::~qtractorSubject (void) { QListIterator iter(m_observers); while (iter.hasNext()) iter.next()->setSubject(nullptr); m_observers.clear(); } // Direct value accessors. void qtractorSubject::setValue ( float fValue, qtractorObserver *pSender ) { if (fValue == m_fValue) return; if (!m_bQueued) { m_fPrevValue = m_fValue; g_subjectQueue.push(this, pSender, fValue); } m_fValue = safeValue(fValue); } // Observer/view updater. void qtractorSubject::notify ( qtractorObserver *pSender, float fValue, bool bUpdate ) { QListIterator iter(m_observers); while (iter.hasNext()) { qtractorObserver *pObserver = iter.next(); if (pSender && pSender == pObserver) continue; m_fLastValue = fValue; pObserver->update(bUpdate); } } // Queue flush (singleton) -- notify all pending observers. bool qtractorSubject::flushQueue ( bool bUpdate ) { return g_subjectQueue.flush(bUpdate); } // Queue reset (clear). void qtractorSubject::resetQueue (void) { g_subjectQueue.reset(); } void qtractorSubject::clearQueue (void) { g_subjectQueue.clear(); } // end of qtractorObserver.cpp qtractor-1.5.11/src/PaxHeaders/qtractorTimeScaleCommand.h0000644000000000000000000000012715124701674020414 xustar0029 mtime=1767080892.80126342 29 atime=1767080892.80126342 29 ctime=1767080892.80126342 qtractor-1.5.11/src/qtractorTimeScaleCommand.h0000644000175000001440000002533615124701674020411 0ustar00rncbcusers// qtractorTimeScaleCommand.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorTimeScaleCommand_h #define __qtractorTimeScaleCommand_h #include "qtractorCommand.h" #include "qtractorTimeScale.h" // Forward declarations. class qtractorClipCommand; class qtractorCurveEditCommand; class qtractorMidiClip; //---------------------------------------------------------------------- // class qtractorTimeScaleNodeCommand - declaration. // class qtractorTimeScaleNodeCommand : public qtractorCommand { public: // Constructor. qtractorTimeScaleNodeCommand(const QString& sName, qtractorTimeScale *pTimeScale, unsigned long iFrame = 0, float fTempo = 120.0f, unsigned short iBeatType = 2, unsigned short iBeatsPerBar = 4, unsigned short iBeatDivisor = 2); // Destructor. virtual ~qtractorTimeScaleNodeCommand(); // Time-scale accessor. qtractorTimeScale *timeScale() const { return m_pTimeScale; } // Node properties accessors. unsigned long frame() const { return m_iFrame; } float tempo() const { return m_fTempo; } unsigned short beatType() const { return m_iBeatType; } unsigned short beatsPerBar() const { return m_iBeatsPerBar; } unsigned short beatDivisor() const { return m_iBeatDivisor; } protected: // Executive commands. bool addNode(); bool updateNode(); bool removeNode(); // Make it automatic clip time-stretching command (static). qtractorClipCommand *createClipCommand( unsigned long iFrameStart, unsigned long iFrameEnd, float fOldTempo, float fNewTempo); // Automation curve time-stretching command (static). void addCurveEditCommands( unsigned long iFrameStart, unsigned long iFrameEnd, float fOldTempo, float fNewTempo); private: // Instance variables. qtractorTimeScale *m_pTimeScale; unsigned long m_iFrame; float m_fTempo; unsigned short m_iBeatType; unsigned short m_iBeatsPerBar; unsigned short m_iBeatDivisor; bool m_bAutoTimeStretch; qtractorClipCommand *m_pClipCommand; QList m_curveEditCommands; }; //---------------------------------------------------------------------- // class qtractorTimeScaleAddNodeCommand - declaration. // class qtractorTimeScaleAddNodeCommand : public qtractorTimeScaleNodeCommand { public: // Constructor. qtractorTimeScaleAddNodeCommand( qtractorTimeScale *pTimeScale, unsigned long iFrame, float fTempo = 120.0f, unsigned short iBeatType = 2, unsigned short iBeatsPerBar = 4, unsigned short iBeatDivisor = 2); // Time-scale command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorTimeScaleUpdateNodeCommand - declaration. // class qtractorTimeScaleUpdateNodeCommand : public qtractorTimeScaleNodeCommand { public: // Constructor. qtractorTimeScaleUpdateNodeCommand(qtractorTimeScale *pTimeScale, unsigned long iFrame, float fTempo, unsigned short iBeatType, unsigned short iBeatsPerBar, unsigned short iBeatDivisor); // Time-scale command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorTimeScaleRemoveNodeCommand - declaration. // class qtractorTimeScaleRemoveNodeCommand : public qtractorTimeScaleNodeCommand { public: // Constructor. qtractorTimeScaleRemoveNodeCommand(qtractorTimeScale *pTimeScale, qtractorTimeScale::Node *pNode); // Time-scale command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorTimeScaleMoveNodeCommand - declaration. // class qtractorTimeScaleMoveNodeCommand : public qtractorTimeScaleNodeCommand { public: // Constructor. qtractorTimeScaleMoveNodeCommand(qtractorTimeScale *pTimeScale, qtractorTimeScale::Node *pNode, unsigned long iFrame); // Time-scale command methods. bool redo(); bool undo(); private: // The new location argument. unsigned long m_iNewFrame; // Replaced node salvage. bool m_bOldNode; unsigned long m_iOldFrame; float m_fOldTempo; unsigned short m_iOldBeatType; unsigned short m_iOldBeatsPerBar; unsigned short m_iOldBeatDivisor; }; //---------------------------------------------------------------------- // class qtractorTimeScaleMarkerCommand - declaration. // class qtractorTimeScaleMarkerCommand : public qtractorCommand { public: // Constructors. qtractorTimeScaleMarkerCommand(const QString& sName, qtractorTimeScale *pTimeScale, unsigned long iFrame = 0, const QString& sText = QString(), const QColor& rgbColor = Qt::darkGray, int iAccidentals = 0, int iMode = 0); // Time-scale accessor. qtractorTimeScale *timeScale() const { return m_pTimeScale; } // Marker properties accessors. unsigned long frame() const { return m_iFrame; } const QString& text() const { return m_sText; } const QColor& color() const { return m_rgbColor; } int accidentals() const { return m_iAccidentals; } bool mode() const { return m_iMode; } protected: // Executive commands. bool addMarker(); bool updateMarker(); bool addKeySignature(); bool updateKeySignature(); bool removeMarker(); private: // Instance variables. qtractorTimeScale *m_pTimeScale; unsigned long m_iFrame; QString m_sText; QColor m_rgbColor; int m_iAccidentals; int m_iMode; }; //---------------------------------------------------------------------- // class qtractorTimeScaleAddMarkerCommand - declaration. // class qtractorTimeScaleAddMarkerCommand : public qtractorTimeScaleMarkerCommand { public: // Constructor. qtractorTimeScaleAddMarkerCommand( qtractorTimeScale *pTimeScale, unsigned long iFrame, const QString& sText, const QColor& rgbColor = Qt::darkGray); // Time-scale command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorTimeScaleUpdateMarkerCommand - declaration. // class qtractorTimeScaleUpdateMarkerCommand : public qtractorTimeScaleMarkerCommand { public: // Constructor. qtractorTimeScaleUpdateMarkerCommand( qtractorTimeScale *pTimeScale, unsigned long iFrame, const QString& sText, const QColor& rgbColor = Qt::darkGray); // Time-scale command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorTimeScaleRemoveMarkerCommand - declaration. // class qtractorTimeScaleRemoveMarkerCommand : public qtractorTimeScaleMarkerCommand { public: // Constructor. qtractorTimeScaleRemoveMarkerCommand( qtractorTimeScale *pTimeScale, qtractorTimeScale::Marker *pMarker); // Time-scale command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorTimeScaleAddKeySignatureCommand - declaration. // class qtractorTimeScaleAddKeySignatureCommand : public qtractorTimeScaleMarkerCommand { public: // Constructor. qtractorTimeScaleAddKeySignatureCommand( qtractorTimeScale *pTimeScale, unsigned long iFrame, int iAccidentals = 0, int iMode = 0); // Time-scale command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorTimeScaleUpdateKeySignatureCommand - declaration. // class qtractorTimeScaleUpdateKeySignatureCommand : public qtractorTimeScaleMarkerCommand { public: // Constructor. qtractorTimeScaleUpdateKeySignatureCommand( qtractorTimeScale *pTimeScale, unsigned long iFrame, int iAccidentals = 0, int iMode = 0); // Time-scale command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorTimeScaleRemoveKeySignatureCommand - declaration. // class qtractorTimeScaleRemoveKeySignatureCommand : public qtractorTimeScaleMarkerCommand { public: // Constructor. qtractorTimeScaleRemoveKeySignatureCommand( qtractorTimeScale *pTimeScale, qtractorTimeScale::Marker *pMarker); // Time-scale command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorTimeScaleMoveMarkerCommand - declaration. // class qtractorTimeScaleMoveMarkerCommand : public qtractorTimeScaleMarkerCommand { public: // Constructor. qtractorTimeScaleMoveMarkerCommand(qtractorTimeScale *pTimeScale, qtractorTimeScale::Marker *pMarker, unsigned long iFrame); // Time-scale command methods. bool redo(); bool undo(); private: // The new location argument. unsigned long m_iNewFrame; // Replaced marker salvage. bool m_bOldMarker; unsigned long m_iOldFrame; QString m_sOldText; QColor m_rgbOldColor; int m_iOldAccidentals; int m_iOldMode; }; //---------------------------------------------------------------------- // class qtractorTimeScaleCommand - declaration. // class qtractorTimeScaleCommand : public qtractorCommand { public: // Constructor. qtractorTimeScaleCommand(const QString& sName); // Destructor. ~qtractorTimeScaleCommand(); // Node commands. void addNodeCommand(qtractorTimeScaleNodeCommand *pNodeCommand); // Time-scale command methods. bool redo(); bool undo(); private: // Node commands. QList m_nodeCommands; }; //---------------------------------------------------------------------- // class qtractorTimeScaleTimeSig2Command - declaration. // class qtractorTimeScaleTimeSig2Command : public qtractorCommand { public: // Constructor. qtractorTimeScaleTimeSig2Command( qtractorTimeScale *pTimeScale, qtractorMidiClip *pMidiClip, unsigned short iBeatsPerBar2, unsigned short iBeatDivisor2); // Time-scale command methods. bool redo(); bool undo(); private: // Instance variables. qtractorTimeScale *m_pTimeScale; qtractorMidiClip *m_pMidiClip; unsigned short m_iBeatsPerBar2; unsigned short m_iBeatDivisor2; }; #endif // __qtractorTimeScaleCommand_h // end of qtractorTimeScaleCommand.h qtractor-1.5.11/src/PaxHeaders/qtractorMessageList.cpp0000644000000000000000000000013215124701674020016 xustar0030 mtime=1767080892.788263475 30 atime=1767080892.788263475 30 ctime=1767080892.788263475 qtractor-1.5.11/src/qtractorMessageList.cpp0000644000175000001440000000372015124701674020010 0ustar00rncbcusers// qtractorMessageList.cpp // /**************************************************************************** Copyright (C) 2005-2019, 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 "qtractorMessageList.h" //---------------------------------------------------------------------- // class qtractorMessageList -- message string list buffer. // // Pseudo-singleton instance. qtractorMessageList *qtractorMessageList::g_pInstance = nullptr; // Constructor. qtractorMessageList::qtractorMessageList (void) { g_pInstance = this; } // Destructor. qtractorMessageList::~qtractorMessageList (void) { m_items.clear(); g_pInstance = nullptr; } // Forfeit methods. void qtractorMessageList::append ( const QString& sText ) { if (g_pInstance) g_pInstance->m_items.append(sText); qWarning("Warning: %s", sText.toUtf8().constData()); } bool qtractorMessageList::isEmpty (void) { return (g_pInstance ? g_pInstance->m_items.isEmpty() : true); } QStringList qtractorMessageList::items (void) { return (g_pInstance ? g_pInstance->m_items : QStringList()); } void qtractorMessageList::clear (void) { if (g_pInstance) g_pInstance->m_items.clear(); } // end of qtractorMessageList.cpp qtractor-1.5.11/src/PaxHeaders/qtractorAudioListView.cpp0000644000000000000000000000013215124701674020326 xustar0030 mtime=1767080892.779263512 30 atime=1767080892.779263512 30 ctime=1767080892.779263512 qtractor-1.5.11/src/qtractorAudioListView.cpp0000644000175000001440000001541015124701674020317 0ustar00rncbcusers// qtractorAudioListView.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorAudioListView.h" #include "qtractorAudioFile.h" #include "qtractorOptions.h" #include "qtractorMessageList.h" #include #include #include //---------------------------------------------------------------------- // class qtractorAudioFileItem -- audio file list view item. // // Constructors. qtractorAudioFileItem::qtractorAudioFileItem ( const QString& sPath, qtractorAudioFile *pFile ) : qtractorFileListItem(sPath) { QTreeWidgetItem::setTextAlignment( qtractorAudioListView::Channels, Qt::AlignRight); QTreeWidgetItem::setTextAlignment( qtractorAudioListView::Frames, Qt::AlignRight); QTreeWidgetItem::setTextAlignment( qtractorAudioListView::Rate, Qt::AlignRight); QTreeWidgetItem::setIcon(qtractorAudioListView::Name, QIcon::fromTheme("itemAudioFile")); QTreeWidgetItem::setText(qtractorAudioListView::Channels, QString::number(pFile->channels())); QTreeWidgetItem::setText(qtractorAudioListView::Frames, QString::number(pFile->frames())); QTreeWidgetItem::setText(qtractorAudioListView::Rate, QString::number(pFile->sampleRate())); QString sTime; unsigned int hh, mm, ss, zzz; float secs = (float) pFile->frames() / (float) pFile->sampleRate(); hh = mm = ss = 0; if (secs >= 3600.0f) { hh = (unsigned int) (secs / 3600.0f); secs -= (float) hh * 3600.0f; } if (secs >= 60.0f) { mm = (unsigned int) (secs / 60.0f); secs -= (float) mm * 60.0f; } if (secs >= 0.0f) { ss = (unsigned int) secs; secs -= (float) ss; } zzz = (unsigned int) (secs * 1000.0f); #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) sTime.sprintf("%02u:%02u:%02u.%03u", hh, mm, ss, zzz); #else sTime = QString::asprintf("%02u:%02u:%02u.%03u", hh, mm, ss, zzz); #endif QTreeWidgetItem::setText(qtractorAudioListView::Time, sTime); QTreeWidgetItem::setText(qtractorAudioListView::Path, sPath); } // Tooltip renderer. QString qtractorAudioFileItem::toolTip (void) const { return QObject::tr( "%1 (%2)\n%3 channels, %4 frames, %5 Hz\n%6") .arg(QTreeWidgetItem::text(qtractorAudioListView::Name)) .arg(QTreeWidgetItem::text(qtractorAudioListView::Time)) .arg(QTreeWidgetItem::text(qtractorAudioListView::Channels)) .arg(QTreeWidgetItem::text(qtractorAudioListView::Frames)) .arg(QTreeWidgetItem::text(qtractorAudioListView::Rate)) .arg(QTreeWidgetItem::text(qtractorAudioListView::Path)); } //---------------------------------------------------------------------------- // qtractorAudioListView -- Group/File list view, supporting drag-n-drop. // // Constructor. qtractorAudioListView::qtractorAudioListView ( QWidget *pParent ) : qtractorFileListView(qtractorFileList::Audio, pParent) { QTreeWidget::setColumnCount(qtractorAudioListView::LastColumn + 1); QTreeWidgetItem *pHeaderItem = QTreeWidget::headerItem(); pHeaderItem->setText(qtractorAudioListView::Name, tr("Name")); pHeaderItem->setText(qtractorAudioListView::Channels, tr("Ch")); pHeaderItem->setText(qtractorAudioListView::Frames, tr("Frames")); pHeaderItem->setText(qtractorAudioListView::Rate, tr("Rate")); pHeaderItem->setText(qtractorAudioListView::Time, tr("Time")); pHeaderItem->setText(qtractorAudioListView::Path, tr("Path")); pHeaderItem->setText(qtractorAudioListView::LastColumn, QString()); pHeaderItem->setTextAlignment( qtractorAudioListView::Channels, Qt::AlignRight); pHeaderItem->setTextAlignment( qtractorAudioListView::Frames, Qt::AlignRight); pHeaderItem->setTextAlignment( qtractorAudioListView::Rate, Qt::AlignRight); QHeaderView *pHeader = QTreeWidget::header(); pHeader->resizeSection(qtractorAudioListView::Name, 160); QTreeWidget::resizeColumnToContents(qtractorAudioListView::Channels); QTreeWidget::resizeColumnToContents(qtractorAudioListView::Frames); QTreeWidget::resizeColumnToContents(qtractorAudioListView::Rate); QTreeWidget::resizeColumnToContents(qtractorAudioListView::Time); pHeader->resizeSection(qtractorAudioListView::Path, 160); } // Prompt for proper file list open. QStringList qtractorAudioListView::getOpenFileNames (void) { QStringList files; const QString& sTitle = tr("Open Audio Files"); const QString& sFilter = qtractorAudioFileFactory::filters().join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) // Ask for the filename to open... files = QFileDialog::getOpenFileNames(pParentWidget, sTitle, recentDir(), sFilter, nullptr, options); #else // Construct open-files dialog... QFileDialog fileDialog(pParentWidget, sTitle, recentDir(), sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFiles); fileDialog.setDefaultSuffix(qtractorAudioFileFactory::defaultExt()); // Stuff sidebar... if (pOptions) { QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sAudioDir)); fileDialog.setSidebarUrls(urls); } fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) files = fileDialog.selectedFiles(); #endif return files; } // File item factory method. qtractorFileListItem *qtractorAudioListView::createFileItem ( const QString& sPath ) { qtractorFileListItem *pFileItem = nullptr; qtractorAudioFile *pFile = qtractorAudioFileFactory::createAudioFile(sPath); if (pFile == nullptr) return nullptr; if (pFile->open(sPath)) { pFileItem = new qtractorAudioFileItem(sPath, pFile); pFile->close(); } else { qtractorMessageList::append( tr("%1: Audio file not found.").arg(sPath)); } delete pFile; return pFileItem; } // end of qtractorAudioListView.cpp qtractor-1.5.11/src/PaxHeaders/qtractorTimeStretcher.h0000644000000000000000000000012715124701674020031 xustar0029 mtime=1767080892.80126342 29 atime=1767080892.80126342 29 ctime=1767080892.80126342 qtractor-1.5.11/src/qtractorTimeStretcher.h0000644000175000001440000000561715124701674020026 0ustar00rncbcusers// qtractorTimeStretcher.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorTimeStretcher_h #define __qtractorTimeStretcher_h #include "qtractorAbout.h" #include "qtractorWsolaTimeStretcher.h" #ifdef CONFIG_LIBRUBBERBAND #include #endif //--------------------------------------------------------------------------- // qtractorTimeStretcher - Time/Pitch-stretcher processor interface. // class qtractorTimeStretcher { public: // Constructor flags. enum Flags { None = 0, WsolaTimeStretch = 1, WsolaQuickSeek = 2, #ifdef CONFIG_LIBRUBBERBAND RubberBandFormant = 4, #ifdef CONFIG_LIBRUBBERBAND_R3 RubberBandFinerR3 = 8 #endif #endif }; // Constructor. qtractorTimeStretcher( unsigned short iChannels = 2, unsigned int iSampleRate = 44100, float fTimeStretch = 1.0f, float fPitchShift = 1.0f, unsigned int iFlags = None, unsigned int iBufferSize = 4096); // Destructor. ~qtractorTimeStretcher(); // Adds frames of samples into the input buffer. void process(float **ppFrames, unsigned int iFrames); // Copies requested frames output buffer and removes them // from the sample buffer. If there are less than available() // samples in the buffer, returns all that available. duh? unsigned int retrieve(float **ppFrames, unsigned int iFrames); // Returns number of frames currently available. unsigned int available() const; // Flush any last samples that are hiding // in the internal processing pipeline. void flush(); // Clears all buffers. void reset(); private: // Instance variables. qtractorWsolaTimeStretcher *m_pWsolaTimeStretcher; #ifdef CONFIG_LIBRUBBERBAND RubberBand::RubberBandStretcher *m_pRubberBandStretcher; unsigned short m_iRubberBandChannels; unsigned int m_iRubberBandLatency; unsigned int m_iRubberBandPadding; unsigned int m_iRubberBandFrames; float **m_ppRubberBandFrames; float **m_ppRubberBandBuffer; bool m_bRubberBandFlush; #endif }; #endif // __qtractorTimeStretcher_h // end of qtractorTimeStretcher.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiFile.cpp0000644000000000000000000000013215124701674017260 xustar0030 mtime=1767080892.794263449 30 atime=1767080892.793263454 30 ctime=1767080892.794263449 qtractor-1.5.11/src/qtractorMidiFile.cpp0000644000175000001440000011410415124701674017251 0ustar00rncbcusers// qtractorMidiFile.cpp // /**************************************************************************** Copyright (C) 2005-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 "qtractorAbout.h" #include "qtractorMidiFile.h" #include "qtractorTimeScale.h" #include "qtractorMidiRpn.h" #include #include // Symbolic header markers. #define SMF_MTHD "MThd" #define SMF_MTRK "MTrk" // - Bank-select (controller) types... #define BANK_MSB 0x00 #define BANK_LSB 0x20 // - Extra-ordinary (controller) types... #define RPN_MSB 0x65 #define RPN_LSB 0x64 #define NRPN_MSB 0x63 #define NRPN_LSB 0x62 #define DATA_MSB 0x06 #define DATA_LSB 0x26 //---------------------------------------------------------------------- // class qtractorMidiFileRpn -- MIDI RPN/NRPN file parser. // class qtractorMidiFileRpn : public qtractorMidiRpn { public: // Constructor. qtractorMidiFileRpn() : qtractorMidiRpn() {} // Encoder. bool process ( unsigned long time, int port, unsigned char status, unsigned short param, unsigned short value ) { qtractorMidiRpn::Event event; event.time = time; event.port = port; event.status = status; event.param = param; event.value = value; return qtractorMidiRpn::process(event); } // Decoder. void dequeue ( qtractorMidiSequence *pSeq ) { while (qtractorMidiRpn::isPending()) { qtractorMidiRpn::Event event; if (qtractorMidiRpn::dequeue(event)) { qtractorMidiEvent::EventType type; switch (qtractorMidiRpn::Type(event.status & 0x70)) { case qtractorMidiRpn::CC: // 0x10 type = qtractorMidiEvent::CONTROLLER; break; case qtractorMidiRpn::RPN: // 0x20 type = qtractorMidiEvent::REGPARAM; break; case qtractorMidiRpn::NRPN: // 0x30 type = qtractorMidiEvent::NONREGPARAM; break; case qtractorMidiRpn::CC14: // 0x40 type = qtractorMidiEvent::CONTROL14; break; default: continue; } qtractorMidiEvent *pEvent = new qtractorMidiEvent( event.time, type, event.param, event.value); pSeq->addEvent(pEvent); pSeq->setChannel(event.status & 0x0f); } } } }; //---------------------------------------------------------------------- // class qtractorMidiFile -- A SMF (Standard MIDI File) class. // // Constructor. qtractorMidiFile::qtractorMidiFile (void) { // SMF instance variables. m_iMode = None; m_pFile = nullptr; m_iOffset = 0; // Header informational data. m_iFormat = 0; m_iTracks = 0; m_iTicksPerBeat = 0; m_pTrackInfo = nullptr; // Special tempo/time-signature map. m_pTempoMap = nullptr; } // Destructor. qtractorMidiFile::~qtractorMidiFile (void) { close(); } // Open file method. bool qtractorMidiFile::open ( const QString& sFilename, int iMode ) { close(); if (iMode == None) iMode = Read; const QByteArray aFilename = sFilename.toUtf8(); m_pFile = ::fopen(aFilename.constData(), iMode == Write ? "w+b" : "rb"); if (m_pFile == nullptr) return false; m_sFilename = sFilename; m_iMode = iMode; m_iOffset = 0; // Bail out of here, if in write mode... if (m_iMode == Write) return true; // First word must identify the file as a SMF; // must be literal "MThd" char header[5]; readData((unsigned char *) &header[0], 4); header[4] = (char) 0; if (::strcmp(header, SMF_MTHD)) { close(); return false; } // Second word should be the total header chunk length... int iMThdLength = readInt(4); if (iMThdLength < 6) { close(); return false; } // Read header data... m_iFormat = (unsigned short) readInt(2); m_iTracks = (unsigned short) readInt(2); m_iTicksPerBeat = (unsigned short) readInt(2); // Should skip any extra bytes... while (iMThdLength > 6) { if (::fgetc(m_pFile) < 0) { close(); return false; } ++m_iOffset; --iMThdLength; } // Allocate the track map. m_pTrackInfo = new TrackInfo [m_iTracks]; for (int iTrack = 0; iTrack < m_iTracks; ++iTrack) { // Must be a track header "MTrk"... readData((unsigned char *) &header[0], 4); header[4] = (char) 0; if (::strcmp(header, SMF_MTRK)) { close(); return false; } // Check track chunk length... const int iMTrkLength = readInt(4); if (iMTrkLength < 0) { close(); return false; } // Set this one track info. m_pTrackInfo[iTrack].length = iMTrkLength; m_pTrackInfo[iTrack].offset = m_iOffset; // Set next track offset... m_iOffset += iMTrkLength; // Advance to next one... if (::fseek(m_pFile, m_iOffset, SEEK_SET)) { close(); return false; } } // Special tempo/time-signature map. m_pTempoMap = new qtractorMidiFileTempo(this); // We're in business... return true; } // Close file method. void qtractorMidiFile::close (void) { if (m_pFile) { ::fclose(m_pFile); m_pFile = nullptr; } if (m_pTrackInfo) { delete [] m_pTrackInfo; m_pTrackInfo = nullptr; } if (m_pTempoMap) { delete m_pTempoMap; m_pTempoMap = nullptr; } } // Sequence/track/channel readers. bool qtractorMidiFile::readTracks ( qtractorMidiSequence **ppSeqs, unsigned short iSeqs, unsigned short iTrackChannel ) { if (m_pFile == nullptr) return false; if (m_pTempoMap == nullptr) return false; if (m_iMode != Read) return false; // Expedite RPN/NRPN controllers processor... qtractorMidiFileRpn xrpn; // So, how many tracks are we reading in a row?... const unsigned short iSeqTracks = (iSeqs > 1 ? m_iTracks : 1); // Go fetch them... for (unsigned short iSeqTrack = 0; iSeqTrack < iSeqTracks; ++iSeqTrack) { // If under a format 0 file, we'll filter for one single channel. if (iSeqTracks > 1) iTrackChannel = iSeqTrack; const unsigned short iTrack = (m_iFormat == 1 ? iTrackChannel : 0); if (iTrack >= m_iTracks) return false; const unsigned short iChannelFilter = (m_iFormat == 1 || iSeqs > 1 ? 0xf0 : iTrackChannel); // Locate the desired track stuff... const unsigned long iTrackStart = m_pTrackInfo[iTrack].offset; if (iTrackStart != m_iOffset) { if (::fseek(m_pFile, iTrackStart, SEEK_SET)) return false; m_iOffset = iTrackStart; } // Now we're going into business... const unsigned long iTrackEnd = m_iOffset + m_pTrackInfo[iTrack].length; unsigned long iTrackTime = 0; unsigned int iLastStatus = 0; unsigned long iTimeout = 0; // While this track lasts... while (m_iOffset < iTrackEnd) { // Read delta timestamp... iTrackTime += readInt(); // Read probable status byte... unsigned int iStatus = readInt(1); // Maybe a running status byte? if ((iStatus & 0x80) == 0) { // Go back one byte... ::ungetc(iStatus, m_pFile); --m_iOffset; iStatus = iLastStatus; } else { iLastStatus = iStatus; } const unsigned short iChannel = (iStatus & 0x0f); qtractorMidiEvent *pEvent; qtractorMidiEvent::EventType type = qtractorMidiEvent::EventType(iStatus & 0xf0); if (iStatus == qtractorMidiEvent::META) type = qtractorMidiEvent::META; // Make proper sequence reference... unsigned short iSeq = 0; if (iSeqs > 1) iSeq = (m_iFormat == 0 ? iChannel : iTrack); qtractorMidiSequence *pSeq = ppSeqs[iSeq]; // Event time converted to sequence resolution... const unsigned long iTime = pSeq->timeq(iTrackTime, m_iTicksPerBeat); // Check for sequence time length, if any... if (pSeq->timeLength() > 0 && iTime >= pSeq->timeOffset() + pSeq->timeLength()) { xrpn.flush(); xrpn.dequeue(pSeq); break; } // Flush/timeout RPN/NRPN stuff... if (iTimeout < iTime || type != qtractorMidiEvent::CONTROLLER) { iTimeout = iTime + (pSeq->ticksPerBeat() >> 2); xrpn.flush(); } // Check whether it won't be channel filtered... const bool bChannelEvent = (iTime >= pSeq->timeOffset() && ((iChannelFilter & 0xf0) || (iChannelFilter == iChannel))); unsigned char *data, data1, data2; unsigned int len, meta, bank; switch (type) { case qtractorMidiEvent::NOTEOFF: case qtractorMidiEvent::NOTEON: data1 = readInt(1); data2 = readInt(1); // Check if its channel filtered... if (bChannelEvent) { if (data2 == 0 && type == qtractorMidiEvent::NOTEON) type = qtractorMidiEvent::NOTEOFF; pEvent = new qtractorMidiEvent(iTime, type, data1, data2); pSeq->addEvent(pEvent); pSeq->setChannel(iChannel); } break; case qtractorMidiEvent::KEYPRESS: data1 = readInt(1); data2 = readInt(1); // Check if its channel filtered... if (bChannelEvent) { // Create the new event... pEvent = new qtractorMidiEvent(iTime, type, data1, data2); pSeq->addEvent(pEvent); pSeq->setChannel(iChannel); } break; case qtractorMidiEvent::CONTROLLER: data1 = readInt(1); data2 = readInt(1); // Check if its channel filtered... if (bChannelEvent) { // Check for RPN/NRPN stuff... if (xrpn.process(iTime, iSeqTrack, (qtractorMidiRpn::CC | iChannel), data1, data2)) { iTimeout = iTime + (pSeq->ticksPerBeat() >> 2); break; } // Create the new event... pEvent = new qtractorMidiEvent(iTime, type, data1, data2); pSeq->addEvent(pEvent); pSeq->setChannel(iChannel); // Set the primordial bank patch... switch (data1) { case BANK_MSB: // Bank MSB if (pSeq->bankSelMethod() < 0) pSeq->setBankSelMethod(1); // Bank-select method (MSB)... switch (pSeq->bankSelMethod()) { case 1: // Bank MSB (current) pSeq->setBank(data2); break; case 2: // Bank LSB (previous) pSeq->setBankSelMethod(0); // Fall thru... case 0: default: bank = (pSeq->bank() < 0 ? 0 : (pSeq->bank() & 0x007f)); pSeq->setBank(bank | (data2 << 7)); break; } break; case BANK_LSB: // Bank LSB if (pSeq->bankSelMethod() < 0) pSeq->setBankSelMethod(2); // Bank-select method (LSB)... switch (pSeq->bankSelMethod()) { case 1: // Bank MSB (previous) bank = (pSeq->bank() < 0 ? 0 : (pSeq->bank() & 0x007f)); pSeq->setBank((bank << 7) | data2); pSeq->setBankSelMethod(0); break; case 2: // Bank LSB (current) pSeq->setBank(data2); break; case 0: // Normal default: bank = (pSeq->bank() < 0 ? 0 : (pSeq->bank() & 0x3f80)); pSeq->setBank(bank | data2); break; } break; default: break; } } break; case qtractorMidiEvent::PGMCHANGE: data1 = readInt(1); data2 = 0x7f; // Check if its channel filtered... if (bChannelEvent) { // Create the new event... pEvent = new qtractorMidiEvent(iTime, type, data1, data2); pSeq->addEvent(pEvent); pSeq->setChannel(iChannel); // Set the primordial program patch... if (pSeq->prog() < 0) pSeq->setProg(data1); } break; case qtractorMidiEvent::CHANPRESS: data1 = 0; data2 = readInt(1); // Check if its channel filtered... if (bChannelEvent) { // Create the new event... pEvent = new qtractorMidiEvent(iTime, type, data1, data2); pSeq->addEvent(pEvent); pSeq->setChannel(iChannel); } break; case qtractorMidiEvent::PITCHBEND: data1 = readInt(1); data2 = readInt(1); // Check if its channel filtered... if (bChannelEvent) { const unsigned short value = (data2 << 7) | data1; // Create the new event... pEvent = new qtractorMidiEvent(iTime, type, 0, value); pSeq->addEvent(pEvent); pSeq->setChannel(iChannel); } break; case qtractorMidiEvent::SYSEX: len = readInt(); if ((int) len < 1) { m_iOffset = iTrackEnd; // Force EoT! break; } data = new unsigned char [1 + len]; data[0] = (unsigned char) type; // Skip 0xf0 head. if (readData(&data[1], len) < (int) len) { delete [] data; return false; } // Check if its channel filtered... if (bChannelEvent) { pEvent = new qtractorMidiEvent(iTime, type); pEvent->setSysex(data, 1 + len); pSeq->addEvent(pEvent); pSeq->setChannel(iChannel); } delete [] data; break; case qtractorMidiEvent::META: meta = qtractorMidiEvent::MetaType(readInt(1)); // Get the meta data... len = readInt(); if ((int) len < 1) { // m_iOffset = iTrackEnd; // Force EoT! break; } if (meta == qtractorMidiEvent::TEMPO) { m_pTempoMap->addNodeTempo(iTrackTime, qtractorTimeScale::uroundf( 60000000.0f / float(readInt(len)))); } else { data = new unsigned char [len + 1]; if (readData(data, len) < (int) len) { delete [] data; return false; } data[len] = (unsigned char) 0; // Now, we'll deal only with some... switch (meta) { case qtractorMidiEvent::TRACKNAME: pSeq->setName( QString::fromLatin1((const char *) data).simplified()); break; case qtractorMidiEvent::TIMESIG: // Beats per bar is the numerator of time signature... if ((unsigned short) data[0] > 0) { m_pTempoMap->addNodeTime(iTrackTime, (unsigned short) data[0], (unsigned short) data[1]); } break; case qtractorMidiEvent::KEYSIG: m_pTempoMap->addMarker(iTrackTime, QString(), int(char(data[0])), bool(data[1])); break; case qtractorMidiEvent::MARKER: m_pTempoMap->addMarker(iTrackTime, QString::fromLatin1((const char *) data).simplified()); break; default: // Ignore all others... break; } delete [] data; } // Fall thru... default: break; } // Flush/pending RPN/NRPN stuff... xrpn.dequeue(pSeq); } } // FIXME: Commit the sequence(s) length... for (unsigned short iSeq = 0; iSeq < iSeqs; ++iSeq) ppSeqs[iSeq]->close(); #ifdef CONFIG_DEBUG_0 for (unsigned short iSeq = 0; iSeq < iSeqs; ++iSeq) { qtractorMidiSequence *pSeq = ppSeqs[iSeq]; qDebug("qtractorMidiFile::readTrack([%u]%p,%u,%u)" " name=\"%s\" events=%d duration=%lu", iSeq, pSeq, iSeqs, iTrackChannel, pSeq->name().toUtf8().constData(), pSeq->events().count(), pSeq->duration()); } #endif return true; } bool qtractorMidiFile::readTrack ( qtractorMidiSequence *pSeq, unsigned short iTrackChannel ) { return readTracks(&pSeq, 1, iTrackChannel); } // Sequence/track/channel duration reader helper. unsigned long qtractorMidiFile::readTrackDuration ( unsigned short iTrackChannel ) { if (m_pFile == nullptr) return 0; if (m_iMode != Read) return 0; const unsigned short iTrack = (m_iFormat == 1 ? iTrackChannel : 0); if (iTrack >= m_iTracks) return 0; const unsigned short iChannelFilter = (m_iFormat == 1 ? 0xf0 : iTrackChannel); // Locate the desired track stuff... const unsigned long iTrackStart = m_pTrackInfo[iTrack].offset; if (iTrackStart != m_iOffset) { if (::fseek(m_pFile, iTrackStart, SEEK_SET)) return 0; m_iOffset = iTrackStart; } // Now we're going into business... const unsigned long iTrackEnd = m_iOffset + m_pTrackInfo[iTrack].length; unsigned long iTrackDuration = 0; unsigned long iTrackTime = 0; unsigned int iLastStatus = 0; // While this track lasts... while (m_iOffset < iTrackEnd) { // Read delta timestamp... iTrackTime += readInt(); // Read probable status byte... unsigned int iStatus = readInt(1); // Maybe a running status byte? if ((iStatus & 0x80) == 0) { // Go back one byte... ::ungetc(iStatus, m_pFile); --m_iOffset; iStatus = iLastStatus; } else { iLastStatus = iStatus; } // Check whether it won't be channel filtered... const unsigned short iChannel = (iStatus & 0x0f); if ((iChannelFilter & 0xf0) || (iChannelFilter == iChannel)) iTrackDuration = iTrackTime; qtractorMidiEvent::EventType type = qtractorMidiEvent::EventType(iStatus & 0xf0); if (iStatus == qtractorMidiEvent::META) type = qtractorMidiEvent::META; switch (type) { case qtractorMidiEvent::NOTEOFF: case qtractorMidiEvent::NOTEON: case qtractorMidiEvent::KEYPRESS: case qtractorMidiEvent::CONTROLLER: case qtractorMidiEvent::PITCHBEND: readInt(2); break; case qtractorMidiEvent::PGMCHANGE: case qtractorMidiEvent::CHANPRESS: readInt(1); break; case qtractorMidiEvent::META: readInt(1); // Fall thru... case qtractorMidiEvent::SYSEX: { int n = readInt(); if (n < 1 || ::fseek(m_pFile, m_iOffset + n, SEEK_SET)) m_iOffset = iTrackEnd; // Force EoT! else m_iOffset += n; } // Fall thru... default: break; } } return iTrackDuration; } // Header writer. bool qtractorMidiFile::writeHeader ( unsigned short iFormat, unsigned short iTracks, unsigned short iTicksPerBeat ) { if (m_pFile == nullptr) return false; if (m_pTempoMap) return false; if (m_iMode != Write) return false; // SMF format 0 sanitization... if (iFormat == 0) iTracks = 1; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiFile::writeHeader(%u,%u,%u)", iFormat, iTracks, iTicksPerBeat); #endif // First word must identify the file as a SMF; // must be literal "MThd" writeData((unsigned char *) SMF_MTHD, 4); // Second word should be the total header chunk length... writeInt(6, 4); // Write header data (6 bytes)... writeInt(m_iFormat = iFormat, 2); writeInt(m_iTracks = iTracks, 2); writeInt(m_iTicksPerBeat = iTicksPerBeat, 2); // Special tempo/time-signature map. m_pTempoMap = new qtractorMidiFileTempo(this); // Assume all is fine. return true; } // Sequence/track writers. bool qtractorMidiFile::writeTracks ( qtractorMidiSequence **ppSeqs, unsigned short iSeqs ) { if (m_pFile == nullptr) return false; if (m_pTempoMap == nullptr) return false; if (m_iMode != Write) return false; #ifdef CONFIG_DEBUG_0 if (ppSeqs == nullptr) qDebug("qtractorMidiFile::writeTrack(nullptr,%u)", iSeqs); for (unsigned short iSeq = 0; ppSeqs && iSeq < iSeqs; ++iSeq) { qtractorMidiSequence *pSeq = ppSeqs[iSeq]; qDebug("qtractorMidiFile::writeTrack([%u]%p,%u)" " name=\"%s\" events=%d duration=%lu", iSeq, pSeq, iSeqs, pSeq->name().toUtf8().constData(), pSeq->events().count(), pSeq->duration()); } #endif // So, how many tracks are we reading in a row?... const unsigned short iTracks = (iSeqs > 1 ? m_iTracks : 1); // Go fetch them... for (unsigned short iTrack = 0; iTrack < iTracks; ++iTrack) { // Make proper initial sequence reference... unsigned short iSeq = 0; if (iSeqs > 1 && m_iFormat == 1) iSeq = iTrack; // Which is just good for track labeling... qtractorMidiSequence *pSeq = nullptr; if (ppSeqs) pSeq = ppSeqs[iSeq]; // Must be a track header "MTrk"... writeData((unsigned char *) SMF_MTRK, 4); // Write a dummy track length (we'll overwrite it later)... const unsigned long iMTrkOffset = m_iOffset; writeInt(0, 4); // Track name... const QString& sTrackName = (pSeq ? pSeq->name() : QFileInfo(m_sFilename).baseName()); if (!sTrackName.isEmpty()) { writeInt(0); // delta-time=0 writeInt(qtractorMidiEvent::META, 1); writeInt(qtractorMidiEvent::TRACKNAME, 1); writeInt(sTrackName.length()); const QByteArray aTrackName = sTrackName.toLatin1(); writeData((unsigned char *) aTrackName.constData(), aTrackName.length()); } // Tempo/time-signature map and location markers... qtractorMidiFileTempo::Node *pNode = m_pTempoMap->nodes().first(); qtractorMidiFileTempo::Marker *pMarker = m_pTempoMap->markers().first(); // Tempo/time-signature map (track 0) // - applicable to SMF format 1 files... if (pSeq == nullptr) { unsigned long iNodeTime = 0; while (pNode) { while (pMarker && pMarker->tick < pNode->tick) { writeMarker(pMarker, iNodeTime); iNodeTime = pMarker->tick; pMarker = pMarker->next(); } writeNode(pNode, iNodeTime); iNodeTime = pNode->tick; pNode = pNode->next(); } while (pMarker) { writeMarker(pMarker, iNodeTime); iNodeTime = pMarker->tick; pMarker = pMarker->next(); } } // Now's time for proper events being written down... // if (pSeq) { // Provisional data structures for sequence merging... struct EventItem { EventItem(qtractorMidiSequence *pSeq) : seq(pSeq), event(pSeq->events().first()) { notesOff.setAutoDelete(true); } qtractorMidiSequence *seq; qtractorMidiEvent *event; qtractorList notesOff; }; EventItem *pItem; unsigned short iItem; // Prolog... const unsigned short iItems = (m_iFormat == 0 ? iSeqs : 1); EventItem **ppItems = new EventItem * [iItems]; for (iItem = 0; iItem < iItems; ++iItem) { iSeq = (m_iFormat == 0 ? iItem : iTrack); ppItems[iItem] = new EventItem(ppSeqs[iSeq]); } // Write the whole sequence out... unsigned int iChannel; unsigned int iStatus; unsigned int iLastStatus = 0; unsigned long iLastTime = 0; unsigned long iTime; unsigned long iTimeOff; unsigned char *data; unsigned int len; EventItem *pEventItem; EventItem *pNoteOffItem; qtractorMidiEvent *pEvent; qtractorMidiEvent *pNoteFirst; qtractorMidiEvent *pNoteAfter; qtractorMidiEvent *pNoteOff; unsigned long iEventTime = 0; // Track/channel bank-select... if (pSeq->bank() >= 0) { iChannel = pSeq->channel(); iStatus = (qtractorMidiEvent::CONTROLLER | iChannel) & 0xff; if (0 >= pSeq->bankSelMethod() || pSeq->bankSelMethod() == 1) { writeInt(0); // delta-time=0 writeInt(iStatus, 1); writeInt(BANK_MSB, 1); // Bank MSB. if (pSeq->bankSelMethod() == 1) writeInt((pSeq->bank() & 0x007f), 1); else writeInt((pSeq->bank() & 0x3f80) >> 7, 1); } if (0 >= pSeq->bankSelMethod() || pSeq->bankSelMethod() == 2) { writeInt(0); // delta-time=0 writeInt(iStatus, 1); writeInt(BANK_LSB, 1); // Bank LSB. writeInt((pSeq->bank() & 0x007f), 1); } } // Track/channel program change... if (pSeq->prog() >= 0) { iChannel = pSeq->channel(); iStatus = (qtractorMidiEvent::PGMCHANGE | iChannel) & 0xff; writeInt(0); // delta-time=0 writeInt(iStatus, 1); writeInt((pSeq->prog() & 0x007f), 1); } // Lets-a go, down to event merge loop... // for (;;) { // Find which will be next event to write out, // keeping account onto sequence merging... pSeq = nullptr; pEvent = nullptr; pEventItem = nullptr; for (iItem = 0; iItem < iItems; ++iItem) { pItem = ppItems[iItem]; if (pItem->event && (pEventItem == nullptr || iEventTime >= (pItem->event)->time())) { iEventTime = (pItem->event)->time(); pEventItem = pItem; } } // So we'll have another event ready? if (pEventItem) { pSeq = pEventItem->seq; pEvent = pEventItem->event; pEventItem->event = pEvent->next(); if (pEventItem->event) iEventTime = (pEventItem->event)->time(); } // Maybe we reached the (partial) end... if (pEvent == nullptr) break; // Strip out all bank-select/program-changes here... if ((pEvent->type() == qtractorMidiEvent::PGMCHANGE) || (pEvent->type() == qtractorMidiEvent::CONTROLLER && (pEvent->controller() == BANK_MSB || pEvent->controller() == BANK_LSB))) { continue; } // Event (absolute) time converted to file resolution... iTime = pSeq->timep(pEvent->time(), m_iTicksPerBeat); // Check for pending note-offs... for (;;) { // Get any a note-off event pending... iTimeOff = iTime; pNoteOff = nullptr; pNoteOffItem = nullptr; for (iItem = 0; iItem < iItems; ++iItem) { pItem = ppItems[iItem]; pNoteFirst = pItem->notesOff.first(); if (pNoteFirst && iTimeOff >= pNoteFirst->time()) { iTimeOff = pNoteFirst->time(); pNoteOff = pNoteFirst; pNoteOffItem = pItem; } } // Was there any? if (pNoteOff == nullptr) break; // - Delta time... writeInt(iTimeOff > iLastTime ? iTimeOff - iLastTime : 0); iLastTime = iTimeOff; // - Status byte... iChannel = (pNoteOffItem->seq)->channel(); iStatus = (pNoteOff->type() | iChannel) & 0xff; // - Running status... if (iStatus != iLastStatus) { writeInt(iStatus, 1); iLastStatus = iStatus; } // - Data bytes... writeInt(pNoteOff->note(), 1); writeInt(pNoteOff->velocity(), 1); // Remove from note-off list and continue... pNoteOffItem->notesOff.remove(pNoteOff); } // Tempo/time-signature map (interleaved) // - applicable to SMF format 0 files... if (iSeqs > 1 && iTrack == 0) { while (pNode && iTime >= pNode->tick) { while (pMarker && pMarker->tick < pNode->tick) { writeMarker(pMarker, iLastTime); iLastTime = pSeq->timep(pMarker->tick, m_iTicksPerBeat); pMarker = pMarker->next(); } writeNode(pNode, iLastTime); iLastTime = pSeq->timep(pNode->tick, m_iTicksPerBeat); iLastStatus = 0; pNode = pNode->next(); } while (pMarker && iTime >= pMarker->tick) { writeMarker(pMarker, iLastTime); iLastTime = pSeq->timep(pMarker->tick, m_iTicksPerBeat); iLastStatus = 0; pMarker = pMarker->next(); } } // OK. Let's get back to actual event... // - Delta time... writeInt(iTime > iLastTime ? iTime - iLastTime : 0); iLastTime = iTime; // - Status byte... iChannel = pSeq->channel(); // - Extra-ordinary (controller) types... const qtractorMidiEvent::EventType etype = pEvent->type(); if (etype == qtractorMidiEvent::REGPARAM || etype == qtractorMidiEvent::NONREGPARAM || etype == qtractorMidiEvent::CONTROL14) { iStatus = (qtractorMidiEvent::CONTROLLER | iChannel) & 0xff; } else { iStatus = (etype | iChannel) & 0xff; } // - Running status? nb. not for sysex, ok? if (iStatus != iLastStatus || etype == qtractorMidiEvent::SYSEX) { writeInt(iStatus, 1); iLastStatus = iStatus; } // - Data bytes... switch (etype) { case qtractorMidiEvent::NOTEON: writeInt(pEvent->note(), 1); writeInt(pEvent->velocity(), 1); iTimeOff = (pEvent->time() + pEvent->duration()); pNoteOff = new qtractorMidiEvent( pSeq->timep(iTimeOff, m_iTicksPerBeat), qtractorMidiEvent::NOTEOFF, pEvent->note()); // Find the proper position in notes-off list ... pNoteAfter = pEventItem->notesOff.last(); while (pNoteAfter && pNoteAfter->time() > pNoteOff->time()) pNoteAfter = pNoteAfter->prev(); if (pNoteAfter) pEventItem->notesOff.insertAfter(pNoteOff, pNoteAfter); else pEventItem->notesOff.prepend(pNoteOff); break; case qtractorMidiEvent::KEYPRESS: writeInt(pEvent->note(), 1); writeInt(pEvent->velocity(), 1); break; case qtractorMidiEvent::CONTROLLER: writeInt(pEvent->controller(), 1); writeInt(pEvent->value(), 1); break; case qtractorMidiEvent::CONTROL14: writeInt((pEvent->controller() & 0x1f), 1); writeInt((pEvent->value() & 0x3f80) >> 7, 1); writeInt(0); // delta-time=0 // writeInt(iStatus, 1); writeInt((pEvent->controller() + 0x20) & 0x3f, 1); writeInt((pEvent->value() & 0x007f), 1); break; case qtractorMidiEvent::REGPARAM: writeInt(RPN_MSB, 1); writeInt((pEvent->param() & 0x3f80) >> 7, 1); writeInt(0); // delta-time=0 // writeInt(iStatus, 1); writeInt(RPN_LSB, 1); writeInt((pEvent->param() & 0x007f), 1); writeInt(0); // delta-time=0 // writeInt(iStatus, 1); writeInt(DATA_MSB, 1); writeInt((pEvent->value() & 0x3f80) >> 7, 1); writeInt(0); // delta-time=0 // writeInt(iStatus, 1); writeInt(DATA_LSB, 1); writeInt((pEvent->value() & 0x007f), 1); break; case qtractorMidiEvent::NONREGPARAM: writeInt(NRPN_MSB, 1); writeInt((pEvent->param() & 0x3f80) >> 7, 1); writeInt(0); // delta-time=0 // writeInt(iStatus, 1); writeInt(NRPN_LSB, 1); writeInt((pEvent->param() & 0x007f), 1); writeInt(0); // delta-time=0 // writeInt(iStatus, 1); writeInt(DATA_MSB, 1); writeInt((pEvent->value() & 0x3f80) >> 7, 1); writeInt(0); // delta-time=0 // writeInt(iStatus, 1); writeInt(DATA_LSB, 1); writeInt((pEvent->value() & 0x007f), 1); break; case qtractorMidiEvent::PGMCHANGE: writeInt(pEvent->param(), 1); break; case qtractorMidiEvent::CHANPRESS: writeInt(pEvent->value(), 1); break; case qtractorMidiEvent::PITCHBEND: writeInt((pEvent->value() & 0x007f), 1); writeInt((pEvent->value() & 0x3f80) >> 7, 1); break; case qtractorMidiEvent::SYSEX: if (pEvent->sysex() && pEvent->sysex_len() > 1) { data = pEvent->sysex() + 1; // Skip 0xf0 head. len = pEvent->sysex_len() - 1; writeInt(len); writeData(data, len); } // Fall thru... default: break; } } // Merge all remaining note-offs... for (iItem = 0; iItem < iItems; ++iItem) { pItem = ppItems[iItem]; pItem->event = pItem->notesOff.first(); } iTimeOff = 0; for (;;) { // Find which note-off will be next to write out, // always accounting for sequence merging... pNoteOff = nullptr; pNoteOffItem = nullptr; for (iItem = 0; iItem < iItems; ++iItem) { pItem = ppItems[iItem]; if (pItem->event && (pNoteOffItem == nullptr || iTimeOff >= (pItem->event)->time())) { iTimeOff = (pItem->event)->time(); pNoteOffItem = pItem; } } // So we'll have another event ready? if (pNoteOffItem) { pNoteOff = pNoteOffItem->event; pNoteOffItem->event = pNoteOff->next(); if (pNoteOffItem->event) iTimeOff = (pNoteOffItem->event)->time(); } // Maybe we reached the (whole) end... if (pNoteOff == nullptr) break; // - Delta time... iTime = pNoteOff->time(); writeInt(iTime > iLastTime ? iTime - iLastTime : 0); iLastTime = iTime; // - Status byte... iChannel = (pNoteOffItem->seq)->channel(); iStatus = (pNoteOff->type() | iChannel) & 0xff; // - Running status... if (iStatus != iLastStatus) { writeInt(iStatus, 1); iLastStatus = iStatus; } // - Data bytes... writeInt(pNoteOff->note(), 1); writeInt(pNoteOff->velocity(), 1); // Done with this note-off... } // Epilog... for (iItem = 0; iItem < iItems; ++iItem) delete ppItems[iItem]; delete [] ppItems; } // End-of-track marker. writeInt(0); // delta-time=0 writeInt(qtractorMidiEvent::META, 1); writeInt(qtractorMidiEvent::EOT, 1); writeInt(0); // length=0; // Time to overwrite the actual track length... if (::fseek(m_pFile, iMTrkOffset, SEEK_SET)) return false; // Do it... writeInt(m_iOffset - (iMTrkOffset + 4), 4); m_iOffset -= 4; // Restore file position to end-of-file... if (::fseek(m_pFile, m_iOffset, SEEK_SET)) return false; } // Success. return true; } bool qtractorMidiFile::writeTrack ( qtractorMidiSequence *pSeq ) { return writeTracks((pSeq ? &pSeq : nullptr), 1); } // Integer read method. int qtractorMidiFile::readInt ( unsigned short n ) { int c, val = 0; if (n > 0) { // Fixed length (n bytes) integer read. for (int i = 0; i < n; ++i) { val <<= 8; c = ::fgetc(m_pFile); if (c < 0) return -1; val |= c; ++m_iOffset; } } else { // Variable length integer read. do { c = ::fgetc(m_pFile); if (c < 0) return -1; val <<= 7; val |= (c & 0x7f); ++m_iOffset; } while ((c & 0x80) == 0x80); } return val; } // Raw data read method. int qtractorMidiFile::readData ( unsigned char *pData, unsigned short n ) { const int nread = ::fread(pData, sizeof(unsigned char), n, m_pFile); if (nread > 0) m_iOffset += nread; return nread; } // Integer write method. int qtractorMidiFile::writeInt ( int val, unsigned short n ) { unsigned int c; if (n > 0) { // Fixed length (n bytes) integer write. for (int i = (n - 1) * 8; i >= 0; i -= 8) { c = (val & (0xff << i)) >> i; if (::fputc(c & 0xff, m_pFile) < 0) return -1; ++m_iOffset; } } else { // Variable length integer write. n = 0; c = val & 0x7f; while ((val >>= 7) > 0) { c <<= 8; c |= (val & 0x7f) | 0x80; } while (true) { if (::fputc(c & 0xff, m_pFile) < 0) return -1; ++n; if ((c & 0x80) == 0) break; c >>= 8; } m_iOffset += n; } return n; } // Raw data write method. int qtractorMidiFile::writeData ( unsigned char *pData, unsigned short n ) { const int nwrite = ::fwrite(pData, sizeof(unsigned char), n, m_pFile); if (nwrite > 0) m_iOffset += nwrite; return nwrite; } // Write tempo-time-signature node. void qtractorMidiFile::writeNode ( qtractorMidiFileTempo::Node *pNode, unsigned long iLastTime ) { unsigned long iDeltaTime = (pNode->tick > iLastTime ? pNode->tick - iLastTime : 0); qtractorMidiFileTempo::Node *pPrev = pNode->prev(); if (pPrev == nullptr || (pPrev->tempo != pNode->tempo)) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiFile::writeNode(%lu) time=%lu TEMPO (%g)", iLastTime, iDeltaTime, pNode->tempo); #endif // Tempo change... writeInt(iDeltaTime); writeInt(qtractorMidiEvent::META, 1); writeInt(qtractorMidiEvent::TEMPO, 1); writeInt(3); writeInt(qtractorTimeScale::uroundf(60000000.0f / pNode->tempo), 3); iDeltaTime = 0; } if (pPrev == nullptr || pPrev->beatsPerBar != pNode->beatsPerBar || pPrev->beatDivisor != pNode->beatDivisor) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiFile::writeNode(%lu) time=%lu TIME (%u/%u)", iLastTime, iDeltaTime, pNode->beatsPerBar, 1 << pNode->beatDivisor); #endif // Time signature change... writeInt(iDeltaTime); writeInt(qtractorMidiEvent::META, 1); writeInt(qtractorMidiEvent::TIMESIG, 1); writeInt(4); writeInt(pNode->beatsPerBar, 1); // Numerator. writeInt(pNode->beatDivisor, 1); // Denominator. writeInt(32, 1); // MIDI clocks per metronome click. writeInt(4, 1); // 32nd notes per quarter. // iDeltaTime = 0; } } // Write location marker. void qtractorMidiFile::writeMarker ( qtractorMidiFileTempo::Marker *pMarker, unsigned long iLastTime ) { const unsigned long iDeltaTime = (pMarker->tick > iLastTime ? pMarker->tick - iLastTime : 0); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiFile::writeMarker(%lu) time=%lu (\"%s\")", iLastTime, iDeltaTime, pMarker->text.toUft8().constData()); #endif writeInt(iDeltaTime); if (qtractorTimeScale::isKeySignature(pMarker->accidentals, pMarker->mode)) { writeInt(qtractorMidiEvent::META, 1); writeInt(qtractorMidiEvent::KEYSIG, 1); writeInt(2); writeInt(pMarker->accidentals, 1); // 1 byte, can be negative... writeInt(pMarker->mode, 1); } if (!pMarker->text.isEmpty()) { writeInt(qtractorMidiEvent::META, 1); writeInt(qtractorMidiEvent::MARKER, 1); writeInt(pMarker->text.length()); const QByteArray aMarker = pMarker->text.toLatin1(); writeData((unsigned char *) aMarker.constData(), aMarker.length()); } } // Create filename revision (name says it all). QString qtractorMidiFile::createFilePathRevision ( const QString& sFilename, int iRevision ) { QFileInfo fi(sFilename); const QRegularExpression rxRevision("(.+)\\-(\\d+)$"); QString sBasename = fi.baseName(); QRegularExpressionMatch match = rxRevision.match(sBasename); if (match.hasMatch()) { sBasename = match.captured(1); iRevision = match.captured(2).toInt(); } sBasename += "-%1." + fi.completeSuffix(); if (iRevision < 1) ++iRevision; const QDir adir(fi.absoluteDir()); fi.setFile(adir, sBasename.arg(iRevision)); while (fi.exists()) fi.setFile(adir, sBasename.arg(++iRevision)); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiFile::createFilePathRevision(\"%s\")", fi.absoluteFilePath().toUtf8().constData()); #endif return fi.absoluteFilePath(); } // All-in-one SMF file writer/creator method. bool qtractorMidiFile::saveCopyFile ( const QString& sNewFilename, const QString& sOldFilename, unsigned short iTrackChannel, unsigned short iFormat, qtractorMidiSequence *pSeq, qtractorTimeScale *pTimeScale, unsigned long iTimeOffset ) { qtractorMidiFile file; qtractorTimeScale ts; unsigned short iTracks; unsigned short iSeq, iSeqs = 0; qtractorMidiSequence **ppSeqs = nullptr; const QString sTrackName("Track %1"); if (pSeq == nullptr) return false; if (pTimeScale) ts.copy(*pTimeScale); // Open and load the whole source file... if (file.open(sOldFilename)) { ts.setTicksPerBeat(file.ticksPerBeat()); iFormat = file.format(); iSeqs = (iFormat == 1 ? file.tracks() : 16); ppSeqs = new qtractorMidiSequence * [iSeqs]; for (iSeq = 0; iSeq < iSeqs; ++iSeq) { ppSeqs[iSeq] = new qtractorMidiSequence( sTrackName.arg(iSeq + 1), iSeq, ts.ticksPerBeat()); } if (file.readTracks(ppSeqs, iSeqs) && file.tempoMap()) file.tempoMap()->intoTimeScale(&ts, iTimeOffset); file.close(); } // Open and save the whole target file... if (!file.open(sNewFilename, qtractorMidiFile::Write)) return false; if (ppSeqs == nullptr) iSeqs = (iFormat == 0 ? 1 : 2); // Write SMF header... iTracks = (iFormat == 0 ? 1 : iSeqs); if (!file.writeHeader(iFormat, iTracks, ts.ticksPerBeat())) { file.close(); return false; } // Set (initial) tempo/time-signature node. if (file.tempoMap()) file.tempoMap()->fromTimeScale(&ts, iTimeOffset); // Write SMF tracks(s)... if (ppSeqs) { // Replace the target track-channel events... ppSeqs[iTrackChannel]->replaceEvents(pSeq); // Write the whole new tracks... file.writeTracks(ppSeqs, iSeqs); } else { // Most probabley this is a brand new file... if (iFormat == 1) file.writeTrack(nullptr); file.writeTrack(pSeq); } file.close(); // Free locally allocated track/sequence array. if (ppSeqs) { for (iSeq = 0; iSeq < iSeqs; ++iSeq) delete ppSeqs[iSeq]; delete [] ppSeqs; } return true; } // end of qtractorMidiFile.cpp qtractor-1.5.11/src/PaxHeaders/qtractorSessionForm.ui0000644000000000000000000000013215124701674017700 xustar0030 mtime=1767080892.800263424 30 atime=1767080892.800263424 30 ctime=1767080892.800263424 qtractor-1.5.11/src/qtractorSessionForm.ui0000644000175000001440000003776715124701674017714 0ustar00rncbcusers rncbc aka Rui Nuno Capela qtractor - An Audio/MIDI multi-track sequencer. Copyright (C) 2005-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. qtractorSessionForm 0 0 480 240 640 320 Qt::StrongFocus Session :/images/qtractor.svg 0 Session &Name: SessionNameLineEdit 320 0 Session name 0 &Directory: SessionDirComboBox Whether to auto-name the session directory &Auto Qt::Horizontal 20 8 0 320 0 Session directory true 22 22 24 24 Qt::TabFocus Browse for session directory ... &Description: DescriptionTextEdit Session description Properties Time Sample &Rate: Qt::AlignRight|Qt::AlignVCenter SampleRateComboBox Sample rate (Hz) true 44100 48000 96000 192000 &Tempo: Qt::AlignRight|Qt::AlignVCenter TempoSpinBox 100 0 Tempo (BPM) / Signature T&icks/Beat: Qt::AlignRight|Qt::AlignVCenter TicksPerBeatSpinBox Resolution (ticks/beat; tpqn) 24 3840 24 960 Qt::Vertical 20 20 View &Snap/Beat: Qt::AlignRight|Qt::AlignVCenter SnapPerBeatComboBox Snap/beat false &Pixels/Beat: Qt::AlignRight|Qt::AlignVCenter PixelsPerBeatSpinBox Pixels/beat 4 240 4 32 &Horizontal Zoom: Qt::AlignRight|Qt::AlignVCenter HorizontalZoomSpinBox Horizontal Zoom (%) % 10 1000 10 100 &Vertical Zoom: Qt::AlignRight|Qt::AlignVCenter VerticalZoomSpinBox Vertical Zoom (%) % 10 1000 10 100 Qt::Vertical 20 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorTempoSpinBox QSpinBox
qtractorSpinBox.h
SessionNameLineEdit AutoSessionDirCheckBox SessionDirComboBox SessionDirToolButton DescriptionTextEdit SampleRateComboBox TempoSpinBox TicksPerBeatSpinBox SnapPerBeatComboBox PixelsPerBeatSpinBox HorizontalZoomSpinBox VerticalZoomSpinBox SessionTabWidget DialogButtonBox
qtractor-1.5.11/src/PaxHeaders/qtractorMmcEvent.h0000644000000000000000000000013215124701674016761 xustar0030 mtime=1767080892.796263441 30 atime=1767080892.796263441 30 ctime=1767080892.796263441 qtractor-1.5.11/src/qtractorMmcEvent.h0000644000175000001440000000745715124701674016766 0ustar00rncbcusers// qtractorMmcEvent.h // /**************************************************************************** Copyright (C) 2005-2017, 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 __qtractorMmcEvent_h #define __qtractorMmcEvent_h #include //---------------------------------------------------------------------- // qtractorMmcEvent - MMC custom event. // class qtractorMmcEvent { public: // MMC command codes. enum Command { STOP = 0x01, PLAY = 0x02, DEFERRED_PLAY = 0x03, FAST_FORWARD = 0x04, REWIND = 0x05, RECORD_STROBE = 0x06, RECORD_EXIT = 0x07, RECORD_PAUSE = 0x08, PAUSE = 0x09, EJECT = 0x0a, CHASE = 0x0b, COMMAND_ERROR_RESET = 0x0c, MMC_RESET = 0x0d, JOG_START = 0x20, JOG_STOP = 0x21, WRITE = 0x40, MASKED_WRITE = 0x41, READ = 0x42, UPDATE = 0x43, LOCATE = 0x44, VARIABLE_PLAY = 0x45, SEARCH = 0x46, SHUTTLE = 0x47, STEP = 0x48, ASSIGN_SYSTEM_MASTER = 0x49, GENERATOR_COMMAND = 0x4a, MTC_COMMAND = 0x4b, MOVE = 0x4c, ADD = 0x4d, SUBTRACT = 0x4e, DROP_FRAME_ADJUST = 0x4f, PROCEDURE = 0x50, EVENT = 0x51, GROUP = 0x52, COMMAND_SEGMENT = 0x53, DEFERRED_VARIABLE_PLAY = 0x54, RECORD_STROBE_VARIABLE = 0x55, WAIT = 0x7c, RESUME = 0x7f }; // MMC sub-command codes (as for MASKED_WRITE). enum SubCommand { TRACK_NONE = 0x00, TRACK_RECORD = 0x4f, TRACK_MONITOR = 0x53, TRACK_MUTE = 0x62, TRACK_SOLO = 0x66 // Custom-implementation ;) }; // Default contructor (fake). qtractorMmcEvent() : m_cmd(Command(0)) {} // Contructor. qtractorMmcEvent(unsigned char *pSysex) : m_cmd(Command(pSysex[4])), m_data((const char *) &pSysex[6], (int) pSysex[5]) {} // Copy contructor. qtractorMmcEvent(const qtractorMmcEvent& mmce) : m_cmd(mmce.m_cmd), m_data(mmce.m_data) {} // Accessors. Command cmd() const { return m_cmd; } unsigned char *data() const { return (unsigned char *) m_data.constData(); } unsigned short len() const { return (unsigned short) m_data.length(); } // Retrieve MMC time-code and standard frame position (SMPTE?). unsigned long locate() const; // Retrieve MMC shuttle-speed and direction. float shuttle() const; // Retrieve MMC step and direction. int step() const; // Retrieve MMC masked-write sub-command data. SubCommand scmd() const; int track() const; bool isOn() const; private: // Instance variables. Command m_cmd; QByteArray m_data; }; #endif // __qtractorMmcEvent_h // end of qtractorMmcEvent.h qtractor-1.5.11/src/PaxHeaders/qtractorTempoAdjustForm.cpp0000644000000000000000000000013015124701674020657 xustar0029 mtime=1767080892.80126342 30 atime=1767080892.800263424 29 ctime=1767080892.80126342 qtractor-1.5.11/src/qtractorTempoAdjustForm.cpp0000644000175000001440000006111415124701674020654 0ustar00rncbcusers// qtractorTempoAdjustForm.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorTempoAdjustForm.h" #include "qtractorAbout.h" #include "qtractorSession.h" #include "qtractorAudioClip.h" #include "qtractorAudioEngine.h" #include "qtractorMainForm.h" #include "qtractorTracks.h" #include #include #include #include #include #include #include #ifdef CONFIG_LIBAUBIO #include #endif #ifdef CONFIG_MINIBPM #include "minibpm/src/MiniBpm.cpp" #endif // Audio clip beat-detection callback. struct audioClipTempoDetectData { // Ctor. audioClipTempoDetectData(unsigned short iChannels, unsigned iSampleRate, unsigned int iBlockSize = 1024) : count(0), offset(0), channels(iChannels), nstep(iBlockSize >> 3) { #ifdef CONFIG_LIBAUBIO aubio = new_aubio_tempo("default", iBlockSize, nstep, iSampleRate); ibuf = new_fvec(nstep); obuf = new_fvec(1); #endif #ifdef CONFIG_MINIBPM minibpm = new breakfastquay::MiniBPM * [iChannels]; for (unsigned short i = 0; i < channels; ++i) minibpm[i] = new breakfastquay::MiniBPM(iSampleRate); #endif } // Dtor. ~audioClipTempoDetectData() { #ifdef CONFIG_LIBAUBIO beats.clear(); del_fvec(obuf); del_fvec(ibuf); del_aubio_tempo(aubio); #endif #ifdef CONFIG_MINIBPM for (unsigned short i = 0; i < channels; ++i) delete minibpm[i]; delete [] minibpm; #endif } // Members. unsigned int count; unsigned long offset; unsigned short channels; unsigned int nstep; #ifdef CONFIG_LIBAUBIO aubio_tempo_t *aubio; fvec_t *ibuf; fvec_t *obuf; QList beats; #endif #ifdef CONFIG_MINIBPM breakfastquay::MiniBPM **minibpm; #endif }; static void audioClipTempoDetect ( float **ppFrames, unsigned int iFrames, void *pvArg ) { audioClipTempoDetectData *pData = static_cast (pvArg); #ifdef CONFIG_LIBAUBIO unsigned int i = 0; while (i < iFrames) { unsigned int j = 0; for (; j < pData->nstep && i < iFrames; ++j, ++i) { float fSum = 0.0f; for (unsigned short n = 0; n < pData->channels; ++n) fSum += ppFrames[n][i]; fvec_set_sample(pData->ibuf, fSum / float(pData->channels), j); } for (; j < pData->nstep; ++j) fvec_set_sample(pData->ibuf, 0.0f, j); aubio_tempo_do(pData->aubio, pData->ibuf, pData->obuf); const bool is_beat = bool(fvec_get_sample(pData->obuf, 0)); if (is_beat) { unsigned long iOffset = aubio_tempo_get_last(pData->aubio); if (iOffset >= pData->offset) iOffset -= pData->offset; pData->beats.append(iOffset); } } #endif #ifdef CONFIG_MINIBPM for (unsigned short i = 0; i < pData->channels; ++i) { pData->minibpm[i]->process(ppFrames[i], iFrames); } #endif if (++(pData->count) > 100) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { QProgressBar *pProgressBar = pMainForm->progressBar(); pProgressBar->setValue(pProgressBar->value() + iFrames); } qtractorSession::stabilize(); pData->count = 0; } } //---------------------------------------------------------------------------- // qtractorTempoAdjustForm::ClipWidget -- Dang simple graphical widget class qtractorTempoAdjustForm::ClipWidget : public QFrame { public: // Constructor. ClipWidget(qtractorTempoAdjustForm *pForm) : QFrame(pForm), m_pForm(pForm) { QFrame::setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding); QFrame::setFrameShape(QFrame::Panel); QFrame::setFrameShadow(QFrame::Sunken); qtractorClip *pClip = m_pForm->clip(); if (pClip) { qtractorTrack *pTrack = pClip->track(); if (pTrack) { QPalette pal; pal.setColor(QPalette::WindowText, pTrack->foreground()); pal.setColor(QPalette::Window, pTrack->background()); QFrame::setPalette(pal); } } } // Accessors. void setBeats( const QList& beats ) { m_beats = beats; QFrame::update(); } const QList& beats() const { return m_beats; } void clearBeats() { m_beats.clear(); QFrame::update(); } // Refresh method. void refresh() { updatePixmap(); QFrame::update(); } protected: // Backing-store method. void updatePixmap() { qtractorClip *pClip = m_pForm->clip(); if (pClip == nullptr) return; qtractorTrack *pTrack = pClip->track(); if (pTrack == nullptr) return; qtractorTimeScale *pTimeScale = m_pForm->timeScale(); if (pTimeScale == nullptr) return; const unsigned long iRangeStart = m_pForm->rangeStart(); const unsigned long iRangeLength = m_pForm->rangeLength(); const int w = pTimeScale->pixelFromFrame(iRangeStart + iRangeLength) - pTimeScale->pixelFromFrame(iRangeStart); const int h = QFrame::height(); m_pixmap = QPixmap(w, h); m_pixmap.fill(QFrame::palette().base().color()); // Render the actual clip region... QPainter painter(&m_pixmap); QColor bg = pTrack->background(); const QPen pen(bg.darker()); bg.setAlpha(192); // translucency... #ifdef CONFIG_GRADIENT QLinearGradient grad(0, 0, 0, h); grad.setColorAt(0.4, bg); grad.setColorAt(1.0, bg.darker(130)); const QBrush brush(grad); #else const QBrush brush(bg); #endif painter.setPen(pen); painter.setBrush(brush); const unsigned long iClipOffset = iRangeStart - pClip->clipStart(); const QRect rectClip(0, 0, w, h); painter.drawRect(rectClip); pClip->draw(&painter, rectClip, iClipOffset); } // Paint method... void paintEvent(QPaintEvent *pPaintEvent) { QPainter painter(this); // Render the scaled pixmap region... const qreal w = qreal(QFrame::width()); const qreal h = qreal(QFrame::height()); const qreal dw = qreal(m_pixmap.width()) / w; const qreal dh = qreal(m_pixmap.height()) / h; const QRectF& rect = QRectF(pPaintEvent->rect()); painter.drawPixmap(rect, m_pixmap, QRectF( rect.x() * dw, rect.y() * dh, rect.width() * dw, rect.height() * dh)); // Render beat lines... const unsigned short iRangeBeats = m_pForm->rangeBeats(); if (iRangeBeats > 0) { const qreal dx = w / qreal(iRangeBeats); for (qreal x = dx; x < w; x += dx) { painter.drawLine(QPointF(x, 0), QPointF(x, h)); } } if (!m_beats.isEmpty()) { qtractorClip *pClip = m_pForm->clip(); qtractorTimeScale *pTimeScale = m_pForm->timeScale(); if (pClip && pTimeScale) { const unsigned long iRangeStart = m_pForm->rangeStart(); painter.setPen(Qt::darkRed); foreach (unsigned long iOffset, m_beats) { const int w1 = pTimeScale->pixelFromFrame(iRangeStart + iOffset) - pTimeScale->pixelFromFrame(iRangeStart); const qreal x = w1 / dw; painter.drawLine(QPointF(x, 0), QPointF(x, h)); } } } } // Resize method... void resizeEvent(QResizeEvent *) { updatePixmap(); } private: // Instance variables. qtractorTempoAdjustForm *m_pForm; // Local double-buffering pixmap. QPixmap m_pixmap; // Extracted beat offsets. QList m_beats; }; //---------------------------------------------------------------------------- // qtractorTempoAdjustForm -- UI wrapper form. // Constructor. qtractorTempoAdjustForm::qtractorTempoAdjustForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Initialize local time scale. m_pTimeScale = new qtractorTimeScale(); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) m_pTimeScale->copy(*pSession->timeScale()); m_pClip = nullptr; m_pAudioClip = nullptr; m_pClipWidget = nullptr; m_pTempoTap = new QElapsedTimer(); m_iTempoTap = 0; m_fTempoTap = 0.0f; m_ui.RangeStartSpinBox->setTimeScale(m_pTimeScale); m_ui.RangeLengthSpinBox->setTimeScale(m_pTimeScale); // FIXME: Set an absolute max. 30 seconds... m_ui.RangeLengthSpinBox->setMaximum(30 * m_pTimeScale->sampleRate()); m_ui.TempoSpinBox->setTempo(m_pTimeScale->tempo(), false); m_ui.TempoSpinBox->setBeatsPerBar(m_pTimeScale->beatsPerBar(), false); m_ui.TempoSpinBox->setBeatDivisor(m_pTimeScale->beatDivisor(), true); m_ui.TempoResetPushButton->setEnabled(false); // Set proper time scales display format... m_ui.FormatComboBox->setCurrentIndex(int(m_pTimeScale->displayFormat())); // Initialize dirty control state (nope). m_iDirtySetup = 0; m_iDirtyCount = 0; // Try to set minimal window positioning. adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.TempoSpinBox, SIGNAL(valueChanged(float, unsigned short, unsigned short)), SLOT(tempoChanged())); QObject::connect(m_ui.TempoTapPushButton, SIGNAL(clicked()), SLOT(tempoTap())); QObject::connect(m_ui.TempoDetectPushButton, SIGNAL(clicked()), SLOT(tempoDetect())); QObject::connect(m_ui.TempoResetPushButton, SIGNAL(clicked()), SLOT(tempoReset())); QObject::connect(m_ui.RangeStartSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(rangeStartChanged(unsigned long))); QObject::connect(m_ui.RangeStartSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.RangeLengthSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(rangeLengthChanged(unsigned long))); QObject::connect(m_ui.RangeLengthSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.RangeBeatsSpinBox, SIGNAL(valueChanged(int)), SLOT(rangeBeatsChanged(int))); QObject::connect(m_ui.FormatComboBox, SIGNAL(activated(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.AdjustPushButton, SIGNAL(clicked()), SLOT(tempoAdjust())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); } // Destructor. qtractorTempoAdjustForm::~qtractorTempoAdjustForm (void) { setClip(nullptr); // Don't forget to get rid of local time-scale instance... if (m_pTimeScale) delete m_pTimeScale; if (m_pTempoTap) delete m_pTempoTap; } // Clip accessors. void qtractorTempoAdjustForm::setClip ( qtractorClip *pClip ) { m_pClip = pClip; if (m_pClip) { const unsigned long iClipStart = m_pClip->clipStart(); const unsigned long iClipLength = m_pClip->clipLength(); unsigned long iMaxRangeLength = m_ui.RangeLengthSpinBox->maximum(); if (iMaxRangeLength > iClipLength) iMaxRangeLength = iClipLength; m_ui.RangeStartSpinBox->setMinimum(iClipStart); m_ui.RangeStartSpinBox->setMaximum(iClipStart + iMaxRangeLength); m_ui.RangeLengthSpinBox->setMaximum(iMaxRangeLength); } if (m_pClip && m_pClip->track() && (m_pClip->track())->trackType() == qtractorTrack::Audio) m_pAudioClip = static_cast (m_pClip); else m_pAudioClip = nullptr; if (m_pClipWidget) { delete m_pClipWidget; m_pClipWidget = nullptr; } if (m_pClip) { m_pClipWidget = new ClipWidget(this); m_pClipWidget->setMinimumHeight(80); m_ui.MainBoxLayout->insertWidget(0, m_pClipWidget); // const int iTempoGroup // = m_ui.GroupBoxLayout->indexOf(m_ui.TempoGroupBox); const int iRangeGroup = m_ui.GroupBoxLayout->indexOf(m_ui.RangeGroupBox); const int iFormatGroup = m_ui.GroupBoxLayout->indexOf(m_ui.FormatGroupBox); // QLayoutItem *pTempoItem = m_ui.GroupBoxLayout->takeAt(iTempoGroup); // if (pTempoItem) // delete pTempoItem; QLayoutItem *pRangeItem = m_ui.GroupBoxLayout->takeAt(iRangeGroup); if (pRangeItem) delete pRangeItem; QLayoutItem *pFormatItem = m_ui.GroupBoxLayout->takeAt(iFormatGroup); if (pFormatItem) delete pFormatItem; // m_ui.GroupBoxLayout->addWidget(m_ui.TempoGroupBox, 0, 1, 1, 3); m_ui.GroupBoxLayout->addWidget(m_ui.RangeGroupBox, 0, 3, 1, 2); m_ui.GroupBoxLayout->addWidget(m_ui.FormatGroupBox, 0, 5, 1, 1); } if (m_pAudioClip) { m_ui.TempoDetectPushButton->setEnabled(true); } else { m_ui.TempoDetectPushButton->setEnabled(false); m_ui.TempoDetectPushButton->hide(); } } qtractorClip *qtractorTempoAdjustForm::clip (void) const { return m_pClip; } qtractorAudioClip *qtractorTempoAdjustForm::audioClip (void) const { return m_pAudioClip; } // Range accessors. void qtractorTempoAdjustForm::setRangeStart ( unsigned long iRangeStart ) { ++m_iDirtySetup; const unsigned long iMinRangeStart = m_ui.RangeStartSpinBox->minimum(); const unsigned long iMaxRangeStart = m_ui.RangeStartSpinBox->maximum(); if (iRangeStart < iMinRangeStart) iRangeStart = iMinRangeStart; else if (iRangeStart > iMaxRangeStart) iRangeStart = iMaxRangeStart; m_ui.RangeStartSpinBox->setValue(iRangeStart, true); updateRangeStart(iRangeStart); --m_iDirtySetup; } unsigned long qtractorTempoAdjustForm::rangeStart (void) const { return m_ui.RangeStartSpinBox->value(); } void qtractorTempoAdjustForm::setRangeLength ( unsigned long iRangeLength ) { ++m_iDirtySetup; const unsigned long iMaxRangeLength = m_ui.RangeLengthSpinBox->maximum(); if (iRangeLength > iMaxRangeLength) iRangeLength = iMaxRangeLength; m_ui.RangeLengthSpinBox->setValue(iRangeLength, true); updateRangeLength(iRangeLength); --m_iDirtySetup; } unsigned long qtractorTempoAdjustForm::rangeLength (void) const { return m_ui.RangeLengthSpinBox->value(); } void qtractorTempoAdjustForm::setRangeBeats ( unsigned short iRangeBeats ) { ++m_iDirtySetup; const unsigned short iMinRangeBeats = m_ui.RangeBeatsSpinBox->minimum(); const unsigned short iMaxRangeBeats = m_ui.RangeBeatsSpinBox->maximum(); if (iRangeBeats < iMinRangeBeats) iRangeBeats = iMinRangeBeats; else if (iRangeBeats > iMaxRangeBeats) iRangeBeats = iMaxRangeBeats; m_ui.RangeBeatsSpinBox->setValue(iRangeBeats); updateRangeBeats(iRangeBeats); --m_iDirtySetup; } unsigned short qtractorTempoAdjustForm::rangeBeats (void) const { return m_ui.RangeBeatsSpinBox->value(); } // Accepted results accessors. float qtractorTempoAdjustForm::tempo (void) const { return m_ui.TempoSpinBox->tempo(); } unsigned short qtractorTempoAdjustForm::beatsPerBar (void) const { return m_ui.TempoSpinBox->beatsPerBar(); } unsigned short qtractorTempoAdjustForm::beatDivisor (void) const { return m_ui.TempoSpinBox->beatDivisor(); } // Time-scale accessor. qtractorTimeScale *qtractorTempoAdjustForm::timeScale (void) const { return m_pTimeScale; } // Tempo signature has changed. void qtractorTempoAdjustForm::tempoChanged (void) { if (m_iDirtySetup > 0) return; #ifdef CONFIG_DEBUG qDebug("qtractorTempoAdjustForm::tempoChanged()"); #endif m_iTempoTap = 0; m_fTempoTap = 0.0f; const float fTempo = m_ui.TempoSpinBox->tempo(); if (fTempo > 0.0f) { const float fBeatLength = 60.0f * float(m_pTimeScale->sampleRate()) / fTempo; if (fBeatLength > 0.0f) { const float fRangeLength = float(m_ui.RangeLengthSpinBox->value()); setRangeBeats(::lrintf(fRangeLength / fBeatLength)); } m_ui.TempoResetPushButton->setEnabled(true); } changed(); } // Audio clip beat-detector method . void qtractorTempoAdjustForm::tempoDetect (void) { if (m_pAudioClip == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorTempoAdjustForm::tempoDetect()"); #endif qtractorTrack *pTrack = m_pAudioClip->track(); if (pTrack == nullptr) return; qtractorAudioBus *pAudioBus = static_cast (pTrack->outputBus()); if (pAudioBus == nullptr) return; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_ui.TempoDetectPushButton->setEnabled(false); const unsigned short iChannels = pAudioBus->channels(); const unsigned int iSampleRate = m_pTimeScale->sampleRate(); const unsigned long iRangeStart = m_ui.RangeStartSpinBox->value(); const unsigned long iRangeLength = m_ui.RangeLengthSpinBox->value(); const unsigned long iOffset = iRangeStart - m_pAudioClip->clipStart(); const unsigned long iLength = iRangeLength; QProgressBar *pProgressBar = nullptr; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pProgressBar = pMainForm->progressBar(); if (pProgressBar) { pProgressBar->setRange(0, iLength / 100); pProgressBar->reset(); pProgressBar->show(); } audioClipTempoDetectData data(iChannels, iSampleRate); #ifdef CONFIG_LIBAUBIO float fTempoDetect = 0.0f; for (int n = 0; n < 5; ++n) { // 5 times at least!... m_pAudioClip->clipExport(audioClipTempoDetect, &data, iOffset, iLength); fTempoDetect = float(aubio_tempo_get_confidence(data.aubio)); if (fTempoDetect > 0.1f) break; // data.beats.clear(); data.offset += iLength; if (pProgressBar) pProgressBar->setMaximum(data.offset / 100); } if (fTempoDetect > 0.1f) { const float fTempo = aubio_tempo_get_bpm(data.aubio); m_ui.TempoSpinBox->setTempo(::rintf(fTempo), true); } if (m_pClipWidget) m_pClipWidget->setBeats(data.beats); #endif // CONFIG_LIBAUBIO #ifdef CONFIG_MINIBPM unsigned short i; const unsigned short iBeatsPerBar = m_ui.TempoSpinBox->beatsPerBar(); for (i = 0; i < iChannels; ++i) { data.minibpm[i]->setBeatsPerBar(iBeatsPerBar); } m_pAudioClip->clipExport(audioClipTempoDetect, &data, iOffset, iLength); float fTempoSum = 0.0f; for (i = 0; i < iChannels; ++i) { fTempoSum += data.minibpm[i]->estimateTempo(); } const float fTempo = fTempoSum / float(iChannels); m_ui.TempoSpinBox->setTempo(::rintf(fTempo), true); if (m_pClipWidget) { QList beats; const unsigned long iBeatLength = ::lrintf(60.0f * float(iSampleRate) / fTempo); for (unsigned long n = iBeatLength; n < iLength; n += iBeatLength) beats.append(n); m_pClipWidget->setBeats(beats); } #endif // CONFIG_MINIBPM if (pProgressBar) pProgressBar->hide(); m_ui.TempoDetectPushButton->setEnabled(true); QApplication::restoreOverrideCursor(); } // Reset to nominal tempo/time-signature. void qtractorTempoAdjustForm::tempoReset (void) { #ifdef CONFIG_DEBUG qDebug("qtractorTempoAdjustForm::tempoReset()"); #endif m_ui.TempoSpinBox->setTempo(m_pTimeScale->tempo(), false); m_ui.TempoSpinBox->setBeatsPerBar(m_pTimeScale->beatsPerBar(), false); m_ui.TempoSpinBox->setBeatDivisor(m_pTimeScale->beatDivisor(), false); m_ui.TempoResetPushButton->setEnabled(false); tempoChanged(); m_iDirtyCount = 0; stabilizeForm(); } // Adjust as instructed. void qtractorTempoAdjustForm::tempoAdjust (void) { #ifdef CONFIG_DEBUG qDebug("qtractorTempoAdjustForm::tempoAdjust()"); #endif const unsigned short iRangeBeats = m_ui.RangeBeatsSpinBox->value(); if (iRangeBeats < 1) return; const unsigned long iRangeLength = m_ui.RangeLengthSpinBox->value(); const unsigned long iBeatLength = iRangeLength / iRangeBeats; const float fTempo = 60.0f * float(m_pTimeScale->sampleRate()) / float(iBeatLength); m_ui.TempoSpinBox->setTempo(::rintf(fTempo), true); changed(); } // Tempo tap click. void qtractorTempoAdjustForm::tempoTap (void) { #ifdef CONFIG_DEBUG qDebug("qtractorTempoAdjustForm::tempoTap()"); #endif const int iTimeTap = m_pTempoTap->restart(); if (iTimeTap < 200 || iTimeTap > 2000) { // Magic! m_iTempoTap = 0; m_fTempoTap = 0.0f; return; } const float fTempoTap = ::rintf(60000.0f / float(iTimeTap)); #if 0 m_fTempoTap += fTempoTap; if (++m_iTempoTap > 2) { m_fTempoTap /= float(m_iTempoTap); m_ui.TempoSpinBox->setTempo(::rintf(m_fTempoTap), false); m_iTempoTap = 1; // Median-like averaging... } #else if (m_fTempoTap > 0.0f) { m_fTempoTap *= 0.5f; m_fTempoTap += 0.5f * fTempoTap; } else { m_fTempoTap = fTempoTap; } if (++m_iTempoTap > 2) { m_ui.TempoSpinBox->setTempo(::rintf(m_fTempoTap), false); m_iTempoTap = 1; // Median-like averaging... m_fTempoTap = fTempoTap; } #endif } // Adjust delta-value spin-boxes to new anchor frame. void qtractorTempoAdjustForm::rangeStartChanged ( unsigned long iRangeStart ) { if (m_iDirtySetup > 0) return; #ifdef CONFIG_DEBUG qDebug("qtractorTempoAdjustForm::rangeStartChanged(%lu)", iRangeStart); #endif updateRangeStart(iRangeStart); updateRangeSelect(); changed(); } void qtractorTempoAdjustForm::rangeLengthChanged ( unsigned long iRangeLength ) { if (m_iDirtySetup > 0) return; #ifdef CONFIG_DEBUG qDebug("qtractorTempoAdjustForm::rangeLengthChanged(%lu)", iRangeLength); #endif updateRangeLength(iRangeLength); updateRangeSelect(); changed(); } void qtractorTempoAdjustForm::rangeBeatsChanged ( int iRangeBeats ) { if (m_iDirtySetup > 0) return; #ifdef CONFIG_DEBUG qDebug("qtractorTempoAdjustForm::rangeBeatsChanged(%d)", iRangeBeats); #endif updateRangeBeats(iRangeBeats); changed(); } // Display format has changed. void qtractorTempoAdjustForm::formatChanged ( int iDisplayFormat ) { #ifdef CONFIG_DEBUG qDebug("qtractorTempoAdjustForm::formatChanged()"); #endif const bool bBlockSignals = m_ui.FormatComboBox->blockSignals(true); m_ui.FormatComboBox->setCurrentIndex(iDisplayFormat); const qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(iDisplayFormat); m_ui.RangeStartSpinBox->setDisplayFormat(displayFormat); m_ui.RangeLengthSpinBox->setDisplayFormat(displayFormat); if (m_pTimeScale) m_pTimeScale->setDisplayFormat(displayFormat); m_ui.FormatComboBox->blockSignals(bBlockSignals); stabilizeForm(); } // Dirty up settings. void qtractorTempoAdjustForm::changed (void) { if (m_iDirtySetup > 0) return; ++m_iDirtyCount; stabilizeForm(); } // Accept settings (OK button slot). void qtractorTempoAdjustForm::accept (void) { // Just go with dialog acceptance. QDialog::accept(); } // Reject settings (Cancel button slot). void qtractorTempoAdjustForm::reject (void) { bool bReject = true; // Check if there's any pending changes... if (m_iDirtyCount > 0) { QMessageBox::StandardButtons buttons = QMessageBox::Discard | QMessageBox::Cancel; if (m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->isEnabled()) buttons |= QMessageBox::Apply; switch (QMessageBox::warning(this, tr("Warning"), tr("Some settings have been changed.\n\n" "Do you want to apply the changes?"), buttons)) { case QMessageBox::Apply: accept(); return; case QMessageBox::Discard: break; default: // Cancel. bReject = false; } } if (bReject) QDialog::reject(); } // Adjust current range start... void qtractorTempoAdjustForm::updateRangeStart ( unsigned long iRangeStart ) { m_ui.RangeLengthSpinBox->setDeltaValue(true, iRangeStart); } // Adjust current range beat count from length... void qtractorTempoAdjustForm::updateRangeLength ( unsigned long iRangeLength ) { const int iMaxRangeBeats // Follows from max. tempo = 300bpm. = int(5.0f * float(iRangeLength) / float(m_pTimeScale->sampleRate())); m_ui.RangeBeatsSpinBox->setMaximum(iMaxRangeBeats); const float fTempo = m_ui.TempoSpinBox->tempo(); if (fTempo > 0.0f) { const float fBeatLength = 60.0f * float(m_pTimeScale->sampleRate()) / fTempo; if (fBeatLength > 0.0f) setRangeBeats(::lrintf(float(iRangeLength) / fBeatLength)); } } // Repaint the graphics... void qtractorTempoAdjustForm::updateRangeBeats ( unsigned short /*iRangeBeats*/ ) { if (m_pClipWidget) m_pClipWidget->clearBeats(); } // Adjust current selection edit/tail. void qtractorTempoAdjustForm::updateRangeSelect (void) { const unsigned long iRangeStart = m_ui.RangeStartSpinBox->value(); const unsigned long iRangeLength = m_ui.RangeLengthSpinBox->value(); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { pSession->setEditHead(iRangeStart); pSession->setEditTail(iRangeStart + iRangeLength); } qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks && m_pClip) { pTracks->clearSelect(); m_pClip->setClipSelect(iRangeStart, iRangeStart + iRangeLength); pTracks->updateSelect(); } pMainForm->selectionNotifySlot(nullptr); } if (m_pClipWidget) m_pClipWidget->refresh(); } // Stabilize current form state. void qtractorTempoAdjustForm::stabilizeForm (void) { const unsigned long iRangeLength = m_ui.RangeLengthSpinBox->value(); const unsigned short iRangeBeats = m_ui.RangeBeatsSpinBox->value(); const bool bValid = (iRangeLength > 0 && iRangeBeats > 0); m_ui.AdjustPushButton->setEnabled(bValid); m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled( bValid && m_iDirtyCount > 0); } // end of qtractorTempoAdjustForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorPluginSelectForm.cpp0000644000000000000000000000013215124701674021020 xustar0030 mtime=1767080892.799263428 30 atime=1767080892.799263428 30 ctime=1767080892.799263428 qtractor-1.5.11/src/qtractorPluginSelectForm.cpp0000644000175000001440000003102415124701674021010 0ustar00rncbcusers// qtractorPluginSelectForm.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorPluginSelectForm.h" #include "qtractorPluginFactory.h" #include "qtractorOptions.h" #include #include #include #include #include #include #include //---------------------------------------------------------------------------- // qtractorPluginSelectForm -- UI wrapper form. // Constructor. qtractorPluginSelectForm::qtractorPluginSelectForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); m_pPluginList = nullptr; // Populate plugin type hints... m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Any)); #ifdef CONFIG_LADSPA m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Ladspa)); #endif #ifdef CONFIG_DSSI m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Dssi)); #endif #ifdef CONFIG_VST2 m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Vst2)); #endif #ifdef CONFIG_VST3 m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Vst3)); #endif #ifdef CONFIG_CLAP m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Clap)); #endif #ifdef CONFIG_LV2 m_ui.PluginTypeComboBox->addItem( qtractorPluginType::textFromHint(qtractorPluginType::Lv2)); #endif QHeaderView *pHeader = m_ui.PluginListView->header(); // pHeader->setDefaultSectionSize(240); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // pHeader->setSectionResizeMode(QHeaderView::Custom); pHeader->setSectionsMovable(false); #else // pHeader->setResizeMode(QHeaderView::Custom); pHeader->setMovable(false); #endif pHeader->setStretchLastSection(true); QTreeWidgetItem *pHeaderItem = m_ui.PluginListView->headerItem(); pHeaderItem->setTextAlignment(0, Qt::AlignLeft); // Name. pHeaderItem->setTextAlignment(5, Qt::AlignLeft); // Filename. pHeaderItem->setTextAlignment(6, Qt::AlignLeft); // Index. pHeaderItem->setTextAlignment(7, Qt::AlignLeft); // Instances. pHeaderItem->setTextAlignment(8, Qt::AlignLeft); // Type. pHeader->resizeSection(0, 240); // Name. m_ui.PluginListView->resizeColumnToContents(1); // Audio. m_ui.PluginListView->resizeColumnToContents(2); // MIDI. m_ui.PluginListView->resizeColumnToContents(3); // Controls. m_ui.PluginListView->resizeColumnToContents(4); // Modes. pHeader->resizeSection(5, 120); // Path. m_ui.PluginListView->resizeColumnToContents(6); // Index m_ui.PluginListView->resizeColumnToContents(7); // Instances m_ui.PluginListView->setSortingEnabled(true); m_ui.PluginListView->sortItems(0, Qt::AscendingOrder); m_ui.PluginScanProgressBar->setMaximum(100); m_ui.PluginScanProgressBar->hide(); // Initialize conveniency options... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { pOptions->loadComboBoxHistory(m_ui.PluginSearchComboBox); m_ui.PluginSearchComboBox->setEditText( pOptions->sPluginSearch); m_ui.PluginTypeComboBox->setCurrentIndex(pOptions->iPluginType); } // Let the search begin... m_ui.PluginSearchComboBox->setFocus(); adjustSize(); // Restore last seen dialog position and extent... if (pOptions) pOptions->loadWidgetGeometry(this, true); // UI signal/slot connections... #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) QObject::connect(m_ui.PluginResetToolButton, SIGNAL(clicked()), SLOT(reset())); #else m_ui.PluginResetToolButton->hide(); // Some conveniency cleaner helper... m_ui.PluginSearchComboBox->lineEdit()->setClearButtonEnabled(true); #endif QObject::connect(m_ui.PluginSearchComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(refresh())); QObject::connect(m_ui.PluginTypeComboBox, SIGNAL(activated(int)), SLOT(typeHintChanged(int))); QObject::connect(m_ui.PluginListView, SIGNAL(itemSelectionChanged()), SLOT(stabilize())); // QObject::connect(m_ui.PluginListView, // SIGNAL(itemActivated(QTreeWidgetItem *, int)), // SLOT(accept())); QObject::connect(m_ui.PluginListView, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), SLOT(accept())); QObject::connect(m_ui.PluginRescanPushButton, SIGNAL(clicked()), SLOT(rescan())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); qtractorPluginFactory *pPluginFactory = qtractorPluginFactory::getInstance(); if (pPluginFactory) { QObject::connect(pPluginFactory, SIGNAL(scanned(int)), SLOT(scanned(int))); } typeHintChanged(m_ui.PluginTypeComboBox->currentIndex()); } // Destructor. qtractorPluginSelectForm::~qtractorPluginSelectForm (void) { // Save other conveniency options, if convenient thought... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { pOptions->iPluginType = m_ui.PluginTypeComboBox->currentIndex(); pOptions->sPluginSearch = m_ui.PluginSearchComboBox->currentText(); pOptions->saveComboBoxHistory(m_ui.PluginSearchComboBox); // Save aslast seen dialog position and extent... pOptions->saveWidgetGeometry(this, true); } } // Base number of channels accessors. void qtractorPluginSelectForm::setPluginList ( qtractorPluginList *pPluginList ) { m_pPluginList = pPluginList; refresh(); } qtractorPluginList *qtractorPluginSelectForm::pluginList (void) const { return m_pPluginList; } // Final selection accessors.. int qtractorPluginSelectForm::pluginCount (void) const { return m_selectedItems.count(); } QString qtractorPluginSelectForm::pluginFilename ( int iPlugin ) const { return m_selectedItems.at(iPlugin)->text(5); } unsigned long qtractorPluginSelectForm::pluginIndex ( int iPlugin ) const { return m_selectedItems.at(iPlugin)->text(6).toULong(); } qtractorPluginType::Hint qtractorPluginSelectForm::pluginTypeHint ( int iPlugin ) const { const QString& sText = m_selectedItems.at(iPlugin)->text(8); return qtractorPluginType::hintFromText(sText); } // Plugin type hint change slot. void qtractorPluginSelectForm::typeHintChanged ( int iTypeHint ) { qtractorPluginType::Hint typeHint = qtractorPluginType::hintFromText( m_ui.PluginTypeComboBox->itemText(iTypeHint)); #if 0 // Rescan not applicable to LV2 plug-in world. m_ui.PluginRescanPushButton->setVisible( typeHint != qtractorPluginType::Lv2); #endif qtractorPluginFactory *pPluginFactory = qtractorPluginFactory::getInstance(); if (pPluginFactory && pPluginFactory->typeHint() != typeHint) { pPluginFactory->setTypeHint(typeHint); pPluginFactory->clear(); } refresh(); } // Reset plugin listing. void qtractorPluginSelectForm::reset (void) { m_ui.PluginSearchComboBox->lineEdit()->clear(); // refresh(); } // Rescan plugin listing. void qtractorPluginSelectForm::rescan (void) { qtractorPluginFactory *pPluginFactory = qtractorPluginFactory::getInstance(); if (pPluginFactory) { const int iTypeHint = m_ui.PluginTypeComboBox->currentIndex(); qtractorPluginType::Hint typeHint = qtractorPluginType::hintFromText( m_ui.PluginTypeComboBox->itemText(iTypeHint)); if (QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) pPluginFactory->clearAll(typeHint); else pPluginFactory->clear(); pPluginFactory->setRescan(true); } refresh(); } // Refresh plugin listing. void qtractorPluginSelectForm::refresh (void) { m_ui.PluginListView->clear(); qtractorPluginFactory *pPluginFactory = qtractorPluginFactory::getInstance(); if (pPluginFactory == nullptr) return; // FIXME: Should this be a global (singleton) registry? if (pPluginFactory->types().isEmpty()) { // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_ui.PluginRescanPushButton->setEnabled(false); m_ui.DialogButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); m_ui.PluginScanProgressBar->show(); pPluginFactory->scan(); m_ui.PluginScanProgressBar->hide(); m_ui.DialogButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(true); m_ui.PluginRescanPushButton->setEnabled(true); // We're formerly done. QApplication::restoreOverrideCursor(); } if (m_pPluginList == nullptr) { stabilize(); return; } const unsigned short iChannels = m_pPluginList->channels(); const bool bMidi = m_pPluginList->isMidi(); QString sSearch = m_ui.PluginSearchComboBox->currentText().simplified(); const QRegularExpression rx(sSearch.replace( QRegularExpression("[\\s]+"), ".*"), QRegularExpression::CaseInsensitiveOption); QStringList cols; QList items; QListIterator type_iter(pPluginFactory->types()); while (type_iter.hasNext()) { qtractorPluginType *pType = type_iter.next(); const QString& sFilename = pType->filename(); const QString& sName = pType->name(); if (rx.pattern().isEmpty() || rx.match(sName).hasMatch() || rx.match(sFilename).hasMatch()) { // Try primary instantiation... const int iAudioIns = pType->audioIns(); const int iAudioOuts = pType->audioOuts(); const int iMidiIns = pType->midiIns(); const int iMidiOuts = pType->midiOuts(); const int iControlIns = pType->controlIns(); const int iControlOuts = pType->controlOuts(); // All that to check whether it will get properly instantiated. const unsigned short iInstances = pType->instances(iChannels, bMidi); cols.clear(); cols << sName; cols << QString("%1:%2").arg(iAudioIns).arg(iAudioOuts); cols << QString("%1:%2").arg(iMidiIns).arg(iMidiOuts); cols << QString("%1:%2").arg(iControlIns).arg(iControlOuts); QStringList modes; if (pType->isEditor()) modes << tr("GUI"); if (pType->isConfigure()) modes << tr("EXT"); if (pType->isRealtime()) modes << tr("RT"); if (modes.isEmpty()) cols << "-"; else cols << modes.join(","); cols << sFilename; cols << QString::number(pType->index()); cols << QString::number(iInstances); cols << qtractorPluginType::textFromHint(pType->typeHint()); QTreeWidgetItem *pItem = new QTreeWidgetItem(cols); if (iInstances < 1) { pItem->setFlags(pItem->flags() & ~Qt::ItemIsSelectable); const int iColumnCount = m_ui.PluginListView->columnCount(); const QPalette& pal = m_ui.PluginListView->palette(); const QColor& rgbForeground = pal.color(QPalette::Disabled, QPalette::WindowText); for (int i = 0; i < iColumnCount; ++i) pItem->setForeground(i, rgbForeground); } pItem->setTextAlignment(1, Qt::AlignHCenter); // Audio pItem->setTextAlignment(2, Qt::AlignHCenter); // MIDI pItem->setTextAlignment(3, Qt::AlignHCenter); // Controls pItem->setTextAlignment(4, Qt::AlignHCenter); // Modes items.append(pItem); } } m_ui.PluginListView->addTopLevelItems(items); QHeaderView *pHeader = m_ui.PluginListView->header(); m_ui.PluginListView->sortItems( pHeader->sortIndicatorSection(), pHeader->sortIndicatorOrder()); m_ui.PluginResetToolButton->setEnabled(!rx.pattern().isEmpty()); stabilize(); } // Refresh plugin scan progress. void qtractorPluginSelectForm::scanned ( int iPercent ) { m_ui.PluginScanProgressBar->setValue(iPercent); } // Stabilize slot. void qtractorPluginSelectForm::stabilize (void) { m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled( !m_ui.PluginListView->selectedItems().isEmpty()); } // Accept slot. void qtractorPluginSelectForm::accept (void) { // Are we done? m_selectedItems = m_ui.PluginListView->selectedItems(); if (!m_selectedItems.isEmpty()) QDialog::accept(); } // end of qtractorPluginSelectForm.cpp qtractor-1.5.11/src/PaxHeaders/qtractorLv2Plugin.cpp0000644000000000000000000000013215124701674017420 xustar0030 mtime=1767080892.787263479 30 atime=1767080892.786263483 30 ctime=1767080892.787263479 qtractor-1.5.11/src/qtractorLv2Plugin.cpp0000644000175000001440000050710615124701674017421 0ustar00rncbcusers// qtractorLv2Plugin.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #ifdef CONFIG_LV2 #include "qtractorLv2Plugin.h" #include "qtractorPluginFactory.h" #include "qtractorPluginCommand.h" #include "qtractorSession.h" #include "qtractorAudioEngine.h" #include "qtractorMidiManager.h" #include "qtractorOptions.h" #include "qtractorMainForm.h" #include #include #ifdef CONFIG_LV2_STATE // LV2 State/Presets: standard directory access. // For local file vs. URI manipulations. #include #include #include #endif #include #include #ifndef INT32_MAX #define INT32_MAX 2147483647 #endif // URI map/unmap features. #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/urid/urid.h" #else #include "lv2//urid/urid.h" #endif static QHash g_uri_map; static QHash g_ids_map; static LV2_URID qtractor_lv2_urid_map ( LV2_URID_Map_Handle /*handle*/, const char *uri ) { if (::strcmp(uri, LILV_URI_MIDI_EVENT) == 0) return QTRACTOR_LV2_MIDI_EVENT_ID; else return qtractorLv2Plugin::lv2_urid_map(uri); } static LV2_URID_Map g_lv2_urid_map = { &g_lv2_urid_map, qtractor_lv2_urid_map }; static const LV2_Feature g_lv2_urid_map_feature = { LV2_URID_MAP_URI, &g_lv2_urid_map }; static const char *qtractor_lv2_urid_unmap ( LV2_URID_Unmap_Handle /*handle*/, LV2_URID id ) { if (id == QTRACTOR_LV2_MIDI_EVENT_ID) return LILV_URI_MIDI_EVENT; else return qtractorLv2Plugin::lv2_urid_unmap(id); } static LV2_URID_Unmap g_lv2_urid_unmap = { nullptr, qtractor_lv2_urid_unmap }; static const LV2_Feature g_lv2_urid_unmap_feature = { LV2_URID_UNMAP_URI, &g_lv2_urid_unmap }; #ifdef CONFIG_LV2_EVENT // URI map (uri_to_id) feature (DEPRECATED) #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h" #else #include "lv2/uri-map/uri-map.h" #endif static LV2_URID qtractor_lv2_uri_to_id ( LV2_URI_Map_Callback_Data /*data*/, const char *map, const char *uri ) { if ((map && strcmp(map, LV2_EVENT_URI) == 0) && strcmp(uri, LILV_URI_MIDI_EVENT) == 0) return QTRACTOR_LV2_MIDI_EVENT_ID; else return qtractorLv2Plugin::lv2_urid_map(uri); } static LV2_URI_Map_Feature g_lv2_uri_map = { nullptr, qtractor_lv2_uri_to_id }; static const LV2_Feature g_lv2_uri_map_feature = { LV2_URI_MAP_URI, &g_lv2_uri_map }; #endif // CONFIG_LV2_EVENT #ifdef CONFIG_LV2_STATE #define QTRACTOR_LV2_STATE_PREFIX "urn:qtractor:state" #define QTRACTOR_LV2_STATE_KEY QTRACTOR_LV2_STATE_PREFIX "#key" #define QTRACTOR_LV2_STATE_TYPE QTRACTOR_LV2_STATE_PREFIX "#type" #ifndef LV2_STATE__StateChanged #define LV2_STATE__StateChanged LV2_STATE_PREFIX "StateChanged" #endif static const LV2_Feature g_lv2_state_feature = { LV2_STATE_URI, nullptr }; static const void *qtractor_lv2_state_retrieve ( LV2_State_Handle handle, uint32_t key, size_t *size, uint32_t *type, uint32_t *flags ) { qtractorLv2Plugin *pLv2Plugin = static_cast (handle); if (pLv2Plugin == nullptr) return nullptr; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_state_retrieve(%p, %d)", pLv2Plugin, int(key)); #endif return pLv2Plugin->lv2_state_retrieve(key, size, type, flags); } #endif // CONFIG_LV2_STATE // URI map helpers (static). LV2_URID qtractorLv2Plugin::lv2_urid_map ( const char *uri ) { const QString sUri(uri); QHash::ConstIterator iter = g_uri_map.constFind(sUri); if (iter == g_uri_map.constEnd()) { LV2_URID id = g_uri_map.size() + 1000; g_uri_map.insert(sUri, id); g_ids_map.insert(id, sUri.toUtf8()); return id; } return iter.value(); } const char *qtractorLv2Plugin::lv2_urid_unmap ( LV2_URID id ) { QHash::ConstIterator iter = g_ids_map.constFind(id); if (iter == g_ids_map.constEnd()) return nullptr; return iter.value().constData(); } #ifdef CONFIG_LV2_WORKER // LV2 Worker/Schedule support. #include #include #include #include //---------------------------------------------------------------------- // class qtractorLv2Worker -- LV2 Worker/Schedule item decl. // class qtractorLv2WorkerThread; class qtractorLv2Worker { public: // Constructor. qtractorLv2Worker(qtractorLv2Plugin *pLv2Plugin, const LV2_Feature *const *features); // Destructor. ~qtractorLv2Worker(); // Instance copy of worker schedule feature. LV2_Feature **lv2_features() const { return m_lv2_features; } // Schedule work. void schedule(uint32_t size, const void *data); // Respond work. void respond(uint32_t size, const void *data); // Commit work. void commit(); // Process work. void process(); private: // Instance members. qtractorLv2Plugin *m_pLv2Plugin; LV2_Feature **m_lv2_features; LV2_Feature m_lv2_schedule_feature; LV2_Worker_Schedule m_lv2_schedule; jack_ringbuffer_t *m_pRequests; jack_ringbuffer_t *m_pResponses; void *m_pResponse; static qtractorLv2WorkerThread *g_pWorkerThread; static unsigned int g_iWorkerRefCount; }; static LV2_Worker_Status qtractor_lv2_worker_schedule ( LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data ) { qtractorLv2Worker *pLv2Worker = static_cast (handle); if (pLv2Worker == nullptr) return LV2_WORKER_ERR_UNKNOWN; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_lv2_worker_schedule(%p, %u, %p)", pLv2Worker, size, data); #endif pLv2Worker->schedule(size, data); return LV2_WORKER_SUCCESS; } static LV2_Worker_Status qtractor_lv2_worker_respond ( LV2_Worker_Respond_Handle handle, uint32_t size, const void *data ) { qtractorLv2Worker *pLv2Worker = static_cast (handle); if (pLv2Worker == nullptr) return LV2_WORKER_ERR_UNKNOWN; #ifdef CONFIG_DEBUG_0 qDebug("qtractor_lv2_worker_respond(%p, %u, %p)", pLv2Worker, size, data); #endif pLv2Worker->respond(size, data); return LV2_WORKER_SUCCESS; } //---------------------------------------------------------------------- // class qtractorLv2WorkerThread -- LV2 Worker/Schedule thread. // class qtractorLv2WorkerThread : public QThread { public: // Constructor. qtractorLv2WorkerThread(unsigned int iSyncSize = 128); // Destructor. ~qtractorLv2WorkerThread(); // Thread run state accessors. void setRunState(bool bRunState); bool runState() const; // Wake from executive wait condition. void sync(qtractorLv2Worker *pLv2Worker = nullptr); protected: // The main thread executive. void run(); private: // The peak file queue instance reference. unsigned int m_iSyncSize; unsigned int m_iSyncMask; qtractorLv2Worker **m_ppSyncItems; volatile unsigned int m_iSyncRead; volatile unsigned int m_iSyncWrite; // Whether the thread is logically running. volatile bool m_bRunState; // Thread synchronization objects. QMutex m_mutex; QWaitCondition m_cond; }; // Constructor. qtractorLv2WorkerThread::qtractorLv2WorkerThread ( unsigned int iSyncSize ) { m_iSyncSize = (64 << 1); while (m_iSyncSize < iSyncSize) m_iSyncSize <<= 1; m_iSyncMask = (m_iSyncSize - 1); m_ppSyncItems = new qtractorLv2Worker * [m_iSyncSize]; m_iSyncRead = 0; m_iSyncWrite = 0; ::memset(m_ppSyncItems, 0, m_iSyncSize * sizeof(qtractorLv2Worker *)); m_bRunState = false; } // Destructor. qtractorLv2WorkerThread::~qtractorLv2WorkerThread (void) { delete [] m_ppSyncItems; } // Run state accessor. void qtractorLv2WorkerThread::setRunState ( bool bRunState ) { QMutexLocker locker(&m_mutex); m_bRunState = bRunState; } bool qtractorLv2WorkerThread::runState (void) const { return m_bRunState; } // Wake from executive wait condition. void qtractorLv2WorkerThread::sync ( qtractorLv2Worker *pLv2Worker ) { if (pLv2Worker) { unsigned int n; unsigned int r = m_iSyncRead; unsigned int w = m_iSyncWrite; if (w > r) { n = ((r - w + m_iSyncSize) & m_iSyncMask) - 1; } else if (r > w) { n = (r - w) - 1; } else { n = m_iSyncSize - 1; } if (n > 0) { m_ppSyncItems[w] = pLv2Worker; m_iSyncWrite = (w + 1) & m_iSyncMask; } } if (m_mutex.tryLock()) { m_cond.wakeAll(); m_mutex.unlock(); } #ifdef CONFIG_DEBUG_0 else qDebug("qtractorLv2WorkerThread[%p]::sync(): tryLock() failed.", this); #endif } // The main thread executive cycle. void qtractorLv2WorkerThread::run (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2WorkerThread[%p]::run(): started...", this); #endif m_mutex.lock(); m_bRunState = true; while (m_bRunState) { // Do whatever we must, then wait for more... unsigned int r = m_iSyncRead; unsigned int w = m_iSyncWrite; while (r != w) { m_ppSyncItems[r]->process(); ++r &= m_iSyncMask; w = m_iSyncWrite; } m_iSyncRead = r; // Wait for sync... m_cond.wait(&m_mutex); } m_mutex.unlock(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2WorkerThread[%p]::run(): stopped.\n", this); #endif } //---------------------------------------------------------------------- // class qtractorLv2Worker -- LV2 Worker/Schedule item impl. // qtractorLv2WorkerThread *qtractorLv2Worker::g_pWorkerThread = nullptr; unsigned int qtractorLv2Worker::g_iWorkerRefCount = 0; // Constructor. qtractorLv2Worker::qtractorLv2Worker ( qtractorLv2Plugin *pLv2Plugin, const LV2_Feature *const *features ) { m_pLv2Plugin = pLv2Plugin; int iFeatures = 0; while (features && features[iFeatures]) { ++iFeatures; } m_lv2_features = new LV2_Feature * [iFeatures + 2]; for (int i = 0; i < iFeatures; ++i) m_lv2_features[i] = (LV2_Feature *) features[i]; m_lv2_schedule.handle = this; m_lv2_schedule.schedule_work = &qtractor_lv2_worker_schedule; m_lv2_schedule_feature.URI = LV2_WORKER__schedule; m_lv2_schedule_feature.data = &m_lv2_schedule; m_lv2_features[iFeatures++] = &m_lv2_schedule_feature; m_lv2_features[iFeatures] = nullptr; m_pRequests = ::jack_ringbuffer_create(4096); m_pResponses = ::jack_ringbuffer_create(4096); m_pResponse = (void *) ::malloc(4096); if (++g_iWorkerRefCount == 1) { g_pWorkerThread = new qtractorLv2WorkerThread(); g_pWorkerThread->start(); } } // Destructor. qtractorLv2Worker::~qtractorLv2Worker (void) { if (--g_iWorkerRefCount == 0) { if (g_pWorkerThread->isRunning()) do { g_pWorkerThread->setRunState(false); // g_pWorkerThread->terminate(); g_pWorkerThread->sync(); } while (!g_pWorkerThread->wait(100)); delete g_pWorkerThread; g_pWorkerThread = nullptr; } ::jack_ringbuffer_free(m_pRequests); ::jack_ringbuffer_free(m_pResponses); ::free(m_pResponse); delete [] m_lv2_features; } // Schedule work. void qtractorLv2Worker::schedule ( uint32_t size, const void *data ) { const uint32_t request_size = size + sizeof(size); if (::jack_ringbuffer_write_space(m_pRequests) >= request_size) { char request_data[request_size]; ::memcpy(request_data, &size, sizeof(size)); ::memcpy(request_data + sizeof(size), data, size); ::jack_ringbuffer_write(m_pRequests, (const char *) &request_data, request_size); } if (g_pWorkerThread) g_pWorkerThread->sync(this); } // Response work. void qtractorLv2Worker::respond ( uint32_t size, const void *data ) { const uint32_t response_size = size + sizeof(size); if (::jack_ringbuffer_write_space(m_pResponses) >= response_size) { char response_data[response_size]; ::memcpy(response_data, &size, sizeof(size)); ::memcpy(response_data + sizeof(size), data, size); ::jack_ringbuffer_write(m_pResponses, (const char *) &response_data, response_size); } } // Commit work. void qtractorLv2Worker::commit (void) { const LV2_Worker_Interface *worker = m_pLv2Plugin->lv2_worker_interface(0); if (worker == nullptr) return; const unsigned short iInstances = m_pLv2Plugin->instances(); unsigned short i; uint32_t read_space = ::jack_ringbuffer_read_space(m_pResponses); while (read_space > 0) { uint32_t size = 0; ::jack_ringbuffer_read(m_pResponses, (char *) &size, sizeof(size)); ::jack_ringbuffer_read(m_pResponses, (char *) m_pResponse, size); if (worker->work_response) { for (i = 0; i < iInstances; ++i) { LV2_Handle handle = m_pLv2Plugin->lv2_handle(i); if (handle) (*worker->work_response)(handle, size, m_pResponse); } } read_space -= sizeof(size) + size; } if (worker->end_run) { for (i = 0; i < iInstances; ++i) { LV2_Handle handle = m_pLv2Plugin->lv2_handle(i); if (handle) (*worker->end_run)(handle); } } } // Process work. void qtractorLv2Worker::process (void) { const LV2_Worker_Interface *worker = m_pLv2Plugin->lv2_worker_interface(0); if (worker == nullptr) return; const unsigned short iInstances = m_pLv2Plugin->instances(); unsigned short i; void *buf = nullptr; uint32_t size = 0; uint32_t read_space = ::jack_ringbuffer_read_space(m_pRequests); if (read_space > 0) buf = ::malloc(read_space); while (read_space > 0) { ::jack_ringbuffer_read(m_pRequests, (char *) &size, sizeof(size)); ::jack_ringbuffer_read(m_pRequests, (char *) buf, size); if (worker->work) { for (i = 0; i < iInstances; ++i) { LV2_Handle handle = m_pLv2Plugin->lv2_handle(i); if (handle) (*worker->work)(handle, qtractor_lv2_worker_respond, this, size, buf); } } read_space -= sizeof(size) + size; } if (buf) ::free(buf); } #endif // CONFIG_LV2_WORKER #ifdef CONFIG_LV2_STATE_FILES #include "qtractorDocument.h" static char *qtractor_lv2_state_abstract_path ( LV2_State_Map_Path_Handle handle, const char *absolute_path ) { qtractorLv2Plugin *pLv2Plugin = static_cast (handle); if (pLv2Plugin == nullptr) return nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return nullptr; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_state_abstract_path(%p, \"%s\")", pLv2Plugin, absolute_path); #endif // abstract_path from absolute_path... QString sDir = pLv2Plugin->lv2_state_save_dir(); const bool bSessionDir = sDir.isEmpty(); if (bSessionDir) sDir = pSession->sessionDir(); const QFileInfo fi(absolute_path); const QString& sAbsolutePath = fi.absoluteFilePath(); QString sAbstractPath = QDir(sDir).relativeFilePath(sAbsolutePath); if (bSessionDir) sAbstractPath = qtractorDocument::addFile(sDir, sAbstractPath); return ::strdup(sAbstractPath.toUtf8().constData()); } static char *qtractor_lv2_state_absolute_path ( LV2_State_Map_Path_Handle handle, const char *abstract_path ) { qtractorLv2Plugin *pLv2Plugin = static_cast (handle); if (pLv2Plugin == nullptr) return nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return nullptr; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_state_absolute_path(%p, \"%s\")", pLv2Plugin, abstract_path); #endif // absolute_path from abstract_path... QString sDir = pLv2Plugin->lv2_state_save_dir(); const bool bSessionDir = sDir.isEmpty(); if (bSessionDir) sDir = pSession->sessionDir(); QFileInfo fi(abstract_path); if (fi.isRelative()) fi.setFile(QDir(sDir), fi.filePath()); const QString& sAbsolutePath = fi.absoluteFilePath(); return ::strdup(sAbsolutePath.toUtf8().constData()); } #ifdef CONFIG_LV2_STATE_MAKE_PATH static char *qtractor_lv2_state_make_path ( LV2_State_Make_Path_Handle handle, const char *relative_path ) { qtractorLv2Plugin *pLv2Plugin = static_cast (handle); if (pLv2Plugin == nullptr) return nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return nullptr; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_state_make_path(%p, \"%s\")", pLv2Plugin, relative_path); #endif // make_path from relative_path... QString sDir = pLv2Plugin->lv2_state_save_dir(); if (sDir.isEmpty()) { sDir = pSession->sessionDir(); sDir += QDir::separator(); #if 0 const QString& sSessionName = pSession->sessionName(); if (!sSessionName.isEmpty()) { sDir += qtractorSession::sanitize(sSessionName); sDir += '-'; } const QString& sListName = pLv2Plugin->list()->name(); if (!sListName.isEmpty()) { sDir += qtractorSession::sanitize(sListName); sDir += '-'; } #endif sDir += pLv2Plugin->type()->label(); sDir += '-'; sDir += QString::number(pLv2Plugin->uniqueID(), 16); } QDir dir; int i = 0; const QString sPath = sDir + "-%1"; do dir.setPath(sPath.arg(++i)); while (dir.exists()); QFileInfo fi(relative_path); if (fi.isRelative()) fi.setFile(dir, fi.filePath()); if (fi.isSymLink()) fi.setFile(fi.symLinkTarget()); const QString& sMakeDir = fi.absolutePath(); if (!QDir(sMakeDir).exists()) dir.mkpath(sMakeDir); const QString& sMakePath = fi.absoluteFilePath(); return ::strdup(sMakePath.toUtf8().constData()); } #endif // CONFIG_LV2_STATE_MAKE_PATH #ifdef CONFIG_LV2_STATE_FREE_PATH static void qtractor_lv2_state_free_path ( LV2_State_Make_Path_Handle handle, char *path ) { qtractorLv2Plugin *pLv2Plugin = static_cast (handle); if (pLv2Plugin == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_state_free_path(%p, \"%s\")", pLv2Plugin, path); #endif ::free((void *) path); } #endif // CONFIG_LV2_STATE_FREE_PATH #endif // CONFIG_LV2_STATE_FILES #ifdef CONFIG_LV2_BUF_SIZE // LV2 Buffer size option. #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h" #else #include "lv2/buf-size/buf-size.h" #endif #ifndef LV2_BUF_SIZE__nominalBlockLength #define LV2_BUF_SIZE__nominalBlockLength LV2_BUF_SIZE_PREFIX "nominalBlockLength" #endif static const LV2_Feature g_lv2_buf_size_fixed_feature = { LV2_BUF_SIZE__fixedBlockLength, nullptr }; static const LV2_Feature g_lv2_buf_size_bounded_feature = { LV2_BUF_SIZE__boundedBlockLength, nullptr }; #endif // CONFIG_LV2_BUF_SIZE static const LV2_Feature *g_lv2_features[] = { &g_lv2_urid_map_feature, &g_lv2_urid_unmap_feature, #ifdef CONFIG_LV2_EVENT &g_lv2_uri_map_feature, // deprecated. #endif #ifdef CONFIG_LV2_STATE &g_lv2_state_feature, #endif #ifdef CONFIG_LV2_BUF_SIZE &g_lv2_buf_size_fixed_feature, &g_lv2_buf_size_bounded_feature, #endif nullptr }; #ifdef CONFIG_LV2_UI #include "qtractorMessageBox.h" #include #include #include #include #define LV2_UI_TYPE_NONE 0 #define LV2_UI_TYPE_QT4 1 #define LV2_UI_TYPE_QT5 2 #define LV2_UI_TYPE_GTK 3 #define LV2_UI_TYPE_X11 4 #define LV2_UI_TYPE_EXTERNAL 5 #define LV2_UI_TYPE_OTHER 6 // LV2 Plug-in UI native flag mask. #define LV2_UI_TYPE_NATIVE 1000 #define LV2_UI_TYPE_GTK_NATIVE LV2_UI_TYPE_GTK + LV2_UI_TYPE_NATIVE #define LV2_UI_TYPE_X11_NATIVE LV2_UI_TYPE_X11 + LV2_UI_TYPE_NATIVE #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #include #endif #ifndef LV2_UI__Qt5UI #define LV2_UI__Qt5UI LV2_UI_PREFIX "Qt5UI" #endif #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #define LV2_UI_HOST_URI LV2_UI__Qt4UI #else #define LV2_UI_HOST_URI LV2_UI__Qt5UI #endif #ifndef LV2_UI__windowTitle #define LV2_UI__windowTitle LV2_UI_PREFIX "windowTitle" #endif #ifndef LV2_UI__updateRate #define LV2_UI__updateRate LV2_UI_PREFIX "updateRate" #endif #ifndef LV2_UI__noUserResize #define LV2_UI__noUserResize LV2_UI_PREFIX "noUserResize" #endif static void qtractor_lv2_ui_port_write ( LV2UI_Controller ui_controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, const void *buffer ) { qtractorLv2Plugin *pLv2Plugin = static_cast (ui_controller); if (pLv2Plugin == nullptr) return; pLv2Plugin->lv2_ui_port_write(port_index, buffer_size, protocol, buffer); } static uint32_t qtractor_lv2_ui_port_index ( LV2UI_Controller ui_controller, const char *port_symbol ) { qtractorLv2Plugin *pLv2Plugin = static_cast (ui_controller); if (pLv2Plugin == nullptr) return LV2UI_INVALID_PORT_INDEX; return pLv2Plugin->lv2_ui_port_index(port_symbol); } #ifdef CONFIG_LV2_EXTERNAL_UI static void qtractor_lv2_ui_closed ( LV2UI_Controller ui_controller ) { qtractorLv2Plugin *pLv2Plugin = static_cast (ui_controller); if (pLv2Plugin == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_ui_closed(%p)", pLv2Plugin); #endif // Just flag up the closure... pLv2Plugin->setEditorClosed(true); } #endif // CONFIG_LV2_EXTERNAL_UI #ifdef CONFIG_LV2_UI_TOUCH static void qtractor_lv2_ui_touch ( LV2UI_Feature_Handle handle, uint32_t port_index, bool grabbed ) { qtractorLv2Plugin *pLv2Plugin = static_cast (handle); if (pLv2Plugin == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_ui_touch(%p, %u, %d)", pLv2Plugin, port_index, int(grabbed)); #endif // Just flag up the closure... pLv2Plugin->lv2_ui_touch(port_index, grabbed); } #endif // CONFIG_LV2_UI_TOUCH #ifdef CONFIG_LV2_UI_REQ_VALUE #include static LV2UI_Request_Value_Status qtractor_lv2_ui_request_value ( LV2UI_Feature_Handle handle, LV2_URID key, LV2_URID type, const LV2_Feature *const *features ) { qtractorLv2Plugin *pLv2Plugin = static_cast (handle); if (pLv2Plugin == nullptr) return LV2UI_REQUEST_VALUE_ERR_UNKNOWN; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_ui_request_value(%p, %d, %d)", pLv2Plugin, int(key), int(type)); #endif return pLv2Plugin->lv2_ui_request_value(key, type, features); } #endif // CONFIG_LV2_UI_REQ_VALUE #ifdef CONFIG_LV2_PORT_CHANGE_REQUEST static LV2_ControlInputPort_Change_Status qtractor_lv2_port_change_request ( LV2_ControlInputPort_Change_Request_Handle handle, uint32_t port_index, float port_value ) { qtractorLv2Plugin *pLv2Plugin = static_cast (handle); if (pLv2Plugin == nullptr) return LV2_CONTROL_INPUT_PORT_CHANGE_ERR_UNKNOWN; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_port_change_request(%p, %u, %g)", pLv2Plugin, port_index, port_value); #endif return pLv2Plugin->lv2_port_change_request(port_index, port_value); } #endif // CONFIG_LV2_PORT_CHANGE_REQUEST #include class qtractorLv2Plugin::EventFilter : public QObject { public: // Constructor. EventFilter(qtractorLv2Plugin *pLv2Plugin, QWidget *pQtWidget) : QObject(), m_pLv2Plugin(pLv2Plugin), m_pQtWidget(pQtWidget) { m_pQtWidget->installEventFilter(this); } bool eventFilter(QObject *pObject, QEvent *pEvent) { if (pObject == static_cast (m_pQtWidget)) { switch (pEvent->type()) { case QEvent::Close: { // Defer widget close! m_pQtWidget->removeEventFilter(this); m_pQtWidget = nullptr; m_pLv2Plugin->closeEditorEx(); pEvent->ignore(); return true; } case QEvent::Resize: { // LV2 UI resize control... QResizeEvent *pResizeEvent = static_cast (pEvent); if (pResizeEvent) m_pLv2Plugin->lv2_ui_resize(pResizeEvent->size()); break; } default: break; } } return QObject::eventFilter(pObject, pEvent); } private: // Instance variables. qtractorLv2Plugin *m_pLv2Plugin; QWidget *m_pQtWidget; }; #endif // CONFIG_LV2_UI // LV2 World stuff (ref. counted). static LilvWorld *g_lv2_world = nullptr; static LilvPlugins *g_lv2_plugins = nullptr; // Supported port classes. static LilvNode *g_lv2_input_class = nullptr; static LilvNode *g_lv2_output_class = nullptr; static LilvNode *g_lv2_control_class = nullptr; static LilvNode *g_lv2_audio_class = nullptr; static LilvNode *g_lv2_midi_class = nullptr; #ifdef CONFIG_LV2_EVENT static LilvNode *g_lv2_event_class = nullptr; #endif #ifdef CONFIG_LV2_ATOM static LilvNode *g_lv2_atom_class = nullptr; #endif #ifdef CONFIG_LV2_CVPORT static LilvNode *g_lv2_cvport_class = nullptr; #endif #ifdef CONFIG_LV2_UI #ifdef CONFIG_LV2_EXTERNAL_UI static LilvNode *g_lv2_external_ui_class = nullptr; #ifdef LV2_EXTERNAL_UI_DEPRECATED_URI static LilvNode *g_lv2_external_ui_deprecated_class = nullptr; #endif #endif static LilvNode *g_lv2_x11_ui_class = nullptr; static LilvNode *g_lv2_gtk_ui_class = nullptr; static LilvNode *g_lv2_qt4_ui_class = nullptr; static LilvNode *g_lv2_qt5_ui_class = nullptr; #endif // CONFIG_LV2_UI // Supported plugin features. static LilvNode *g_lv2_realtime_hint = nullptr; static LilvNode *g_lv2_extension_data_hint = nullptr; #ifdef CONFIG_LV2_WORKER static LilvNode *g_lv2_worker_schedule_hint = nullptr; #endif #ifdef CONFIG_LV2_STATE static LilvNode *g_lv2_state_interface_hint = nullptr; static LilvNode *g_lv2_state_load_default_hint = nullptr; #endif // Supported port properties (hints). static LilvNode *g_lv2_toggled_prop = nullptr; static LilvNode *g_lv2_integer_prop = nullptr; static LilvNode *g_lv2_sample_rate_prop = nullptr; #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" #else #include "lv2/port-props/port-props.h" #endif static LilvNode *g_lv2_logarithmic_prop = nullptr; #ifdef CONFIG_LV2_ATOM #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/atom/forge.h" #include "lv2/lv2plug.in/ns/ext/atom/util.h" #else #include "lv2/atom/forge.h" #include "lv2/atom/util.h" #endif static LV2_Atom_Forge *g_lv2_atom_forge = nullptr; #ifndef CONFIG_LV2_ATOM_FORGE_OBJECT #define lv2_atom_forge_object(forge, frame, id, otype) \ lv2_atom_forge_blank(forge, frame, id, otype) #endif #ifndef CONFIG_LV2_ATOM_FORGE_KEY #define lv2_atom_forge_key(forge, key) \ lv2_atom_forge_property_head(forge, key, 0) #endif #ifndef LV2_ATOM__PortEvent #define LV2_ATOM__PortEvent LV2_ATOM_PREFIX "PortEvent" #endif #ifndef LV2_ATOM__portTuple #define LV2_ATOM__portTuple LV2_ATOM_PREFIX "portTuple" #endif static LilvNode *g_lv2_minimum_prop = nullptr; static LilvNode *g_lv2_maximum_prop = nullptr; static LilvNode *g_lv2_default_prop = nullptr; #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h" #else #include "lv2/resize-port/resize-port.h" #endif static LilvNode *g_lv2_minimum_size_prop = nullptr; #endif // CONFIG_LV2_ATOM #ifdef CONFIG_LV2_PATCH static LilvNode *g_lv2_patch_message_class = nullptr; #endif #ifdef CONFIG_LV2_PARAMETERS // LV2 Parameters option. #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/parameters/parameters.h" #else #include "lv2/parameters/parameters.h" #endif #endif // LV2 URIDs stock. static struct qtractorLv2Urids { #ifdef CONFIG_LV2_ATOM LV2_URID atom_eventTransfer; LV2_URID atom_Chunk; LV2_URID atom_Sequence; LV2_URID atom_Object; LV2_URID atom_Blank; LV2_URID atom_Bool; LV2_URID atom_Int; LV2_URID atom_Long; LV2_URID atom_Float; LV2_URID atom_Double; LV2_URID atom_String; LV2_URID atom_Path; #ifdef CONFIG_LV2_PORT_EVENT LV2_URID atom_PortEvent; LV2_URID atom_portTuple; #endif #endif // CONFIG_LV2_ATOM #ifdef CONFIG_LV2_PATCH LV2_URID patch_Get; LV2_URID patch_Put; LV2_URID patch_Set; LV2_URID patch_body; LV2_URID patch_property; LV2_URID patch_value; #endif #ifdef CONFIG_LV2_TIME #ifdef CONFIG_LV2_TIME_POSITION LV2_URID time_Position; #endif #endif // CONFIG_LV2_TIME #ifdef CONFIG_LV2_OPTIONS #ifdef CONFIG_LV2_BUF_SIZE LV2_URID bufsz_minBlockLength; LV2_URID bufsz_maxBlockLength; LV2_URID bufsz_nominalBlockLength; LV2_URID bufsz_sequenceSize; #endif #ifdef CONFIG_LV2_UI LV2_URID ui_windowTitle; LV2_URID ui_updateRate; LV2_URID ui_sampleRate; #endif #ifdef CONFIG_LV2_PARAMETERS LV2_URID param_sampleRate; #endif #endif // CONFIG_LV2_OPTIONS #ifdef CONFIG_LV2_STATE LV2_URID state_StateChanged; #endif } g_lv2_urids; #ifdef CONFIG_LV2_PROGRAMS static void qtractor_lv2_program_changed ( LV2_Programs_Handle handle, int32_t index ) { qtractorLv2Plugin *pLv2Plugin = static_cast (handle); if (pLv2Plugin == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_program_changed(%p, %d)", pLv2Plugin, index); #endif pLv2Plugin->lv2_program_changed(index); } #endif // CONFIG_LV2_PROGRAMS #ifdef CONFIG_LV2_MIDNAM // LV2 MIDNAM XML support. #include static void qtractor_lv2_midnam_update ( LV2_Midnam_Handle handle ) { qtractorLv2Plugin *pLv2Plugin = static_cast (handle); if (pLv2Plugin == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_midname_update(%p)", pLv2Plugin); #endif pLv2Plugin->lv2_midnam_update(); } #endif // CONFIG_LV2_MIDNAME #ifdef CONFIG_LV2_STATE // LV2 State/Presets: port value settler. static void qtractor_lv2_set_port_value ( const char *port_symbol, void *user_data, const void *value, uint32_t size, uint32_t type ) { qtractorLv2Plugin *pLv2Plugin = static_cast (user_data); if (pLv2Plugin == nullptr) return; const LilvPlugin *plugin = pLv2Plugin->lv2_plugin(); if (plugin == nullptr) return; if (size != sizeof(float) || type != g_lv2_urids.atom_Float) return; LilvNode *symbol = lilv_new_string(g_lv2_world, port_symbol); const LilvPort *port = lilv_plugin_get_port_by_symbol(plugin, symbol); if (port) { const float fValue = *(float *) value; const unsigned long iIndex = lilv_port_get_index(plugin, port); qtractorPlugin::Param *pParam = pLv2Plugin->findParam(iIndex); if (pParam) pParam->setValue(fValue, false); } lilv_node_free(symbol); } #endif // CONFIG_LV2_STATE #ifdef CONFIG_LV2_PRESETS // LV2 Presets: have sort avaliable. #include // LV2 Presets: port value getter. static const void *qtractor_lv2_get_port_value ( const char *port_symbol, void *user_data, uint32_t *size, uint32_t *type ) { const void *retv = nullptr; *size = 0; *type = 0; qtractorLv2Plugin *pLv2Plugin = static_cast (user_data); if (pLv2Plugin == nullptr) return retv; const LilvPlugin *plugin = pLv2Plugin->lv2_plugin(); if (plugin == nullptr) return retv; LilvNode *symbol = lilv_new_string(g_lv2_world, port_symbol); const LilvPort *port = lilv_plugin_get_port_by_symbol(plugin, symbol); if (port) { unsigned long iIndex = lilv_port_get_index(plugin, port); qtractorPlugin::Param *pParam = pLv2Plugin->findParam(iIndex); if (pParam) { *size = sizeof(float); *type = g_lv2_urids.atom_Float; retv = (const void *) (pParam->subject())->data(); } } lilv_node_free(symbol); return retv; } // Remove specific dir/file path. static void qtractor_lv2_remove_file (const QFileInfo& info); static void qtractor_lv2_remove_dir ( const QString& sDir ) { const QDir dir(sDir); const QList& list = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot); QListIterator iter(list); while (iter.hasNext()) qtractor_lv2_remove_file(iter.next()); QDir cwd = QDir::current(); if (cwd.absolutePath() == dir.absolutePath()) { cwd.cdUp(); QDir::setCurrent(cwd.path()); } dir.rmdir(sDir); } static void qtractor_lv2_remove_file ( const QFileInfo& info ) { if (info.exists()) { const QString& sPath = info.absoluteFilePath(); if (info.isDir()) { qtractor_lv2_remove_dir(sPath); } else { QFile::remove(sPath); } } } #endif // CONFIG_LV2_PRESETS #ifdef CONFIG_LV2_PATCH #ifdef CONFIG_LV2_TIME // JACK Transport position support. #include // LV2 Time-position control structure. static struct qtractorLv2Time { enum Index { frame = 0, framesPerSecond, speed, bar, beat, barBeat, beatUnit, beatsPerBar, beatsPerMinute, numOfMembers }; const char *uri; LV2_URID urid; LilvNode *node; float value; uint32_t changed; QList *params; } g_lv2_time[] = { { LV2_TIME__frame, 0, nullptr, 0.0f, 0, nullptr }, { LV2_TIME__framesPerSecond, 0, nullptr, 0.0f, 0, nullptr }, { LV2_TIME__speed, 0, nullptr, 0.0f, 0, nullptr }, { LV2_TIME__bar, 0, nullptr, 0.0f, 0, nullptr }, { LV2_TIME__beat, 0, nullptr, 0.0f, 0, nullptr }, { LV2_TIME__barBeat, 0, nullptr, 0.0f, 0, nullptr }, { LV2_TIME__beatUnit, 0, nullptr, 0.0f, 0, nullptr }, { LV2_TIME__beatsPerBar, 0, nullptr, 0.0f, 0, nullptr }, { LV2_TIME__beatsPerMinute, 0, nullptr, 0.0f, 0, nullptr } }; static uint32_t g_lv2_time_refcount = 0; #ifdef CONFIG_LV2_TIME_POSITION // LV2 Time position atoms... static LilvNode *g_lv2_time_position_class = nullptr; static uint8_t *g_lv2_time_position_buffer = nullptr; static uint32_t g_lv2_time_position_changed = 0; static QList *g_lv2_time_position_plugins = nullptr; static void qtractor_lv2_time_position_open ( qtractorLv2Plugin *pLv2Plugin ) { #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_time_position_open(%p)", pLv2Plugin); #endif if (g_lv2_time_position_plugins == nullptr) g_lv2_time_position_plugins = new QList (); g_lv2_time_position_plugins->append(pLv2Plugin); if (g_lv2_time_position_buffer == nullptr) g_lv2_time_position_buffer = new uint8_t [256]; ++g_lv2_time_refcount; } static void qtractor_lv2_time_position_close ( qtractorLv2Plugin *pLv2Plugin ) { #ifdef CONFIG_DEBUG qDebug("qtractor_lv2_time_position_close(%p)", pLv2Plugin); #endif --g_lv2_time_refcount; if (g_lv2_time_position_plugins) { g_lv2_time_position_plugins->removeAll(pLv2Plugin); if (g_lv2_time_position_plugins->isEmpty()) { delete g_lv2_time_position_plugins; g_lv2_time_position_plugins = nullptr; } } if (g_lv2_time_position_plugins == nullptr) { if (g_lv2_time_position_buffer) { delete [] g_lv2_time_position_buffer; g_lv2_time_position_buffer = nullptr; } } } #endif // CONFIG_LV2_TIME_POSITION #endif // CONFIG_LV2_TIME #endif // CONFIG_LV2_PATCH #ifdef CONFIG_LV2_UI #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef CONFIG_LV2_UI_GTK2 #include "qtractorLv2Gtk2Plugin.h" #include #undef signals // Collides with some GTK symbology #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #include #include #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic pop #endif static void qtractor_lv2_ui_gtk2_on_size_request ( GtkWidget */*widget*/, GtkRequisition *req, gpointer user_data ) { QWidget *pQtWidget = static_cast (user_data); pQtWidget->setMinimumSize(req->width, req->height); } static void qtractor_lv2_ui_gtk2_on_size_allocate ( GtkWidget */*widget*/, GdkRectangle *rect, gpointer user_data ) { QWidget *pQtWidget = static_cast (user_data); pQtWidget->resize(rect->width, rect->height); } #endif // CONFIG_LV2_UI_GTK2 #ifdef CONFIG_LV2_UI_X11 #include #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #endif static void qtractor_lv2_ui_size_hints ( WId wid, QSize& size ) { xcb_connection_t *c = nullptr; #if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0) const QNativeInterface::QX11Application *ni = qApp->nativeInterface (); if (ni) c = ni->connection(); #endif #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) c = QX11Info::connection(); #endif if (c == nullptr) return; xcb_get_geometry_cookie_t cookie = ::xcb_get_geometry(c, wid); xcb_get_geometry_reply_t *reply = ::xcb_get_geometry_reply(c, cookie, nullptr); if (reply) { size.setWidth(reply->width); size.setHeight(reply->height); ::free(reply); } } static int qtractor_lv2_ui_resize ( LV2UI_Feature_Handle handle, int width, int height ) { QWidget *pQtWidget = static_cast (handle); if (pQtWidget) { pQtWidget->resize(width, height); return 0; } else { return 1; } } #endif // CONFIG_LV2_UI_X11 #endif #endif // CONFIG_LV2_UI //---------------------------------------------------------------------------- // qtractorLv2PluginType -- LV2 plugin type instance. // // Derived methods. bool qtractorLv2PluginType::open (void) { // Do we have a descriptor already? if (m_lv2_plugin == nullptr) m_lv2_plugin = lv2_plugin(m_sUri); if (m_lv2_plugin == nullptr) return false; #ifdef CONFIG_DEBUG qDebug("qtractorLv2PluginType[%p]::open() uri=\"%s\"", this, filename().toUtf8().constData()); #endif // Retrieve plugin type names. LilvNode *name = lilv_plugin_get_name(m_lv2_plugin); if (name) { m_sName = lilv_node_as_string(name); lilv_node_free(name); } else { m_sName = filename(); const int iIndex = m_sName.lastIndexOf('/'); if (iIndex > 0) m_sName = m_sName.right(m_sName.length() - iIndex - 1); } // Sanitize plugin label. m_sLabel = m_sName.simplified().replace(QRegularExpression("[\\s|\\.|\\-]+"), "_"); // Retrieve plugin unique identifier. m_iUniqueID = qHash(m_sUri); // Compute and cache port counts... m_iControlIns = 0; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; m_iMidiIns = 0; m_iMidiOuts = 0; #ifdef CONFIG_LV2_EVENT m_iEventIns = 0; m_iEventOuts = 0; #endif #ifdef CONFIG_LV2_ATOM m_iAtomIns = 0; m_iAtomOuts = 0; #endif #ifdef CONFIG_LV2_CVPORT m_iCVPortIns = 0; m_iCVPortOuts = 0; #endif const unsigned long iNumPorts = lilv_plugin_get_num_ports(m_lv2_plugin); for (unsigned long i = 0; i < iNumPorts; ++i) { const LilvPort *port = lilv_plugin_get_port_by_index(m_lv2_plugin, i); if (port) { if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_control_class)) { if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_input_class)) ++m_iControlIns; else if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_output_class)) ++m_iControlOuts; } else if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_audio_class)) { if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_input_class)) ++m_iAudioIns; else if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_output_class)) ++m_iAudioOuts; } #ifdef CONFIG_LV2_EVENT else if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_event_class)) { if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_input_class)) { ++m_iEventIns; if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_midi_class)) ++m_iMidiIns; } else if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_output_class)) { ++m_iEventOuts; if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_midi_class)) ++m_iMidiOuts; } } #endif #ifdef CONFIG_LV2_ATOM else if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_atom_class)) { if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_input_class)) { ++m_iAtomIns; if (lilv_port_supports_event( m_lv2_plugin, port, g_lv2_midi_class) || lilv_port_is_a(m_lv2_plugin, port, g_lv2_midi_class)) ++m_iMidiIns; } else if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_output_class)) { ++m_iAtomOuts; if (lilv_port_supports_event( m_lv2_plugin, port, g_lv2_midi_class) || lilv_port_is_a(m_lv2_plugin, port, g_lv2_midi_class)) ++m_iMidiOuts; } } #endif #ifdef CONFIG_LV2_CVPORT else if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_cvport_class)) { if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_input_class)) ++m_iCVPortIns; else if (lilv_port_is_a(m_lv2_plugin, port, g_lv2_output_class)) ++m_iCVPortOuts; } #endif } } // Cache flags. m_bRealtime = lilv_plugin_has_feature(m_lv2_plugin, g_lv2_realtime_hint); m_bConfigure = false; #ifdef CONFIG_LV2_STATE // Query for state interface extension data... LilvNodes *nodes = lilv_plugin_get_value(m_lv2_plugin, g_lv2_extension_data_hint); if (nodes) { LILV_FOREACH(nodes, iter, nodes) { const LilvNode *node = lilv_nodes_get(nodes, iter); if (lilv_node_equals(node, g_lv2_state_interface_hint)) { m_bConfigure = true; break; } } lilv_nodes_free(nodes); } #endif #ifdef CONFIG_LV2_UI // Check the UI inventory... LilvUIs *uis = lilv_plugin_get_uis(m_lv2_plugin); if (uis) { int uis_count = 0; LILV_FOREACH(uis, iter, uis) { LilvUI *ui = const_cast (lilv_uis_get(uis, iter)); #ifdef CONFIG_LV2_EXTERNAL_UI if (lilv_ui_is_a(ui, g_lv2_external_ui_class) #ifdef LV2_EXTERNAL_UI_DEPRECATED_URI || lilv_ui_is_a(ui, g_lv2_external_ui_deprecated_class) #endif ) ++uis_count; else #endif if (lilv_ui_is_a(ui, g_lv2_x11_ui_class)) ++uis_count; else if (lilv_ui_is_a(ui, g_lv2_gtk_ui_class)) ++uis_count; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) else if (lilv_ui_is_a(ui, g_lv2_qt4_ui_class)) ++uis_count; #else else if (lilv_ui_is_a(ui, g_lv2_qt5_ui_class)) ++uis_count; #endif #ifdef CONFIG_LV2_UI_SHOW else if (lv2_ui_show_interface(ui)) ++uis_count; #endif } m_bEditor = (uis_count > 0); lilv_uis_free(uis); } #endif // Done. return true; } void qtractorLv2PluginType::close (void) { m_lv2_plugin = nullptr; } // Factory method (static) qtractorLv2PluginType *qtractorLv2PluginType::createType ( const QString& sUri ) { // Sanity check... if (sUri.isEmpty()) return nullptr; LilvPlugin *plugin = lv2_plugin(sUri); if (plugin == nullptr) return nullptr; // Yep, most probably its a valid plugin descriptor... return new qtractorLv2PluginType(sUri, plugin); } // Descriptor method (static) LilvPlugin *qtractorLv2PluginType::lv2_plugin ( const QString& sUri ) { if (g_lv2_plugins == nullptr) return nullptr; // Retrieve plugin descriptor if any... LilvNode *uri = lilv_new_uri(g_lv2_world, sUri.toUtf8().constData()); if (uri == nullptr) return nullptr; LilvPlugin *plugin = const_cast ( lilv_plugins_get_by_uri(g_lv2_plugins, uri)); #if 0 LilvNodes *list = lilv_plugin_get_required_features( static_cast (plugin)); if (list) { LILV_FOREACH(nodes, iter, list) { const LilvNode *node = lilv_nodes_get(list, iter); bool bSupported = false; for (int i = 0; !bSupported && g_lv2_features[i]; ++i) { const LilvNode *impl = lilv_new_uri(g_lv2_world, g_lv2_features[i]->URI); bSupported = lilv_node_equals(impl, node); lilv_node_free(impl); } if (!bSupported) { #ifdef CONFIG_DEBUG qDebug("qtractorLv2PluginType::lilv_plugin: node %s not supported.", lilv_node_as_string(lilv_nodes_get(node, iter))); #endif plugin = nullptr; break; } } } #endif lilv_node_free(uri); return plugin; } // LV2 World stuff (ref. counted). void qtractorLv2PluginType::lv2_open (void) { if (g_lv2_plugins) return; #ifdef CONFIG_DEBUG qDebug("qtractorLv2PluginType::lv2_open()"); #endif // HACK: set special environment for LV2... qtractorPluginFactory *pPluginFactory = qtractorPluginFactory::getInstance(); if (pPluginFactory) { const char *LV2_PATH = "LV2_PATH"; const QStringList& lv2_paths = pPluginFactory->pluginPaths(qtractorPluginType::Lv2); if (lv2_paths.isEmpty()) { ::unsetenv(LV2_PATH); } else { #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) const QString sPathSep(';'); #else const QString sPathSep(':'); #endif ::setenv(LV2_PATH, lv2_paths.join(sPathSep).toUtf8().constData(), 1); } } // Taking on all the world... g_lv2_world = lilv_world_new(); // Find all installed plugins. lilv_world_load_all(g_lv2_world); g_lv2_plugins = const_cast ( lilv_world_get_all_plugins(g_lv2_world)); // Set up the port classes we support. g_lv2_input_class = lilv_new_uri(g_lv2_world, LILV_URI_INPUT_PORT); g_lv2_output_class = lilv_new_uri(g_lv2_world, LILV_URI_OUTPUT_PORT); g_lv2_control_class = lilv_new_uri(g_lv2_world, LILV_URI_CONTROL_PORT); g_lv2_audio_class = lilv_new_uri(g_lv2_world, LILV_URI_AUDIO_PORT); g_lv2_midi_class = lilv_new_uri(g_lv2_world, LILV_URI_MIDI_EVENT); #ifdef CONFIG_LV2_EVENT g_lv2_event_class = lilv_new_uri(g_lv2_world, LILV_URI_EVENT_PORT); #endif #ifdef CONFIG_LV2_ATOM g_lv2_atom_class = lilv_new_uri(g_lv2_world, LV2_ATOM__AtomPort); #endif #ifdef CONFIG_LV2_CVPORT g_lv2_cvport_class = lilv_new_uri(g_lv2_world, LV2_CORE__CVPort); #endif #ifdef CONFIG_LV2_UI #ifdef CONFIG_LV2_EXTERNAL_UI g_lv2_external_ui_class = lilv_new_uri(g_lv2_world, LV2_EXTERNAL_UI__Widget); #ifdef LV2_EXTERNAL_UI_DEPRECATED_URI g_lv2_external_ui_deprecated_class = lilv_new_uri(g_lv2_world, LV2_EXTERNAL_UI_DEPRECATED_URI); #endif #endif // CONFIG_LV2_EXTERNAL_UI g_lv2_x11_ui_class = lilv_new_uri(g_lv2_world, LV2_UI__X11UI); g_lv2_gtk_ui_class = lilv_new_uri(g_lv2_world, LV2_UI__GtkUI); g_lv2_qt4_ui_class = lilv_new_uri(g_lv2_world, LV2_UI__Qt4UI); g_lv2_qt5_ui_class = lilv_new_uri(g_lv2_world, LV2_UI__Qt5UI); #endif // CONFIG_LV2_UI // Set up the feature we may want to know (as hints). g_lv2_realtime_hint = lilv_new_uri(g_lv2_world, LV2_CORE__hardRTCapable); g_lv2_extension_data_hint = lilv_new_uri(g_lv2_world, LV2_CORE__extensionData); #ifdef CONFIG_LV2_WORKER // LV2 Worker/Schedule support hints... g_lv2_worker_schedule_hint = lilv_new_uri(g_lv2_world, LV2_WORKER__schedule); #endif #ifdef CONFIG_LV2_STATE // LV2 State: set up supported interface and types... g_lv2_state_interface_hint = lilv_new_uri(g_lv2_world, LV2_STATE__interface); g_lv2_state_load_default_hint = lilv_new_uri(g_lv2_world, LV2_STATE__loadDefaultState); #endif // Set up the port properties we support (as hints). g_lv2_toggled_prop = lilv_new_uri(g_lv2_world, LV2_CORE__toggled); g_lv2_integer_prop = lilv_new_uri(g_lv2_world, LV2_CORE__integer); g_lv2_sample_rate_prop = lilv_new_uri(g_lv2_world, LV2_CORE__sampleRate); g_lv2_logarithmic_prop = lilv_new_uri(g_lv2_world, LV2_PORT_PROPS__logarithmic); #ifdef CONFIG_LV2_ATOM g_lv2_atom_forge = new LV2_Atom_Forge(); lv2_atom_forge_init(g_lv2_atom_forge, &g_lv2_urid_map); g_lv2_maximum_prop = lilv_new_uri(g_lv2_world, LV2_CORE__maximum); g_lv2_minimum_prop = lilv_new_uri(g_lv2_world, LV2_CORE__minimum); g_lv2_default_prop = lilv_new_uri(g_lv2_world, LV2_CORE__default); g_lv2_minimum_size_prop = lilv_new_uri(g_lv2_world, LV2_RESIZE_PORT__minimumSize); #endif // CONFIG_LV2_ATOM #ifdef CONFIG_LV2_PATCH g_lv2_patch_message_class = lilv_new_uri(g_lv2_world, LV2_PATCH__Message); #endif // LV2 URIDs stock setup... #ifdef CONFIG_LV2_ATOM g_lv2_urids.atom_eventTransfer = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__eventTransfer); g_lv2_urids.atom_Chunk = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Chunk); g_lv2_urids.atom_Sequence = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Sequence); g_lv2_urids.atom_Object = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Object); g_lv2_urids.atom_Blank = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Blank); g_lv2_urids.atom_Bool = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Bool); g_lv2_urids.atom_Int = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Int); g_lv2_urids.atom_Long = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Long); g_lv2_urids.atom_Float = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Float); g_lv2_urids.atom_Double = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Double); g_lv2_urids.atom_String = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__String); g_lv2_urids.atom_Path = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__Path); #ifdef CONFIG_LV2_PORT_EVENT g_lv2_urids.atom_PortEvent = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__PortEvent); g_lv2_urids.atom_portTuple = qtractorLv2Plugin::lv2_urid_map(LV2_ATOM__portTuple); #endif #endif // CONFIG_LV2_ATOM #ifdef CONFIG_LV2_PATCH g_lv2_urids.patch_Get = qtractorLv2Plugin::lv2_urid_map(LV2_PATCH__Get); g_lv2_urids.patch_Put = qtractorLv2Plugin::lv2_urid_map(LV2_PATCH__Put); g_lv2_urids.patch_Set = qtractorLv2Plugin::lv2_urid_map(LV2_PATCH__Set); g_lv2_urids.patch_body = qtractorLv2Plugin::lv2_urid_map(LV2_PATCH__body); g_lv2_urids.patch_property = qtractorLv2Plugin::lv2_urid_map(LV2_PATCH__property); g_lv2_urids.patch_value = qtractorLv2Plugin::lv2_urid_map(LV2_PATCH__value); #endif #ifdef CONFIG_LV2_TIME #ifdef CONFIG_LV2_TIME_POSITION g_lv2_urids.time_Position = qtractorLv2Plugin::lv2_urid_map(LV2_TIME__Position); #endif #endif // CONFIG_LV2_TIME #ifdef CONFIG_LV2_OPTIONS #ifdef CONFIG_LV2_BUF_SIZE g_lv2_urids.bufsz_minBlockLength = qtractorLv2Plugin::lv2_urid_map(LV2_BUF_SIZE__minBlockLength); g_lv2_urids.bufsz_maxBlockLength = qtractorLv2Plugin::lv2_urid_map(LV2_BUF_SIZE__maxBlockLength); g_lv2_urids.bufsz_nominalBlockLength = qtractorLv2Plugin::lv2_urid_map(LV2_BUF_SIZE__nominalBlockLength); g_lv2_urids.bufsz_sequenceSize = qtractorLv2Plugin::lv2_urid_map(LV2_BUF_SIZE__sequenceSize); #endif #ifdef CONFIG_LV2_UI g_lv2_urids.ui_windowTitle = qtractorLv2Plugin::lv2_urid_map(LV2_UI__windowTitle); g_lv2_urids.ui_updateRate = qtractorLv2Plugin::lv2_urid_map(LV2_UI__updateRate); g_lv2_urids.ui_sampleRate = qtractorLv2Plugin::lv2_urid_map(LV2_CORE__sampleRate); #endif #ifdef CONFIG_LV2_PARAMETERS g_lv2_urids.param_sampleRate = qtractorLv2Plugin::lv2_urid_map(LV2_PARAMETERS__sampleRate); #endif #endif // CONFIG_LV2_OPTIONS #ifdef CONFIG_LV2_STATE g_lv2_urids.state_StateChanged = qtractorLv2Plugin::lv2_urid_map(LV2_STATE__StateChanged); #endif #ifdef CONFIG_LV2_TIME // LV2 Time: set up supported port designations... for (int i = 0; i < int(qtractorLv2Time::numOfMembers); ++i) { qtractorLv2Time& member = g_lv2_time[i]; member.node = lilv_new_uri(g_lv2_world, member.uri); member.urid = qtractorLv2Plugin::lv2_urid_map(member.uri); member.value = 0.0f; member.changed = 0; member.params = new QList (); } #ifdef CONFIG_LV2_TIME_POSITION // LV2 Time: set up for atom port event notifications... g_lv2_time_position_class = lilv_new_uri(g_lv2_world, LV2_TIME__Position); #endif #endif // CONFIG_LV2_TIME } void qtractorLv2PluginType::lv2_close (void) { if (g_lv2_plugins == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorLv2PluginType::lv2_close()"); #endif #ifdef CONFIG_LV2_CVPORT g_lv2_cvport_class = NULL; #endif // LV2 URIDs stock reset. ::memset(&g_lv2_urids, 0, sizeof(g_lv2_urids)); #ifdef CONFIG_LV2_TIME for (int i = 0; i < int(qtractorLv2Time::numOfMembers); ++i) { qtractorLv2Time& member = g_lv2_time[i]; lilv_node_free(member.node); member.node = nullptr; member.urid = 0; member.value = 0.0f; member.changed = 0; delete member.params; member.params = nullptr; } #ifdef CONFIG_LV2_TIME_POSITION lilv_node_free(g_lv2_time_position_class); #endif #endif // CONFIG_LV2_TIME // Clean up. lilv_node_free(g_lv2_toggled_prop); lilv_node_free(g_lv2_integer_prop); lilv_node_free(g_lv2_sample_rate_prop); lilv_node_free(g_lv2_logarithmic_prop); #ifdef CONFIG_LV2_ATOM if (g_lv2_atom_forge) { delete g_lv2_atom_forge; g_lv2_atom_forge = nullptr; } lilv_node_free(g_lv2_maximum_prop); lilv_node_free(g_lv2_minimum_prop); lilv_node_free(g_lv2_default_prop); lilv_node_free(g_lv2_minimum_size_prop); #endif // CONFIG_LV2_ATOM #ifdef CONFIG_LV2_PATCH lilv_node_free(g_lv2_patch_message_class); #endif #ifdef CONFIG_LV2_STATE lilv_node_free(g_lv2_state_interface_hint); lilv_node_free(g_lv2_state_load_default_hint); #endif #ifdef CONFIG_LV2_WORKER lilv_node_free(g_lv2_worker_schedule_hint); #endif lilv_node_free(g_lv2_extension_data_hint); lilv_node_free(g_lv2_realtime_hint); lilv_node_free(g_lv2_input_class); lilv_node_free(g_lv2_output_class); lilv_node_free(g_lv2_control_class); lilv_node_free(g_lv2_audio_class); lilv_node_free(g_lv2_midi_class); #ifdef CONFIG_LV2_EVENT lilv_node_free(g_lv2_event_class); #endif #ifdef CONFIG_LV2_ATOM lilv_node_free(g_lv2_atom_class); #endif #ifdef CONFIG_LV2_CVPORT lilv_node_free(g_lv2_cvport_class); #endif #ifdef CONFIG_LV2_UI #ifdef CONFIG_LV2_EXTERNAL_UI lilv_node_free(g_lv2_external_ui_class); #ifdef LV2_EXTERNAL_UI_DEPRECATED_URI lilv_node_free(g_lv2_external_ui_deprecated_class); #endif #endif // CONFIG_LV2_EXTERNAL_UI lilv_node_free(g_lv2_x11_ui_class); lilv_node_free(g_lv2_gtk_ui_class); lilv_node_free(g_lv2_qt4_ui_class); lilv_node_free(g_lv2_qt5_ui_class); #endif // CONFIG_LV2_UI lilv_world_free(g_lv2_world); #ifdef CONFIG_LV2_TIME #ifdef CONFIG_LV2_TIME_POSITION g_lv2_time_position_class = nullptr; #endif #endif g_lv2_toggled_prop = nullptr; g_lv2_integer_prop = nullptr; g_lv2_sample_rate_prop = nullptr; g_lv2_logarithmic_prop = nullptr; #ifdef CONFIG_LV2_ATOM g_lv2_maximum_prop = nullptr; g_lv2_minimum_prop = nullptr; g_lv2_default_prop = nullptr; g_lv2_minimum_size_prop = nullptr; #endif // CONFIG_LV2_ATOM #ifdef CONFIG_LV2_PATCH g_lv2_patch_message_class = nullptr; #endif #ifdef CONFIG_LV2_STATE g_lv2_state_interface_hint = nullptr; g_lv2_state_load_default_hint = nullptr; #endif #ifdef CONFIG_LV2_WORKER g_lv2_worker_schedule_hint = nullptr; #endif g_lv2_extension_data_hint = nullptr; g_lv2_realtime_hint = nullptr; g_lv2_input_class = nullptr; g_lv2_output_class = nullptr; g_lv2_control_class = nullptr; g_lv2_audio_class = nullptr; g_lv2_midi_class = nullptr; #ifdef CONFIG_LV2_EVENT g_lv2_event_class = nullptr; #endif #ifdef CONFIG_LV2_ATOM g_lv2_atom_class = nullptr; #endif #ifdef CONFIG_LV2_UI #ifdef CONFIG_LV2_EXTERNAL_UI g_lv2_external_ui_class = nullptr; #ifdef LV2_EXTERNAL_UI_DEPRECATED_URI g_lv2_external_ui_deprecated_class = nullptr; #endif #endif // CONFIG_LV2_EXTERNAL_UI g_lv2_x11_ui_class = nullptr; g_lv2_gtk_ui_class = nullptr; g_lv2_qt4_ui_class = nullptr; g_lv2_qt5_ui_class = nullptr; #endif // CONFIG_LV2_UI g_lv2_plugins = nullptr; g_lv2_world = nullptr; } // Plugin URI listing (static). QStringList qtractorLv2PluginType::lv2_plugins (void) { QStringList list; if (g_lv2_plugins) { LILV_FOREACH(plugins, iter, g_lv2_plugins) { const LilvPlugin *plugin = lilv_plugins_get(g_lv2_plugins, iter); const char *pszUri = lilv_node_as_uri(lilv_plugin_get_uri(plugin)); list.append(QString::fromLocal8Bit(pszUri)); } } return list; } #ifdef CONFIG_LV2_UI_SHOW // Check for LV2 UI Show interface. bool qtractorLv2PluginType::lv2_ui_show_interface ( LilvUI *ui ) const { if (m_lv2_plugin == nullptr) return false; const LilvNode *ui_uri = lilv_ui_get_uri(ui); lilv_world_load_resource(g_lv2_world, ui_uri); LilvNode *show_interface_uri = lilv_new_uri(g_lv2_world, LV2_UI__showInterface); const bool ui_show_interface = lilv_world_ask(g_lv2_world, ui_uri, g_lv2_extension_data_hint, show_interface_uri); lilv_node_free(show_interface_uri); #ifdef CONFIG_LILV_WORLD_UNLOAD_RESOURCE lilv_world_unload_resource(g_lv2_world, ui_uri); #endif return ui_show_interface; } #endif // CONFIG_LV2_UI_SHOW // Instance cached-deferred accesors. const QString& qtractorLv2PluginType::aboutText (void) { if (m_sAboutText.isEmpty() && m_lv2_plugin) { LilvNode *node = lilv_plugin_get_project(m_lv2_plugin); if (node) { if (!m_sAboutText.isEmpty()) m_sAboutText += '\n'; m_sAboutText += QObject::tr("Project: "); m_sAboutText += lilv_node_as_string(node); lilv_node_free(node); } node = lilv_plugin_get_author_name(m_lv2_plugin); if (node) { if (!m_sAboutText.isEmpty()) m_sAboutText += '\n'; m_sAboutText += QObject::tr("Author: "); m_sAboutText += lilv_node_as_string(node); lilv_node_free(node); } node = lilv_plugin_get_author_email(m_lv2_plugin); if (node) { if (!m_sAboutText.isEmpty()) m_sAboutText += '\n'; // m_sAboutText += QObject::tr("Email: "); m_sAboutText += lilv_node_as_string(node); lilv_node_free(node); } node = lilv_plugin_get_author_homepage(m_lv2_plugin); if (node) { if (!m_sAboutText.isEmpty()) m_sAboutText += '\n'; // m_sAboutText += QObject::tr("Homepage: "); m_sAboutText += lilv_node_as_string(node); lilv_node_free(node); } } return m_sAboutText; } //---------------------------------------------------------------------------- // qtractorLv2Plugin -- LV2 plugin instance. // // Dynamic singleton list of LV2 plugins. static QList g_lv2Plugins; // Constructors. qtractorLv2Plugin::qtractorLv2Plugin ( qtractorPluginList *pList, qtractorLv2PluginType *pLv2Type ) : qtractorPlugin(pList, pLv2Type) , m_ppInstances(nullptr) , m_piControlOuts(nullptr) , m_pfControlOuts(nullptr) , m_pfControlOutsLast(nullptr) , m_piAudioIns(nullptr) , m_piAudioOuts(nullptr) , m_pfIDummy(nullptr) , m_pfODummy(nullptr) #ifdef CONFIG_LV2_EVENT , m_piEventIns(nullptr) , m_piEventOuts(nullptr) #endif #ifdef CONFIG_LV2_ATOM , m_piAtomIns(nullptr) , m_piAtomOuts(nullptr) , m_lv2_atom_buffer_ins(nullptr) , m_lv2_atom_buffer_outs(nullptr) , m_lv2_atom_midi_port_in(0) , m_lv2_atom_midi_port_out(0) #endif #ifdef CONFIG_LV2_CVPORT , m_piCVPortIns(nullptr) , m_piCVPortOuts(nullptr) #endif , m_lv2_features(nullptr) #ifdef CONFIG_LV2_WORKER , m_lv2_worker(nullptr) #endif #ifdef CONFIG_LV2_UI , m_lv2_ui_type(LV2_UI_TYPE_NONE) , m_bEditorVisible(false) , m_bEditorClosed(false) , m_lv2_uis(nullptr) , m_lv2_ui(nullptr) , m_lv2_ui_features(nullptr) , m_lv2_ui_module(nullptr) , m_lv2_ui_descriptor(nullptr) , m_lv2_ui_handle(nullptr) , m_lv2_ui_widget(nullptr) , m_lv2_ui_no_user_resize(false) #ifdef CONFIG_LIBSUIL , m_suil_host(nullptr) , m_suil_instance(nullptr) , m_suil_support(false) #endif #ifdef CONFIG_LV2_ATOM , m_ui_events(nullptr) , m_plugin_events(nullptr) #endif , m_pQtFilter(nullptr) , m_pQtWidget(nullptr) , m_bQtDelete(false) #ifdef CONFIG_LV2_UI_REQ_VALUE , m_lv2_ui_req_value_busy(false) #endif // CONFIG_LV2_UI_REQ_VALUE #ifdef CONFIG_LV2_UI_IDLE , m_lv2_ui_idle_interface(nullptr) #endif #ifdef CONFIG_LV2_UI_SHOW , m_lv2_ui_show_interface(nullptr) #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef CONFIG_LV2_UI_GTK2 , m_pGtkWindow(nullptr) , m_pQtWindow(nullptr) #endif // CONFIG_LV2_UI_GTK2 #endif #endif // CONFIG_LV2_UI #ifdef CONFIG_LV2_MIDNAM , m_lv2_midnam_update(0) #endif #ifdef CONFIG_LV2_TIME #ifdef CONFIG_LV2_TIME_POSITION , m_lv2_time_position_enabled(false) , m_lv2_time_position_port_in(0) , m_lv2_time_position_changed(0) #endif #endif // CONFIG_LV2_TIME #ifdef CONFIG_LV2_PATCH , m_lv2_patch_port_in(0) , m_lv2_patch_changed(0) #endif // CONFIG_LV2_PATCH #ifdef CONFIG_LV2_OPTIONS #ifdef CONFIG_LV2_BUF_SIZE , m_iMinBlockLength(0) , m_iMaxBlockLength(0) , m_iNominalBlockLength(0) , m_iSequenceSize(0) #endif #ifdef CONFIG_LV2_UI , m_fUpdateRate(15.0f) , m_fSampleRate(44100.0f) , m_dSampleRate(44100.0) #endif #endif // CONFIG_LV2_OPTIONS , m_pfLatency(nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractorLv2Plugin[%p] uri=\"%s\"", this, pLv2Type->filename().toUtf8().constData()); #endif int iFeatures = 0; while (g_lv2_features[iFeatures]) { ++iFeatures; } m_lv2_features = new LV2_Feature * [iFeatures + 9]; for (int i = 0; i < iFeatures; ++i) m_lv2_features[i] = (LV2_Feature *) g_lv2_features[i]; #ifdef CONFIG_LV2_STATE m_lv2_state_load_default_feature.URI = LV2_STATE__loadDefaultState; m_lv2_state_load_default_feature.data = nullptr; m_lv2_features[iFeatures++] = &m_lv2_state_load_default_feature; #endif // CONFIG_LV2_STATE #ifdef CONFIG_LV2_STATE_FILES m_lv2_state_map_path.handle = this; m_lv2_state_map_path.abstract_path = &qtractor_lv2_state_abstract_path; m_lv2_state_map_path.absolute_path = &qtractor_lv2_state_absolute_path; m_lv2_state_map_path_feature.URI = LV2_STATE__mapPath; m_lv2_state_map_path_feature.data = &m_lv2_state_map_path; m_lv2_features[iFeatures++] = &m_lv2_state_map_path_feature; #ifdef CONFIG_LV2_STATE_MAKE_PATH m_lv2_state_make_path.handle = this; m_lv2_state_make_path.path = &qtractor_lv2_state_make_path; m_lv2_state_make_path_feature.URI = LV2_STATE__makePath; m_lv2_state_make_path_feature.data = &m_lv2_state_make_path; m_lv2_features[iFeatures++] = &m_lv2_state_make_path_feature; #endif // CONFIG_LV2_STATE_MAKE_PATH #ifdef CONFIG_LV2_STATE_FREE_PATH m_lv2_state_free_path.handle = this; m_lv2_state_free_path.free_path = &qtractor_lv2_state_free_path; m_lv2_state_free_path_feature.URI = LV2_STATE__freePath; m_lv2_state_free_path_feature.data = &m_lv2_state_free_path; m_lv2_features[iFeatures++] = &m_lv2_state_free_path_feature; #endif // CONFIG_LV2_STATE_FREE_PATH #endif // CONFIG_LV2_STATE_FILES #ifdef CONFIG_LV2_PROGRAMS m_lv2_programs_host.handle = this; m_lv2_programs_host.program_changed = &qtractor_lv2_program_changed; m_lv2_programs_host_feature.URI = LV2_PROGRAMS__Host; m_lv2_programs_host_feature.data = &m_lv2_programs_host; m_lv2_features[iFeatures++] = &m_lv2_programs_host_feature; #endif // CONFIG_LV2_PROGRAMS #ifdef CONFIG_LV2_MIDNAM m_lv2_midnam.handle = this; m_lv2_midnam.update = &qtractor_lv2_midnam_update; m_lv2_midnam_feature.URI = LV2_MIDNAM__update; m_lv2_midnam_feature.data = &m_lv2_midnam; m_lv2_features[iFeatures++] = &m_lv2_midnam_feature; #endif // CONFIG_LV2_MIDNAM #ifdef CONFIG_LV2_UI #ifdef CONFIG_LV2_PORT_CHANGE_REQUEST m_lv2_port_change_request.handle = this; m_lv2_port_change_request.request_change = &qtractor_lv2_port_change_request; m_lv2_port_change_request_feature.URI = LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_URI; m_lv2_port_change_request_feature.data = &m_lv2_port_change_request; m_lv2_features[iFeatures++] = &m_lv2_port_change_request_feature; #endif // CONFIG_LV2_PORT_CHANGE_REQUEST #endif #if defined(CONFIG_LV2_EVENT) || defined(CONFIG_LV2_ATOM) qtractorMidiManager *pMidiManager = list()->midiManager(); #endif #ifdef CONFIG_LV2_OPTIONS #ifdef CONFIG_LV2_BUF_SIZE m_iMinBlockLength = 0; m_iMaxBlockLength = 0; m_iNominalBlockLength = 0; m_iSequenceSize = 0; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) { m_iMinBlockLength = pAudioEngine->bufferSize(); m_iMaxBlockLength = pAudioEngine->bufferSizeEx(); m_iNominalBlockLength = m_iMinBlockLength; } } if (pMidiManager) { const uint32_t MaxMidiEvents = (pMidiManager->bufferSize() << 1); #ifdef CONFIG_LV2_EVENT const uint32_t Lv2EventBufferSize = (sizeof(LV2_Event) + 4) * MaxMidiEvents; if (m_iSequenceSize < Lv2EventBufferSize) m_iSequenceSize = Lv2EventBufferSize; #endif #ifdef CONFIG_LV2_ATOM const uint32_t Lv2AtomBufferSize = (sizeof(LV2_Atom_Event) + 4) * MaxMidiEvents; if (m_iSequenceSize < Lv2AtomBufferSize) m_iSequenceSize = Lv2AtomBufferSize; #endif } // Build options array to pass to plugin const LV2_Options_Option options[] = { { LV2_OPTIONS_INSTANCE, 0, g_lv2_urids.bufsz_minBlockLength, sizeof(int32_t), g_lv2_urids.atom_Int, &m_iMinBlockLength }, { LV2_OPTIONS_INSTANCE, 0, g_lv2_urids.bufsz_maxBlockLength, sizeof(int32_t), g_lv2_urids.atom_Int, &m_iMaxBlockLength }, { LV2_OPTIONS_INSTANCE, 0, g_lv2_urids.bufsz_nominalBlockLength, sizeof(int32_t), g_lv2_urids.atom_Int, &m_iNominalBlockLength }, { LV2_OPTIONS_INSTANCE, 0, g_lv2_urids.bufsz_sequenceSize, sizeof(int32_t), g_lv2_urids.atom_Int, &m_iSequenceSize }, { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, nullptr } }; ::memcpy(&m_lv2_options, &options, sizeof(options)); m_lv2_options_feature.URI = LV2_OPTIONS__options; m_lv2_options_feature.data = &m_lv2_options; m_lv2_features[iFeatures++] = &m_lv2_options_feature; #endif // CONFIG_LV2_BUF_SIZE #endif // CONFIG_LV2_OPTIONS m_lv2_features[iFeatures] = nullptr; // Get some structural data first... const LilvPlugin *plugin = pLv2Type->lv2_plugin(); if (plugin) { unsigned short iControlOuts = pLv2Type->controlOuts(); unsigned short iAudioIns = pLv2Type->audioIns(); unsigned short iAudioOuts = pLv2Type->audioOuts(); if (iAudioIns > 0) m_piAudioIns = new unsigned long [iAudioIns]; if (iAudioOuts > 0) m_piAudioOuts = new unsigned long [iAudioOuts]; if (iControlOuts > 0) { m_piControlOuts = new unsigned long [iControlOuts]; m_pfControlOuts = new float [iControlOuts]; m_pfControlOutsLast = new float [iControlOuts]; } iControlOuts = iAudioIns = iAudioOuts = 0; #ifdef CONFIG_LV2_EVENT unsigned short iEventIns = pLv2Type->eventIns(); unsigned short iEventOuts = pLv2Type->eventOuts(); if (iEventIns > 0) m_piEventIns = new unsigned long [iEventIns]; if (iEventOuts > 0) m_piEventOuts = new unsigned long [iEventOuts]; iEventIns = iEventOuts = 0; #endif // CONFIG_LV2_EVENT #ifdef CONFIG_LV2_ATOM unsigned short iAtomIns = pLv2Type->atomIns(); unsigned short iAtomOuts = pLv2Type->atomOuts(); if (iAtomIns > 0) m_piAtomIns = new unsigned long [iAtomIns]; if (iAtomOuts > 0) m_piAtomOuts = new unsigned long [iAtomOuts]; iAtomIns = iAtomOuts = 0; #endif // CONFIG_LV2_ATOM #ifdef CONFIG_LV2_CVPORT unsigned short iCVPortIns = pLv2Type->cvportIns(); unsigned short iCVPortOuts = pLv2Type->cvportOuts(); if (iCVPortIns > 0) m_piCVPortIns = new unsigned long [iCVPortIns]; if (iCVPortOuts > 0) m_piCVPortOuts = new unsigned long [iCVPortOuts]; iCVPortIns = iCVPortOuts = 0; #endif // CONFIG_LV2_CVPORT const bool bLatency = lilv_plugin_has_latency(plugin); const uint32_t iLatencyPort = (bLatency ? lilv_plugin_get_latency_port_index(plugin) : 0); const unsigned long iNumPorts = lilv_plugin_get_num_ports(plugin); for (unsigned long i = 0; i < iNumPorts; ++i) { const LilvPort *port = lilv_plugin_get_port_by_index(plugin, i); if (port) { if (lilv_port_is_a(plugin, port, g_lv2_input_class)) { if (lilv_port_is_a(plugin, port, g_lv2_audio_class)) m_piAudioIns[iAudioIns++] = i; else #ifdef CONFIG_LV2_EVENT if (lilv_port_is_a(plugin, port, g_lv2_event_class) || lilv_port_is_a(plugin, port, g_lv2_midi_class)) m_piEventIns[iEventIns++] = i; else #endif #ifdef CONFIG_LV2_ATOM if (lilv_port_is_a(plugin, port, g_lv2_atom_class)) m_piAtomIns[iAtomIns++] = i; else #endif #ifdef CONFIG_LV2_CVPORT if (lilv_port_is_a(plugin, port, g_lv2_cvport_class)) m_piCVPortIns[iCVPortIns++] = i; else #endif if (lilv_port_is_a(plugin, port, g_lv2_control_class)) addParam(new Param(this, i)); } else if (lilv_port_is_a(plugin, port, g_lv2_output_class)) { if (lilv_port_is_a(plugin, port, g_lv2_audio_class)) m_piAudioOuts[iAudioOuts++] = i; else #ifdef CONFIG_LV2_EVENT if (lilv_port_is_a(plugin, port, g_lv2_event_class) || lilv_port_is_a(plugin, port, g_lv2_midi_class)) m_piEventOuts[iEventOuts++] = i; else #endif #ifdef CONFIG_LV2_ATOM if (lilv_port_is_a(plugin, port, g_lv2_atom_class)) m_piAtomOuts[iAtomOuts++] = i; else #endif #ifdef CONFIG_LV2_CVPORT if (lilv_port_is_a(plugin, port, g_lv2_cvport_class)) m_piCVPortOuts[iCVPortOuts++] = i; else #endif if (lilv_port_is_a(plugin, port, g_lv2_control_class)) { m_piControlOuts[iControlOuts] = i; m_pfControlOuts[iControlOuts] = 0.0f; m_pfControlOutsLast[iControlOuts] = 0.0f; if (bLatency && iLatencyPort == i) m_pfLatency = &m_pfControlOuts[iControlOuts]; ++iControlOuts; } } } } #ifdef CONFIG_LV2_ATOM unsigned int iAtomInsCapacity = 0; if (iAtomIns > 0) { unsigned short iMidiAtomIns = 0; m_lv2_atom_buffer_ins = new LV2_Atom_Buffer * [iAtomIns]; for (unsigned short j = 0; j < iAtomIns; ++j) { unsigned int iMinBufferCapacity = 1024; const LilvPort *port = lilv_plugin_get_port_by_index(plugin, m_piAtomIns[j]); if (port) { LilvNode *minimum_size = lilv_port_get(plugin, port, g_lv2_minimum_size_prop); if (minimum_size) { if (lilv_node_is_int(minimum_size)) { const unsigned int iMinimumSize = lilv_node_as_int(minimum_size); if (iMinBufferCapacity < iMinimumSize) iMinBufferCapacity = iMinimumSize; } lilv_node_free(minimum_size); } if (pMidiManager && lilv_port_supports_event(plugin, port, g_lv2_midi_class)) { if (++iMidiAtomIns == 1) m_lv2_atom_midi_port_in = j; // First wins input. pMidiManager->lv2_atom_buffer_resize(iMinBufferCapacity); } #ifdef CONFIG_LV2_TIME_POSITION if (lilv_port_supports_event(plugin, port, g_lv2_time_position_class)) { m_lv2_time_position_enabled = true; m_lv2_time_position_port_in = j; qtractor_lv2_time_position_open(this); } #endif #ifdef CONFIG_LV2_PATCH if (lilv_port_supports_event(plugin, port, g_lv2_patch_message_class)) { m_lv2_patch_port_in = j; } #endif } m_lv2_atom_buffer_ins[j] = lv2_atom_buffer_new(iMinBufferCapacity, g_lv2_urids.atom_Chunk, g_lv2_urids.atom_Sequence, true); if (iAtomInsCapacity < iMinBufferCapacity) iAtomInsCapacity = iMinBufferCapacity; } } unsigned int iAtomOutsCapacity = 0; if (iAtomOuts > 0) { unsigned short iMidiAtomOuts = 0; m_lv2_atom_buffer_outs = new LV2_Atom_Buffer * [iAtomOuts]; for (unsigned short j = 0; j < iAtomOuts; ++j) { unsigned int iMinBufferCapacity = 1024; const LilvPort *port = lilv_plugin_get_port_by_index(plugin, m_piAtomOuts[j]); if (port) { LilvNode *minimum_size = lilv_port_get(plugin, port, g_lv2_minimum_size_prop); if (minimum_size) { if (lilv_node_is_int(minimum_size)) { const unsigned int iMinimumSize = lilv_node_as_int(minimum_size); if (iMinBufferCapacity < iMinimumSize) iMinBufferCapacity = iMinimumSize; } lilv_node_free(minimum_size); } if (pMidiManager && lilv_port_supports_event(plugin, port, g_lv2_midi_class)) { if (++iMidiAtomOuts == 1) m_lv2_atom_midi_port_out = j; // First wins output. pMidiManager->lv2_atom_buffer_resize(iMinBufferCapacity); } } m_lv2_atom_buffer_outs[j] = lv2_atom_buffer_new(iMinBufferCapacity, g_lv2_urids.atom_Chunk, g_lv2_urids.atom_Sequence, false); if (iAtomOutsCapacity < iMinBufferCapacity) iAtomOutsCapacity = iMinBufferCapacity; } } #ifdef CONFIG_LV2_UI m_ui_events = ::jack_ringbuffer_create(iAtomInsCapacity); m_plugin_events = ::jack_ringbuffer_create(iAtomOutsCapacity); #endif #endif // CONFIG_LV2_ATOM #ifdef CONFIG_LV2_PRESETS LilvNode *label_uri = lilv_new_uri(g_lv2_world, LILV_NS_RDFS "label"); LilvNode *preset_uri = lilv_new_uri(g_lv2_world, LV2_PRESETS__Preset); LilvNodes *presets = lilv_plugin_get_related(lv2_plugin(), preset_uri); if (presets) { LILV_FOREACH(nodes, iter, presets) { const LilvNode *preset = lilv_nodes_get(presets, iter); lilv_world_load_resource(g_lv2_world, preset); LilvNodes *labels = lilv_world_find_nodes( g_lv2_world, preset, label_uri, nullptr); if (labels) { const LilvNode *label = lilv_nodes_get_first(labels); const QString sPreset(lilv_node_as_string(label)); const QString sUri(lilv_node_as_string(preset)); m_lv2_presets.insert(sPreset, sUri); lilv_nodes_free(labels); } } lilv_nodes_free(presets); } lilv_node_free(preset_uri); lilv_node_free(label_uri); #endif // CONFIG_LV2_PRESETS #ifdef CONFIG_LV2_TIME // Set up time-pos designated port indexes, if any... for (int i = 0; i < int(qtractorLv2Time::numOfMembers); ++i) { qtractorLv2Time& member = g_lv2_time[i]; if (member.node) { const LilvPort *port = lilv_plugin_get_port_by_designation( plugin, g_lv2_input_class, member.node); if (port) { const unsigned long iIndex = lilv_port_get_index(plugin, port); Param *pParam = static_cast ( qtractorPlugin::findParam(iIndex)); if (pParam) { m_lv2_time_ports.insert(iIndex, i); member.params->append(pParam); } } } } // Add to global running LV2 Time/position ref-count... if (!m_lv2_time_ports.isEmpty()) ++g_lv2_time_refcount; #endif // CONFIG_LV2_TIME #ifdef CONFIG_LV2_PATCH lv2_patch_properties(LV2_PATCH__writable); lv2_patch_properties(LV2_PATCH__readable); #endif // FIXME: instantiate each instance properly... qtractorLv2Plugin::setChannels(channels()); } } // Destructor. qtractorLv2Plugin::~qtractorLv2Plugin (void) { // Cleanup all plugin instances... cleanup(); // setChannels(0); // Clear programs cache. clearInstruments(); #ifdef CONFIG_LV2_TIME // Remove from global running LV2 Time/position ref-count... if (!m_lv2_time_ports.isEmpty()) { --g_lv2_time_refcount; // Unsubscribe mapped params... QHash::ConstIterator iter = m_lv2_time_ports.constBegin(); const QHash::ConstIterator& iter_end = m_lv2_time_ports.constEnd(); for ( ; iter != iter_end; ++iter) { Param *pParam = static_cast ( qtractorPlugin::findParam(iter.key())); if (pParam) g_lv2_time[iter.value()].params->removeAll(pParam); } } #ifdef CONFIG_LV2_TIME_POSITION if (m_lv2_time_position_enabled) qtractor_lv2_time_position_close(this); #endif #endif // CONFIG_LV2_TIME #ifdef CONFIG_LV2_CVPORT if (m_piCVPortOuts) delete [] m_piCVPortOuts; if (m_piCVPortIns) delete [] m_piCVPortIns; #endif // CONFIG_LV2_CVPORT #ifdef CONFIG_LV2_ATOM qtractorLv2PluginType *pLv2Type = static_cast (type()); const unsigned short iAtomOuts = (pLv2Type ? pLv2Type->atomOuts() : 0); if (m_lv2_atom_buffer_outs) { for (unsigned short j = 0; j < iAtomOuts; ++j) lv2_atom_buffer_free(m_lv2_atom_buffer_outs[j]); delete [] m_lv2_atom_buffer_outs; } const unsigned short iAtomIns = (pLv2Type ? pLv2Type->atomIns() : 0); if (m_lv2_atom_buffer_ins) { for (unsigned short j = 0; j < iAtomIns; ++j) lv2_atom_buffer_free(m_lv2_atom_buffer_ins[j]); delete [] m_lv2_atom_buffer_ins; } if (m_piAtomOuts) delete [] m_piAtomOuts; if (m_piAtomIns) delete [] m_piAtomIns; #endif // CONFIG_LV2_ATOM #ifdef CONFIG_LV2_EVENT if (m_piEventOuts) delete [] m_piEventOuts; if (m_piEventIns) delete [] m_piEventIns; #endif // CONFIG_LV2_EVENT #ifdef CONFIG_LV2_UI if (m_plugin_events) ::jack_ringbuffer_free(m_plugin_events); if (m_ui_events) ::jack_ringbuffer_free(m_ui_events); #endif if (m_piAudioOuts) delete [] m_piAudioOuts; if (m_piAudioIns) delete [] m_piAudioIns; if (m_piControlOuts) delete [] m_piControlOuts; if (m_pfControlOuts) delete [] m_pfControlOuts; if (m_pfControlOutsLast) delete [] m_pfControlOutsLast; if (m_pfIDummy) delete [] m_pfIDummy; if (m_pfODummy) delete [] m_pfODummy; if (m_lv2_features) delete [] m_lv2_features; } // Channel/instance number accessors. void qtractorLv2Plugin::setChannels ( unsigned short iChannels ) { // Check our type... qtractorLv2PluginType *pLv2Type = static_cast (type()); if (pLv2Type == nullptr) return; // Estimate the (new) number of instances... const unsigned short iOldInstances = instances(); unsigned short iInstances = 0; if (iChannels > 0) { iInstances = pLv2Type->instances(iChannels, list()->isMidi()); // Now see if instance and channel count changed anyhow... if (iInstances == iOldInstances && iChannels == channels()) return; } const LilvPlugin *plugin = pLv2Type->lv2_plugin(); if (plugin == nullptr) return; // Gotta go for a while... const bool bActivated = isActivated(); setChannelsActivated(iChannels, false); // Set new instance number... setInstances(iInstances); // Close old instances, all the way... const int iLv2Plugin = g_lv2Plugins.indexOf(this); if (iLv2Plugin >= 0) g_lv2Plugins.removeAt(iLv2Plugin); #ifdef CONFIG_LV2_WORKER if (m_lv2_worker) { delete m_lv2_worker; m_lv2_worker = nullptr; } #endif if (m_ppInstances) { for (unsigned short i = 0; i < iOldInstances; ++i) { LilvInstance *instance = m_ppInstances[i]; if (instance) lilv_instance_free(instance); } delete [] m_ppInstances; m_ppInstances = nullptr; } // Bail out, if none are about to be created... if (iInstances < 1) { setChannelsActivated(iChannels, bActivated); return; } #ifdef CONFIG_DEBUG qDebug("qtractorLv2Plugin[%p]::setChannels(%u) instances=%u", this, iChannels, iInstances); #endif // Allocate the dummy audio I/O buffers... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; const unsigned int iSampleRate = pAudioEngine->sampleRate(); const unsigned int iBufferSizeEx = pAudioEngine->bufferSizeEx(); const unsigned short iAudioIns = pLv2Type->audioIns(); const unsigned short iAudioOuts = pLv2Type->audioOuts(); if (iChannels < iAudioIns #ifdef CONFIG_LV2_CVPORT || pLv2Type->cvportIns() > 0 #endif ) { if (m_pfIDummy) delete [] m_pfIDummy; m_pfIDummy = new float [iBufferSizeEx]; ::memset(m_pfIDummy, 0, iBufferSizeEx * sizeof(float)); } if (iChannels < iAudioOuts #ifdef CONFIG_LV2_CVPORT || pLv2Type->cvportOuts() > 0 #endif ) { if (m_pfODummy) delete [] m_pfODummy; m_pfODummy = new float [iBufferSizeEx]; // ::memset(m_pfODummy, 0, iBufferSizeEx * sizeof(float)); } #ifdef CONFIG_LV2_WORKER if (lilv_plugin_has_feature(plugin, g_lv2_worker_schedule_hint)) m_lv2_worker = new qtractorLv2Worker(this, m_lv2_features); #endif LV2_Feature **features = m_lv2_features; #ifdef CONFIG_LV2_WORKER if (m_lv2_worker) features = m_lv2_worker->lv2_features(); #endif // We'll need output control (not dummy anymore) port indexes... const unsigned short iControlOuts = pLv2Type->controlOuts(); // But we'll need dummy CV ports and indexes... #ifdef CONFIG_LV2_CVPORT const unsigned short iCVPortIns = pLv2Type->cvportIns(); const unsigned short iCVPortOuts = pLv2Type->cvportOuts(); #endif unsigned short i, j; // Allocate new instances... m_ppInstances = new LilvInstance * [iInstances]; for (i = 0; i < iInstances; ++i) { // Instantiate them properly first... LilvInstance *instance = lilv_plugin_instantiate(plugin, iSampleRate, features); if (instance) { // (Dis)connect all ports... const unsigned long iNumPorts = lilv_plugin_get_num_ports(plugin); for (unsigned long k = 0; k < iNumPorts; ++k) lilv_instance_connect_port(instance, k, nullptr); // Connect all existing input control ports... const qtractorPlugin::Params& params = qtractorPlugin::params(); qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator& param_end = params.constEnd(); for ( ; param != param_end; ++param) { qtractorPlugin::Param *pParam = param.value(); lilv_instance_connect_port(instance, pParam->index(), pParam->subject()->data()); } // Connect all existing output control ports... for (j = 0; j < iControlOuts; ++j) { lilv_instance_connect_port(instance, m_piControlOuts[j], &m_pfControlOuts[j]); } // Connect all dummy input ports... if (m_pfIDummy) for (j = iChannels; j < iAudioIns; ++j) { lilv_instance_connect_port(instance, m_piAudioIns[j], m_pfIDummy); // dummy input port! } // Connect all dummy output ports... if (m_pfODummy) for (j = iChannels; j < iAudioOuts; ++j) { lilv_instance_connect_port(instance, m_piAudioOuts[j], m_pfODummy); // dummy input port! } #ifdef CONFIG_LV2_CVPORT // Connect all existing CVPort's to a dummy port... for (unsigned short j = 0; j < iCVPortIns; ++j) { lilv_instance_connect_port(instance, m_piCVPortIns[j], m_pfIDummy); } for (unsigned short j = 0; j < iCVPortOuts; ++j) { lilv_instance_connect_port(instance, m_piCVPortOuts[j], m_pfODummy); } #endif #if 0//def CONFIG_LV2_TIME // Connect time-pos designated ports, if any... QHash::ConstIterator iter = m_lv2_time_ports.constBegin(); const QHash::ConstIterator& iter_end = m_lv2_time_ports.constEnd(); for ( ; iter != iter_end; ++iter) { lilv_instance_connect_port(instance, iter.key(), &(g_lv2_time[iter.value()].data)); } #endif } #ifdef CONFIG_DEBUG qDebug("qtractorLv2Plugin[%p]::setChannels(%u) instance[%u]=%p", this, iChannels, i, instance); #endif // This is it... m_ppInstances[i] = instance; } // Finally add it to the LV2 plugin roster... g_lv2Plugins.append(this); #ifdef CONFIG_LV2_STATE // Load default state as needed... if (lilv_plugin_has_feature(plugin, g_lv2_state_load_default_hint)) lv2_state_load_default(); #endif // (Re)issue all configuration as needed... realizeConfigs(); realizeValues(); // But won't need it anymore. releaseConfigs(); releaseValues(); // Initialize programs cache. updateInstruments(); // (Re)activate instance if necessary... setChannelsActivated(iChannels, bActivated); } // Specific accessors. LilvPlugin *qtractorLv2Plugin::lv2_plugin (void) const { qtractorLv2PluginType *pLv2Type = static_cast (type()); return (pLv2Type ? pLv2Type->lv2_plugin() : nullptr); } LilvInstance *qtractorLv2Plugin::lv2_instance ( unsigned short iInstance ) const { return (m_ppInstances ? m_ppInstances[iInstance] : nullptr); } LV2_Handle qtractorLv2Plugin::lv2_handle ( unsigned short iInstance ) const { const LilvInstance *instance = lv2_instance(iInstance); return (instance ? lilv_instance_get_handle(instance) : nullptr); } // Do the actual activation. void qtractorLv2Plugin::activate (void) { if (m_ppInstances) { const unsigned short iInstances = instances(); for (unsigned short i = 0; i < iInstances; ++i) { LilvInstance *instance = m_ppInstances[i]; if (instance) lilv_instance_activate(instance); } } } // Do the actual deactivation. void qtractorLv2Plugin::deactivate (void) { if (m_ppInstances) { const unsigned short iInstances = instances(); for (unsigned short i = 0; i < iInstances; ++i) { LilvInstance *instance = m_ppInstances[i]; if (instance) lilv_instance_deactivate(instance); } } } // The main plugin processing procedure. void qtractorLv2Plugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { if (m_ppInstances == nullptr) return; const LilvPlugin *plugin = lv2_plugin(); if (plugin == nullptr) return; #if defined(CONFIG_LV2_EVENT) || defined(CONFIG_LV2_ATOM) qtractorMidiManager *pMidiManager = nullptr; const unsigned short iMidiIns = midiIns(); const unsigned short iMidiOuts = midiOuts(); if ((iMidiIns + iMidiOuts) > 0) pMidiManager = list()->midiManager(); qtractorLv2PluginType *pLv2Type = static_cast (type()); #ifdef CONFIG_LV2_EVENT const unsigned short iEventIns = pLv2Type->eventIns(); const unsigned short iEventOuts = pLv2Type->eventOuts(); #endif #ifdef CONFIG_LV2_ATOM const unsigned short iAtomIns = pLv2Type->atomIns(); const unsigned short iAtomOuts = pLv2Type->atomOuts(); #endif #endif // We'll cross channels over instances... const unsigned short iInstances = instances(); const unsigned short iChannels = channels(); const unsigned short iAudioIns = audioIns(); const unsigned short iAudioOuts = audioOuts(); unsigned short iIChannel = 0; unsigned short iOChannel = 0; unsigned short i, j; // For each plugin instance... for (i = 0; i < iInstances; ++i) { LilvInstance *instance = m_ppInstances[i]; if (instance) { // For each instance audio input port... for (j = 0; j < iAudioIns && iIChannel < iChannels; ++j) { lilv_instance_connect_port(instance, m_piAudioIns[j], ppIBuffer[iIChannel++]); } // For each instance audio output port... for (j = 0; j < iAudioOuts && iOChannel < iChannels; ++j) { lilv_instance_connect_port(instance, m_piAudioOuts[j], ppOBuffer[iOChannel++]); } #ifdef CONFIG_LV2_EVENT // Connect all existing input event/MIDI ports... if (pMidiManager) { for (j = 0; j < iEventIns; ++j) { lilv_instance_connect_port(instance, m_piEventIns[j], pMidiManager->lv2_events_in()); } for (j = 0; j < iEventOuts; ++j) { lilv_instance_connect_port(instance, m_piEventOuts[j], pMidiManager->lv2_events_out()); } } #endif #ifdef CONFIG_LV2_ATOM // Connect all existing input atom/MIDI ports... for (j = 0; j < iAtomIns; ++j) { LV2_Atom_Buffer *abuf = m_lv2_atom_buffer_ins[j]; if (pMidiManager && j == m_lv2_atom_midi_port_in) abuf = pMidiManager->lv2_atom_buffer_in(); else lv2_atom_buffer_reset(abuf, true); lilv_instance_connect_port(instance, m_piAtomIns[j], &abuf->aseq); #ifdef CONFIG_LV2_TIME_POSITION // Time position has changed, provide an update... if (m_lv2_time_position_changed > 0 && j == m_lv2_time_position_port_in) { LV2_Atom_Buffer_Iterator aiter; lv2_atom_buffer_end(&aiter, abuf); const LV2_Atom *atom = (const LV2_Atom *) g_lv2_time_position_buffer; lv2_atom_buffer_write(&aiter, 0, 0, atom->type, atom->size, (const uint8_t *) LV2_ATOM_BODY(atom)); m_lv2_time_position_changed = 0; } #endif #ifdef CONFIG_LV2_PATCH // Plugin state has changed, request an update... if (m_lv2_patch_changed > 0 && j == m_lv2_patch_port_in) { LV2_Atom_Buffer_Iterator aiter; lv2_atom_buffer_end(&aiter, abuf); LV2_Atom_Object obj; obj.atom.size = sizeof(LV2_Atom_Object_Body); obj.atom.type = g_lv2_urids.atom_Object; obj.body.id = 0; obj.body.otype = g_lv2_urids.patch_Get; lv2_atom_buffer_write(&aiter, 0, 0, obj.atom.type, obj.atom.size, (const uint8_t *) LV2_ATOM_BODY(&obj)); m_lv2_patch_changed = 0; } #endif } for (j = 0; j < iAtomOuts; ++j) { LV2_Atom_Buffer *abuf = m_lv2_atom_buffer_outs[j]; if (pMidiManager && j == m_lv2_atom_midi_port_out) abuf = pMidiManager->lv2_atom_buffer_out(); else lv2_atom_buffer_reset(abuf, false); lilv_instance_connect_port(instance, m_piAtomOuts[j], &abuf->aseq); } #ifdef CONFIG_LV2_UI // Read and apply control changes, eventually from an UI... uint32_t read_space = ::jack_ringbuffer_read_space(m_ui_events); while (read_space > 0) { ControlEvent ev; ::jack_ringbuffer_read(m_ui_events, (char *) &ev, sizeof(ev)); char ebuf[ev.size]; if (::jack_ringbuffer_read(m_ui_events, ebuf, ev.size) < ev.size) break; if (ev.protocol == g_lv2_urids.atom_eventTransfer) { for (j = 0; j < iAtomIns; ++j) { if (m_piAtomIns[j] == ev.index) { LV2_Atom_Buffer *abuf = m_lv2_atom_buffer_ins[j]; if (pMidiManager && j == m_lv2_atom_midi_port_in) abuf = pMidiManager->lv2_atom_buffer_in(); LV2_Atom_Buffer_Iterator aiter; lv2_atom_buffer_end(&aiter, abuf); const LV2_Atom *atom = (const LV2_Atom *) ebuf; lv2_atom_buffer_write(&aiter, nframes, 0, atom->type, atom->size, (const uint8_t *) LV2_ATOM_BODY(atom)); break; } } } read_space -= sizeof(ev) + ev.size; } #endif // CONFIG_LV2_UI #endif // CONFIG_LV2_ATOM // Make it run... lilv_instance_run(instance, nframes); // Wrap dangling output channels?... for (j = iOChannel; j < iChannels; ++j) ::memset(ppOBuffer[j], 0, nframes * sizeof(float)); } } #ifdef CONFIG_LV2_WORKER if (m_lv2_worker) m_lv2_worker->commit(); #endif #ifdef CONFIG_LV2_ATOM #ifdef CONFIG_LV2_UI for (j = 0; j < iAtomOuts; ++j) { LV2_Atom_Buffer *abuf; if (pMidiManager && j == m_lv2_atom_midi_port_out) abuf = pMidiManager->lv2_atom_buffer_out(); else abuf = m_lv2_atom_buffer_outs[j]; LV2_Atom_Buffer_Iterator aiter; lv2_atom_buffer_begin(&aiter, abuf); while (true) { uint8_t *data; LV2_Atom_Event *pLv2AtomEvent = lv2_atom_buffer_get(&aiter, &data); if (pLv2AtomEvent == nullptr) break; //if (j > 0 || pLv2AtomEvent->body.type != QTRACTOR_LV2_MIDI_EVENT_ID) { char buf[sizeof(ControlEvent) + sizeof(LV2_Atom)]; const uint32_t type = pLv2AtomEvent->body.type; const uint32_t size = pLv2AtomEvent->body.size; ControlEvent *ev = (ControlEvent *) buf; ev->index = m_piAtomOuts[j]; ev->protocol = g_lv2_urids.atom_eventTransfer; ev->size = sizeof(LV2_Atom) + size; LV2_Atom *atom = (LV2_Atom *) ev->body; atom->type = type; atom->size = size; if (::jack_ringbuffer_write_space(m_plugin_events) < sizeof(buf) + size) break; ::jack_ringbuffer_write(m_plugin_events, (const char *) buf, sizeof(buf)); ::jack_ringbuffer_write(m_plugin_events, (const char *) data, size); //} lv2_atom_buffer_increment(&aiter); } } #endif // CONFIG_LV2_UI #endif // CONFIG_LV2_ATOM #ifdef CONFIG_LV2_EVENT if (pMidiManager && iEventOuts > 0) { if (iMidiOuts > 0) pMidiManager->lv2_events_swap(); else pMidiManager->resetOutputBuffers(); } #endif #ifdef CONFIG_LV2_ATOM if (pMidiManager && iAtomOuts > 0) { if (iMidiOuts > 0) pMidiManager->lv2_atom_buffer_swap(); else pMidiManager->resetOutputBuffers(); } #endif } #ifdef CONFIG_LV2_UI // Open editor. void qtractorLv2Plugin::openEditor ( QWidget *pParent ) { if (m_lv2_ui) { setEditorVisible(true); return; } qtractorLv2PluginType *pLv2Type = static_cast (type()); if (pLv2Type == nullptr) return; // Check the UI inventory... m_lv2_uis = lilv_plugin_get_uis(pLv2Type->lv2_plugin()); if (m_lv2_uis == nullptr) return; // Make sure native UIs gets top priority... struct ui_key { ui_key (int type = LV2_UI_TYPE_NONE) { if (type >= LV2_UI_TYPE_NATIVE) ukey = ((type - LV2_UI_TYPE_NATIVE) << 1); else ukey = (type << 1) | 1; } int ui_type () const { int type = (ukey >> 1); if ((ukey & 1) == 0) type += LV2_UI_TYPE_NATIVE; return type; } bool operator< (const ui_key& key) const { return (ukey < key.ukey); } uint ukey; }; QMap ui_map; LILV_FOREACH(uis, iter, m_lv2_uis) { LilvUI *ui = const_cast (lilv_uis_get(m_lv2_uis, iter)); #ifdef CONFIG_LV2_EXTERNAL_UI if (lilv_ui_is_a(ui, g_lv2_external_ui_class) #ifdef LV2_EXTERNAL_UI_DEPRECATED_URI || lilv_ui_is_a(ui, g_lv2_external_ui_deprecated_class) #endif ) ui_map.insert(ui_key(LV2_UI_TYPE_EXTERNAL), ui); else #endif if (lilv_ui_is_a(ui, g_lv2_x11_ui_class)) { #ifdef CONFIG_LIBSUIL ui_map.insert(ui_key(LV2_UI_TYPE_X11), ui); #endif #ifdef CONFIG_LV2_UI_X11 ui_map.insert(ui_key(LV2_UI_TYPE_X11_NATIVE), ui); #endif } else if (lilv_ui_is_a(ui, g_lv2_gtk_ui_class)) { #ifdef CONFIG_LIBSUIL ui_map.insert(ui_key(LV2_UI_TYPE_GTK), ui); #endif #ifdef CONFIG_LV2_UI_GTK2 ui_map.insert(ui_key(LV2_UI_TYPE_GTK_NATIVE), ui); #endif } #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) else if (lilv_ui_is_a(ui, g_lv2_qt4_ui_class)) ui_map.insert(ui_key(LV2_UI_TYPE_QT4), ui); #else #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) else if (lilv_ui_is_a(ui, g_lv2_qt5_ui_class)) ui_map.insert(ui_key(LV2_UI_TYPE_QT5), ui); #endif #endif #ifdef CONFIG_LV2_UI_SHOW else if (pLv2Type->lv2_ui_show_interface(ui)) ui_map.insert(ui_key(LV2_UI_TYPE_OTHER), ui); #endif } const QMap::ConstIterator& ui_begin = ui_map.constBegin(); const QMap::ConstIterator& ui_end = ui_map.constEnd(); QMap::ConstIterator ui_iter = ui_begin; int iEditorType = editorType(); if (iEditorType > 0) { // Must be != LV2_UI_TYPE_NONE. ui_iter = ui_map.constFind(ui_key(iEditorType)); if (ui_iter == ui_end) { iEditorType = -1; ui_iter = ui_begin; } } qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bQueryEditorType && (ui_map.count() > 1) && (0 >= iEditorType || ui_iter == ui_end)) { const QString& sTitle = title(); const QString& sText = QObject::tr("Select plug-in's editor (GUI):"); qtractorMessageBox mbox(qtractorMainForm::getInstance()); mbox.setIcon(QMessageBox::Question); mbox.setWindowTitle(sTitle); mbox.setText(sText); mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); QButtonGroup group; for (ui_iter = ui_begin; ui_iter != ui_end; ++ui_iter) { const int ui_type = ui_iter.key().ui_type(); QRadioButton *pRadioButton; switch (ui_type) { case LV2_UI_TYPE_EXTERNAL: pRadioButton = new QRadioButton(QObject::tr("External")); break; case LV2_UI_TYPE_X11: pRadioButton = new QRadioButton(QObject::tr("X11")); break; case LV2_UI_TYPE_X11_NATIVE: pRadioButton = new QRadioButton(QObject::tr("X11 (native)")); break; case LV2_UI_TYPE_GTK: pRadioButton = new QRadioButton(QObject::tr("Gtk2")); break; case LV2_UI_TYPE_GTK_NATIVE: pRadioButton = new QRadioButton(QObject::tr("Gtk2 (native)")); break; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) case LV2_UI_TYPE_QT4: pRadioButton = new QRadioButton(QObject::tr("Qt4")); break; #else #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) case LV2_UI_TYPE_QT5: pRadioButton = new QRadioButton(QObject::tr("Qt5")); break; #endif #endif case LV2_UI_TYPE_OTHER: default: pRadioButton = new QRadioButton(QObject::tr("Other")); break; } pRadioButton->setChecked(ui_iter == ui_begin); group.addButton(pRadioButton, ui_type); mbox.addCustomButton(pRadioButton); } mbox.addCustomSpacer(); QCheckBox cbox(QObject::tr("Don't ask this again")); cbox.setChecked(false); cbox.blockSignals(true); mbox.addButton(&cbox, QMessageBox::ActionRole); if (mbox.exec() == QMessageBox::Ok) ui_iter = ui_map.constFind(ui_key(group.checkedId())); else ui_iter = ui_end; if (ui_iter != ui_end && cbox.isChecked()) setEditorType(ui_iter.key().ui_type()); } if (ui_iter == ui_end) { lilv_uis_free(m_lv2_uis); m_lv2_uis = nullptr; return; } // Tell the world we'll (maybe) take some time... qtractorPluginList::WaitCursor waiting; m_lv2_ui_type = ui_iter.key().ui_type(); m_lv2_ui = ui_iter.value(); #ifdef CONFIG_LIBSUIL m_suil_support = bool(m_lv2_ui_type < LV2_UI_TYPE_NATIVE); #endif if (m_lv2_ui_type >= LV2_UI_TYPE_NATIVE) m_lv2_ui_type -= LV2_UI_TYPE_NATIVE; const char *ui_type_uri = nullptr; switch (m_lv2_ui_type) { #ifdef CONFIG_LV2_EXTERNAL_UI case LV2_UI_TYPE_EXTERNAL: #ifdef LV2_EXTERNAL_UI_DEPRECATED_URI if (lilv_ui_is_a(m_lv2_ui, g_lv2_external_ui_deprecated_class)) ui_type_uri = LV2_EXTERNAL_UI_DEPRECATED_URI; else #endif ui_type_uri = LV2_EXTERNAL_UI__Widget; break; #endif case LV2_UI_TYPE_X11: ui_type_uri = LV2_UI__X11UI; break; case LV2_UI_TYPE_GTK: ui_type_uri = LV2_UI__GtkUI; break; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) case LV2_UI_TYPE_QT4: ui_type_uri = LV2_UI__Qt4UI; break; #else #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) case LV2_UI_TYPE_QT5: ui_type_uri = LV2_UI__Qt5UI; break; #endif #endif default: break; } #ifdef CONFIG_DEBUG qDebug("qtractorLv2Plugin[%p]::openEditor(\"%s\")", this, ui_type_uri); #endif // Whether LV2 UI no-user-resize feature is being requested. const LilvNode *ui_uri_node = lilv_ui_get_uri(m_lv2_ui); lilv_world_load_resource(g_lv2_world, ui_uri_node); LilvNode *core_optional_feature_uri_node = lilv_new_uri(g_lv2_world, LV2_CORE__optionalFeature); LilvNode *ui_no_user_resize_uri_node = lilv_new_uri(g_lv2_world, LV2_UI__noUserResize); m_lv2_ui_no_user_resize = lilv_world_ask(g_lv2_world, ui_uri_node, g_lv2_extension_data_hint, ui_no_user_resize_uri_node) ||lilv_world_ask(g_lv2_world, ui_uri_node, core_optional_feature_uri_node, ui_no_user_resize_uri_node); lilv_node_free(ui_no_user_resize_uri_node); lilv_node_free(core_optional_feature_uri_node); #ifdef CONFIG_LILV_WORLD_UNLOAD_RESOURCE lilv_world_unload_resource(g_lv2_world, ui_uri_node); #endif // What style do we create tool childs? Qt::WindowFlags wflags = Qt::Window; #if 0//QTRACTOR_LV2_EDITOR_TOOL qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bKeepToolsOnTop) { wflags |= Qt::Tool; // wflags |= Qt::WindowStaysOnTopHint; // Make sure it has a parent... if (pParent == nullptr) pParent = qtractorMainForm::getInstance(); } #endif const char *ui_host_uri = LV2_UI_HOST_URI; const char *plugin_uri = lilv_node_as_uri(lilv_plugin_get_uri(pLv2Type->lv2_plugin())); const char *ui_uri = lilv_node_as_uri(ui_uri_node); const char *ui_bundle_uri = lilv_node_as_uri(lilv_ui_get_bundle_uri(m_lv2_ui)); const char *ui_binary_uri = lilv_node_as_uri(lilv_ui_get_binary_uri(m_lv2_ui)); #ifdef CONFIG_LILV_FILE_URI_PARSE const char *ui_bundle_path = lilv_file_uri_parse(ui_bundle_uri, nullptr); const char *ui_binary_path = lilv_file_uri_parse(ui_binary_uri, nullptr); #else const char *ui_bundle_path = lilv_uri_to_path(ui_bundle_uri); const char *ui_binary_path = lilv_uri_to_path(ui_binary_uri); #endif // Do try to instantiate the UI... const bool ui_instantiate = lv2_ui_instantiate( ui_host_uri, plugin_uri, ui_uri, ui_type_uri, ui_bundle_path, ui_binary_path, pParent, wflags); #ifdef CONFIG_LILV_FILE_URI_PARSE lilv_free((void *) ui_binary_path); lilv_free((void *) ui_bundle_path); #endif // Did we failed miserably? if (!ui_instantiate) return; #ifdef CONFIG_LIBSUIL const bool ui_supported = (m_suil_instance != nullptr); #else const bool ui_supported = false; #endif const qtractorPlugin::Params& params = qtractorPlugin::params(); qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator& param_end = params.constEnd(); for ( ; param != param_end; ++param) { qtractorPlugin::Param *pParam = param.value(); const float fValue = pParam->value(); lv2_ui_port_event(pParam->index(), sizeof(float), 0, &fValue); } const unsigned long iControlOuts = pLv2Type->controlOuts(); for (unsigned long j = 0; j < iControlOuts; ++j) { lv2_ui_port_event(m_piControlOuts[j], sizeof(float), 0, &m_pfControlOuts[j]); } #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef CONFIG_LV2_UI_X11 if (!ui_supported && m_lv2_ui_widget && m_pQtWidget && m_lv2_ui_type == LV2_UI_TYPE_X11) { // Initialize widget event filter... m_pQtFilter = new EventFilter(this, m_pQtWidget); // m_bQtDelete = true; // LV2 UI resize control... QSize size = m_pQtWidget->sizeHint(); #ifdef CONFIG_LV2_UI_X11 qtractor_lv2_ui_size_hints(WId(m_lv2_ui_widget), size); #endif if (!size.isValid() || size.isNull()) size = m_pQtWidget->size(); if (size.isValid() && !size.isNull()) { if (m_lv2_ui_no_user_resize) m_pQtWidget->setFixedSize(size); else m_pQtWidget->setMinimumSize(size); lv2_ui_resize(size); } // m_pQtWidget->show(); } else #endif // CONFIG_LV2_UI_X11 #ifdef CONFIG_LV2_UI_GTK2 if (!ui_supported && m_lv2_ui_widget && m_lv2_ui_type == LV2_UI_TYPE_GTK) { // Initialize GTK+ framework (one time only)... qtractorLv2Gtk2Plugin::init_main(); // Create embeddable native window... GtkWidget *pGtkWidget = static_cast (m_lv2_ui_widget); GtkWidget *pGtkWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); // GtkWidget *pGtkWindow = gtk_plug_new(0); gtk_window_set_resizable(GTK_WINDOW(pGtkWindow), 0); // Add plugin widget into our new window container... gtk_container_add(GTK_CONTAINER(pGtkWindow), pGtkWidget); gtk_widget_show_all(pGtkWindow); // Embed native GTK+ window into a Qt widget... const WId wid = GDK_WINDOW_XID(gtk_widget_get_window(pGtkWindow)); // const WId wid = gtk_plug_get_id((GtkPlug *) pGtkWindow); QWindow *pQtWindow = QWindow::fromWinId(wid); // Create the new parent frame... QWidget *pQtWidget = new QWidget(pParent, wflags); pQtWidget->setAttribute(Qt::WA_QuitOnClose, false); QWidget *pQtContainer = QWidget::createWindowContainer(pQtWindow, pQtWidget); QVBoxLayout *pVBoxLayout = new QVBoxLayout(); pVBoxLayout->setContentsMargins(0, 0, 0, 0); pVBoxLayout->setSpacing(0); pVBoxLayout->addWidget(pQtContainer); pQtWidget->setLayout(pVBoxLayout); // Get initial window size... GtkAllocation alloc; gtk_widget_get_allocation(pGtkWidget, &alloc); pQtWidget->resize(alloc.width, alloc.height); // Set native GTK+ window size callbacks... g_signal_connect(G_OBJECT(pGtkWindow), "size-request", G_CALLBACK(qtractor_lv2_ui_gtk2_on_size_request), pQtWidget); g_signal_connect(G_OBJECT(pGtkWindow), "size-allocate", G_CALLBACK(qtractor_lv2_ui_gtk2_on_size_allocate), pQtWidget); m_pGtkWindow = pGtkWindow; m_pQtWindow = pQtWindow; // done. m_pQtWidget = pQtWidget; m_pQtFilter = new EventFilter(this, m_pQtWidget); m_bQtDelete = true; // owned! // LV2 UI resize control... lv2_ui_resize(QSize(alloc.width, alloc.height)); // m_pQtWidget->show(); } else #endif // CONFIG_LV2_UI_GTK2 #endif if (ui_supported && m_lv2_ui_widget && m_lv2_ui_type != LV2_UI_TYPE_EXTERNAL) { m_pQtWidget = static_cast (m_lv2_ui_widget); m_pQtFilter = new EventFilter(this, m_pQtWidget); m_bQtDelete = false; // LV2 UI resize control... QSize size = m_pQtWidget->sizeHint(); if (!size.isValid() || size.isNull()) size = m_pQtWidget->size(); if (size.isValid() && !size.isNull()) { if (m_lv2_ui_no_user_resize) m_pQtWidget->setFixedSize(size); else m_pQtWidget->setMinimumSize(size); lv2_ui_resize(size); } // m_pQtWidget->show(); } else { m_pQtWidget = nullptr; m_pQtFilter = nullptr; m_bQtDelete = false; } #ifdef CONFIG_LV2_UI_IDLE if (m_lv2_ui_type != LV2_UI_TYPE_EXTERNAL) { m_lv2_ui_idle_interface = (const LV2UI_Idle_Interface *) lv2_ui_extension_data(LV2_UI__idleInterface); } else { m_lv2_ui_idle_interface = nullptr; } #endif // CONFIG_LV2_UI_IDLE #ifdef CONFIG_LV2_UI_SHOW if (m_pQtWidget == nullptr && m_lv2_ui_type != LV2_UI_TYPE_EXTERNAL) { m_lv2_ui_show_interface = (const LV2UI_Show_Interface *) lv2_ui_extension_data(LV2_UI__showInterface); } else { m_lv2_ui_show_interface = nullptr; } #endif // CONFIG_LV2_UI_SHOW updateEditorTitle(); setEditorVisible(true); loadEditorPos(); // idleEditor(); } // Close editor. void qtractorLv2Plugin::closeEditor (void) { if (m_lv2_ui == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorLv2Plugin[%p]::closeEditor()", this); #endif setEditorVisible(false); m_ui_params.clear(); #ifdef CONFIG_LV2_UI_TOUCH m_ui_params_touch.clear(); #endif m_port_events.clear(); #ifdef CONFIG_LV2_UI_SHOW m_lv2_ui_show_interface = nullptr; #endif #ifdef CONFIG_LV2_UI_IDLE m_lv2_ui_idle_interface = nullptr; #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef CONFIG_LV2_UI_GTK2 if (m_pQtWindow) { m_pQtWindow->setParent(nullptr); delete m_pQtWindow; m_pQtWindow = nullptr; } if (m_pGtkWindow) { gtk_widget_destroy(m_pGtkWindow); m_pGtkWindow = nullptr; } qtractorLv2Gtk2Plugin::exit_main(); #endif // CONFIG_LV2_UI_GTK2 #endif if (m_pQtWidget) { if (m_bQtDelete) { delete m_pQtWidget; m_bQtDelete = false; } m_pQtWidget = nullptr; } if (m_pQtFilter) { delete m_pQtFilter; m_pQtFilter = nullptr; } #ifdef CONFIG_LIBSUIL if (m_suil_instance) { suil_instance_free(m_suil_instance); m_suil_instance = nullptr; } if (m_suil_host) { suil_host_free(m_suil_host); m_suil_host = nullptr; } #endif if (m_lv2_ui_descriptor) { if (m_lv2_ui_handle) m_lv2_ui_descriptor->cleanup(m_lv2_ui_handle); m_lv2_ui_descriptor = nullptr; } if (m_lv2_ui_module) { ::dlclose(m_lv2_ui_module); m_lv2_ui_module = nullptr; } if (m_lv2_ui_features) { delete [] m_lv2_ui_features; m_lv2_ui_features = nullptr; } if (m_lv2_uis) { lilv_uis_free(m_lv2_uis); m_lv2_uis = nullptr; } m_lv2_ui_no_user_resize = false; m_lv2_ui_widget = nullptr; m_lv2_ui_handle = nullptr; m_lv2_ui_type = LV2_UI_TYPE_NONE; m_lv2_ui = nullptr; } // Idle editor. void qtractorLv2Plugin::idleEditor (void) { #ifdef CONFIG_LV2_ATOM uint32_t read_space = ::jack_ringbuffer_read_space(m_plugin_events); while (read_space > 0) { ControlEvent ev; ::jack_ringbuffer_read(m_plugin_events, (char *) &ev, sizeof(ev)); char buf[ev.size]; if (::jack_ringbuffer_read(m_plugin_events, buf, ev.size) < ev.size) break; lv2_ui_port_event(ev.index, ev.size, ev.protocol, buf); read_space -= sizeof(ev) + ev.size; } #endif // Try to make all parameter changes into one single command... if (m_ui_params.count() > 0) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorPluginParamValuesCommand *pParamValuesCommand = new qtractorPluginParamValuesCommand( QObject::tr("plugin parameters")); QHash::ConstIterator iter = m_ui_params.constBegin(); const QHash::ConstIterator& iter_end = m_ui_params.constEnd(); for ( ; iter != iter_end; ++iter) { const unsigned long iIndex = iter.key(); const float fValue = iter.value(); qtractorPlugin::Param *pParam = findParam(iIndex); if (pParam) pParamValuesCommand->updateParamValue(pParam, fValue, false); } if (pParamValuesCommand->isEmpty()) delete pParamValuesCommand; else pSession->execute(pParamValuesCommand); } // Done. m_ui_params.clear(); } // Try to make all port events at once now... if (m_port_events.count() > 0) { QHash::ConstIterator iter = m_port_events.constBegin(); const QHash::ConstIterator& iter_end = m_port_events.constEnd(); for ( ; iter != iter_end; ++iter) { const unsigned long iIndex = iter.key(); const float fValue = iter.value(); qtractorPlugin::Param *pParam = findParam(iIndex); if (pParam) pParam->setValue(fValue, false); } // Done. m_port_events.clear(); } #ifdef CONFIG_LV2_MIDNAM if (m_lv2_midnam_update > 0) { m_lv2_midnam_update = 0; updateInstruments(); } #endif // Now, the following only makes sense // iif you have an open custom GUI editor.. if (m_lv2_ui == nullptr) return; if (m_piControlOuts && m_pfControlOuts && m_pfControlOutsLast) { const unsigned long iControlOuts = type()->controlOuts(); for (unsigned short j = 0; j < iControlOuts; ++j) { if (m_pfControlOutsLast[j] != m_pfControlOuts[j]) { lv2_ui_port_event(m_piControlOuts[j], sizeof(float), 0, &m_pfControlOuts[j]); m_pfControlOutsLast[j] = m_pfControlOuts[j]; } } } // Do we need some clean-up...? if (isEditorClosed()) { setEditorClosed(false); toggleFormEditor(false); m_bEditorVisible = false; // Do really close now. closeEditor(); return; } #ifdef CONFIG_LV2_EXTERNAL_UI if (m_lv2_ui_widget && m_lv2_ui_type == LV2_UI_TYPE_EXTERNAL) LV2_EXTERNAL_UI_RUN((LV2_External_UI_Widget *) m_lv2_ui_widget); #endif #ifdef CONFIG_LV2_UI_IDLE if (m_lv2_ui_handle && m_lv2_ui_idle_interface && m_lv2_ui_idle_interface->idle) { if ((*m_lv2_ui_idle_interface->idle)(m_lv2_ui_handle)) closeEditorEx(); } #endif } void qtractorLv2Plugin::closeEditorEx (void) { #ifdef CONFIG_DEBUG qDebug("qtractorLv2Plugin[%p]::closeEditorEx()", this); #endif // Save current editor position, if any... // saveEditorPos(); setEditorClosed(true); } // GUI editor visibility state. void qtractorLv2Plugin::setEditorVisible ( bool bVisible ) { if (m_lv2_ui == nullptr) return; if (/*!m_bEditorVisible && */bVisible) { if (m_pQtWidget) { // Guaranteeing a reasonable window type: // ie. not Qt::Dialog or Qt::Popup from the seemingly good choices. if (m_lv2_ui_type == LV2_UI_TYPE_QT4 || m_lv2_ui_type == LV2_UI_TYPE_QT5) { const Qt::WindowFlags wflags = m_pQtWidget->windowFlags() & ~Qt::WindowType_Mask; m_pQtWidget->setWindowFlags(wflags | Qt::Widget); } m_pQtWidget->show(); m_pQtWidget->raise(); m_pQtWidget->activateWindow(); } #ifdef CONFIG_LV2_EXTERNAL_UI else if (m_lv2_ui_widget && m_lv2_ui_type == LV2_UI_TYPE_EXTERNAL) LV2_EXTERNAL_UI_SHOW((LV2_External_UI_Widget *) m_lv2_ui_widget); #endif #ifdef CONFIG_LV2_UI_SHOW else if (m_lv2_ui_handle && m_lv2_ui_show_interface && m_lv2_ui_show_interface->show) (*m_lv2_ui_show_interface->show)(m_lv2_ui_handle); #endif m_bEditorVisible = true; // Restore editor last known position, if any... // loadEditorPos(); } else if (/*m_bEditorVisible && */!bVisible) { // Save editor current position, if any... saveEditorPos(); if (m_pQtWidget) m_pQtWidget->hide(); #ifdef CONFIG_LV2_EXTERNAL_UI else if (m_lv2_ui_widget && m_lv2_ui_type == LV2_UI_TYPE_EXTERNAL) LV2_EXTERNAL_UI_HIDE((LV2_External_UI_Widget *) m_lv2_ui_widget); #endif #ifdef CONFIG_LV2_UI_SHOW else if (m_lv2_ui_handle && m_lv2_ui_show_interface && m_lv2_ui_show_interface->hide) (*m_lv2_ui_show_interface->hide)(m_lv2_ui_handle); #endif m_bEditorVisible = false; } toggleFormEditor(m_bEditorVisible); } bool qtractorLv2Plugin::isEditorVisible (void) const { return m_bEditorVisible; } // GUI editor window title methods. void qtractorLv2Plugin::setEditorTitle ( const QString& sTitle ) { qtractorPlugin::setEditorTitle(sTitle); m_aEditorTitle = editorTitle().toUtf8(); updateEditorTitleEx(); } void qtractorLv2Plugin::updateEditorTitleEx (void) { if (m_lv2_ui == nullptr) return; #ifdef CONFIG_LV2_OPTIONS for (int i = 0; m_lv2_ui_options[i].type; ++i) { if (m_lv2_ui_options[i].type == g_lv2_urids.ui_windowTitle) { m_lv2_ui_options[i].value = m_aEditorTitle.constData(); break; } } #endif if (m_pQtWidget) { m_pQtWidget->setWindowTitle(m_aEditorTitle); m_pQtWidget->setWindowIcon(QIcon::fromTheme("qtractorPlugin")); } #ifdef CONFIG_LV2_EXTERNAL_UI else if (m_lv2_ui_widget && m_lv2_ui_type == LV2_UI_TYPE_EXTERNAL) m_lv2_ui_external_host.plugin_human_id = m_aEditorTitle.constData(); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef CONFIG_LV2_UI_GTK2 #ifdef CONFIG_LV2_UI_SHOW else if (m_lv2_ui_widget && m_lv2_ui_show_interface && m_lv2_ui_type == LV2_UI_TYPE_GTK) { GtkWidget *pGtkWidget = static_cast (m_lv2_ui_widget); gtk_window_set_title( GTK_WINDOW(gtk_widget_get_toplevel(pGtkWidget)), m_aEditorTitle.constData()); } #endif // CONFIG_LV2_UI_SHOW #endif // CONFIG_LV2_UI_GTK2 #endif } // GUI editor window (re)position methods. void qtractorLv2Plugin::saveEditorPos (void) { if (m_lv2_ui == nullptr) return; QPoint posEditor; if (m_pQtWidget && m_pQtWidget->isVisible()) posEditor = m_pQtWidget->pos(); #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef CONFIG_LV2_UI_GTK2 #ifdef CONFIG_LV2_UI_SHOW else if (m_lv2_ui_widget && m_lv2_ui_show_interface && m_lv2_ui_type == LV2_UI_TYPE_GTK) { GtkWidget *pGtkWidget = static_cast (m_lv2_ui_widget); gint x = 0; gint y = 0; gtk_window_get_position( GTK_WINDOW(gtk_widget_get_toplevel(pGtkWidget)), &x, &y); posEditor.setX(int(x)); posEditor.setY(int(y)); } #endif // CONFIG_LV2_UI_SHOW #endif // CONFIG_LV2_UI_GTK2 #endif setEditorPos(posEditor); } void qtractorLv2Plugin::loadEditorPos (void) { if (m_lv2_ui == nullptr) return; const QPoint& posEditor = editorPos(); if (m_pQtWidget) moveWidgetPos(m_pQtWidget, posEditor); #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef CONFIG_LV2_UI_GTK2 #ifdef CONFIG_LV2_UI_SHOW else if (m_lv2_ui_widget && m_lv2_ui_show_interface && m_lv2_ui_type == LV2_UI_TYPE_GTK) { GtkWidget *pGtkWidget = static_cast (m_lv2_ui_widget); const gint x = posEditor.x(); const gint y = posEditor.y(); gtk_window_move( GTK_WINDOW(gtk_widget_get_toplevel(pGtkWidget)), x, y); } #endif // CONFIG_LV2_UI_SHOW #endif // CONFIG_LV2_UI_GTK2 #endif } // Parameter update method. void qtractorLv2Plugin::updateParam ( qtractorPlugin::Param *pParam, float fValue, bool bUpdate ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2Plugin[%p]::updateParam(%lu, %g, %d)", this, pParam->index(), fValue, int(bUpdate)); #endif if (bUpdate) lv2_ui_port_event(pParam->index(), sizeof(float), 0, &fValue); } // Idle editor (static). void qtractorLv2Plugin::idleEditorAll (void) { QListIterator iter(g_lv2Plugins); while (iter.hasNext()) iter.next()->idleEditor(); } // LV2 UI control change method. void qtractorLv2Plugin::lv2_ui_port_write ( uint32_t port_index, uint32_t buffer_size, uint32_t protocol, const void *buffer ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2Plugin[%p]::lv2_ui_port_write(%u, %u, %u, %p)", this, port_index, buffer_size, protocol, buffer); #endif #ifdef CONFIG_LV2_ATOM if (protocol == g_lv2_urids.atom_eventTransfer) { char buf[sizeof(ControlEvent) + buffer_size]; ControlEvent *ev = (ControlEvent *) buf; ev->index = port_index; ev->protocol = protocol; ev->size = buffer_size; ::memcpy(ev->body, buffer, buffer_size); ::jack_ringbuffer_write(m_ui_events, buf, sizeof(buf)); return; } #endif if (buffer_size != sizeof(float) || protocol != 0) return; const float port_value = *(float *) buffer; #ifdef CONFIG_LV2_UI_TOUCH // Hold plugin param value if under touch... if (m_ui_params_touch.contains(port_index)) { m_ui_params_touch.insert(port_index, port_value); return; } #endif // Update plugin params... m_ui_params.insert(port_index, port_value); } // LV2 UI portMap method. uint32_t qtractorLv2Plugin::lv2_ui_port_index ( const char *port_symbol ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2Plugin[%p]::lv2_ui_port_index(%s)", this, port_symbol); #endif LilvPlugin *plugin = lv2_plugin(); if (plugin == nullptr) return LV2UI_INVALID_PORT_INDEX; LilvNode *symbol_uri = lilv_new_string(g_lv2_world, port_symbol); const LilvPort *port = lilv_plugin_get_port_by_symbol(plugin, symbol_uri); lilv_node_free(symbol_uri); return port ? lilv_port_get_index(plugin, port) : LV2UI_INVALID_PORT_INDEX; } #ifdef CONFIG_LV2_UI_TOUCH // LV2 UI touch control (ui->host). void qtractorLv2Plugin::lv2_ui_touch ( uint32_t port_index, bool grabbed ) { qtractorPlugin::Param *pParam = findParam(port_index); if (pParam == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2Plugin[%p]::lv2_ui_touch(%u, %d)", this, port_index, int(grabbed)); #endif if (grabbed) { m_ui_params_touch.insert(port_index, pParam->value()); } else { const float port_value = m_ui_params_touch.value(port_index, pParam->value()); m_ui_params.insert(port_index, port_value); m_ui_params_touch.remove(port_index); } } #endif // CONFIG_LV2_UI_TOUCH #ifdef CONFIG_LV2_UI_REQ_VALUE // LV2 UI requestValue control (ui->host). LV2UI_Request_Value_Status qtractorLv2Plugin::lv2_ui_request_value ( LV2_URID key, LV2_URID type, const LV2_Feature *const */*features*/ ) { if (m_lv2_ui_req_value_busy) return LV2UI_REQUEST_VALUE_BUSY; #ifdef CONFIG_LV2_PATCH Property *pProp = static_cast ( qtractorPlugin::findProperty(key)); if (pProp == nullptr) return LV2UI_REQUEST_VALUE_ERR_UNSUPPORTED; if (!type) type = pProp->type(); if (type != g_lv2_urids.atom_Path) return LV2UI_REQUEST_VALUE_ERR_UNSUPPORTED; QString sFilename = pProp->variant().toString(); #ifdef CONFIG_DEBUG qDebug("qtractorLv2Plugin[%p]::lv2_ui_request_value(%d, %d) [%s=\"%s\"]", this, int(key), int(type), pProp->name().toUtf8().constData(), sFilename.toUtf8().constData()); #endif m_lv2_ui_req_value_busy = true; const QString& sTitle = QObject::tr("Open File", "lv2_ui_request_parameter"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; if (m_pQtWidget && m_pQtWidget->isVisible()) pParentWidget = m_pQtWidget; else pParentWidget = qtractorMainForm::getInstance(); } #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) sFilename = QFileDialog::getOpenFileName(pParentWidget, sTitle, sFilename, QString(), nullptr, options); #else // Construct open-file dialog... QFileDialog fileDialog(pParentWidget, sTitle, sFilename); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFile); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); #endif if (!sFilename.isEmpty()) { pProp->setVariant(QFileInfo(sFilename).canonicalFilePath(), true); lv2_property_update(key); } m_lv2_ui_req_value_busy = false; #endif // CONFIG_LV2_PATCH return LV2UI_REQUEST_VALUE_SUCCESS; } #endif // CONFIG_LV2_UI_REQ_VALUE #ifdef CONFIG_LV2_PORT_CHANGE_REQUEST // LV2 Control Input Port change request. LV2_ControlInputPort_Change_Status qtractorLv2Plugin::lv2_port_change_request ( unsigned long port_index, float port_value ) { m_port_events.insert(port_index, port_value); return LV2_CONTROL_INPUT_PORT_CHANGE_SUCCESS; } #endif // CONFIG_LV2_PORT_CHANGE_REQUEST // LV2 UI resize control (host->ui). void qtractorLv2Plugin::lv2_ui_resize ( const QSize& size ) { #ifdef CONFIG_DEBUG//_0 qDebug("qtractorLv2Plugin[%p]::lv2_ui_resize(%d, %d)", this, size.width(), size.height()); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef CONFIG_LV2_UI_X11 if (m_lv2_ui_type == LV2_UI_TYPE_X11 && m_lv2_ui_widget #ifdef CONFIG_LIBSUIL && m_suil_instance == nullptr #endif ) { const WId wid = WId(m_lv2_ui_widget); QWindow *pWindow = QWindow::fromWinId(wid); if (pWindow) { pWindow->resize(size); delete pWindow; return; } } #endif // CONFIG_LV2_UI_X11 #endif const LV2UI_Resize *resize = (const LV2UI_Resize *) lv2_ui_extension_data(LV2_UI__resize); if (resize && resize->ui_resize) { LV2UI_Feature_Handle handle = resize->handle; if (handle == nullptr) handle = m_lv2_ui_handle; (*resize->ui_resize)(handle, size.width(), size.height()); } } #ifndef CONFIG_LIBSUIL #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif #endif // Alternate UI instantiation stuff. bool qtractorLv2Plugin::lv2_ui_instantiate ( const char *ui_host_uri, const char *plugin_uri, const char *ui_uri, const char *ui_type_uri, const char *ui_bundle_path, const char *ui_binary_path, QWidget *pParent, Qt::WindowFlags wflags ) { #ifdef CONFIG_DEBUG qDebug("qtractorLv2Plugin[%p]::lv2_ui_instantiate(\"%s\")", this, ui_uri); #endif // Setup fundamental UI features... const LilvInstance *instance = lv2_instance(0); if (instance == nullptr) return false; const LV2_Descriptor *descriptor = lilv_instance_get_descriptor(instance); if (descriptor == nullptr) return false; int iFeatures = 0; while (m_lv2_features[iFeatures]) { ++iFeatures; } m_lv2_ui_features = new LV2_Feature * [iFeatures + 10]; for (int i = 0; i < iFeatures; ++i) m_lv2_ui_features[i] = (LV2_Feature *) m_lv2_features[i]; m_lv2_ui_data_access.data_access = descriptor->extension_data; m_lv2_ui_data_access_feature.URI = LV2_DATA_ACCESS_URI; m_lv2_ui_data_access_feature.data = &m_lv2_ui_data_access; m_lv2_ui_features[iFeatures++] = &m_lv2_ui_data_access_feature; m_lv2_ui_instance_access_feature.URI = LV2_INSTANCE_ACCESS_URI; m_lv2_ui_instance_access_feature.data = lilv_instance_get_handle(instance); m_lv2_ui_features[iFeatures++] = &m_lv2_ui_instance_access_feature; #ifdef CONFIG_LV2_EXTERNAL_UI m_lv2_ui_external_host.ui_closed = qtractor_lv2_ui_closed; m_lv2_ui_external_host.plugin_human_id = m_aEditorTitle.constData(); m_lv2_ui_external_feature.URI = LV2_EXTERNAL_UI__Host; m_lv2_ui_external_feature.data = &m_lv2_ui_external_host; m_lv2_ui_features[iFeatures++] = &m_lv2_ui_external_feature; #ifdef LV2_EXTERNAL_UI_DEPRECATED_URI m_lv2_ui_external_deprecated_feature.URI = LV2_EXTERNAL_UI_DEPRECATED_URI; m_lv2_ui_external_deprecated_feature.data = &m_lv2_ui_external_host; m_lv2_ui_features[iFeatures++] = &m_lv2_ui_external_deprecated_feature; #endif #endif #ifdef CONFIG_LV2_OPTIONS m_fUpdateRate = 15.0f; m_fSampleRate = 44100.0f; m_dSampleRate = 44100.0; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) { m_fSampleRate = float(pAudioEngine->sampleRate()); m_dSampleRate = double(m_fSampleRate); } } const LV2_Options_Option ui_options[] = { { LV2_OPTIONS_INSTANCE, 0, g_lv2_urids.ui_windowTitle, sizeof(char *), g_lv2_urids.atom_String, m_aEditorTitle.constData() }, { LV2_OPTIONS_INSTANCE, 0, g_lv2_urids.ui_updateRate, sizeof(float), g_lv2_urids.atom_Float, &m_fUpdateRate }, { LV2_OPTIONS_INSTANCE, 0, g_lv2_urids.ui_sampleRate, sizeof(double), g_lv2_urids.atom_Double, &m_dSampleRate }, #ifdef CONFIG_LV2_PARAMETERS { LV2_OPTIONS_INSTANCE, 0, g_lv2_urids.param_sampleRate, sizeof(float), g_lv2_urids.atom_Float, &m_fSampleRate }, #endif { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, nullptr } }; ::memcpy(&m_lv2_ui_options, &ui_options, sizeof(ui_options)); m_lv2_ui_options_feature.URI = LV2_OPTIONS__options; m_lv2_ui_options_feature.data = &m_lv2_ui_options; // Find and override options feature... for (int i = 0; i < iFeatures; ++i) { if (::strcmp(m_lv2_ui_features[i]->URI, LV2_OPTIONS__options) == 0) { m_lv2_ui_features[i] = &m_lv2_ui_options_feature; break; } } #endif m_lv2_ui_features[iFeatures] = nullptr; #ifdef CONFIG_LIBSUIL // Check whether special UI wrapping are supported... if (m_suil_support && ui_type_uri && suil_ui_supported(ui_host_uri, ui_type_uri) > 0) { m_suil_host = suil_host_new( qtractor_lv2_ui_port_write, qtractor_lv2_ui_port_index, nullptr, nullptr); if (m_suil_host) { #ifdef CONFIG_LV2_UI_TOUCH suil_host_set_touch_func(m_suil_host, qtractor_lv2_ui_touch); #endif m_suil_instance = suil_instance_new(m_suil_host, this, ui_host_uri, plugin_uri, ui_uri, ui_type_uri, ui_bundle_path, ui_binary_path, m_lv2_ui_features); if (m_suil_instance) { #ifdef CONFIG_SUIL_INSTANCE_GET_HANDLE m_lv2_ui_handle = (LV2UI_Handle) suil_instance_get_handle(m_suil_instance); #else struct SuilInstanceHead { // HACK! void *ui_lib_handle; const LV2UI_Descriptor *ui_descriptor; LV2UI_Handle ui_handle; } *suil_instance_head = (SuilInstanceHead *) m_suil_instance; m_lv2_ui_handle = suil_instance_head->ui_handle; #endif // CONFIG_SUIL_INSTANCE_GET_HANDLE m_lv2_ui_widget = suil_instance_get_widget(m_suil_instance); return true; } // Fall thru... suil_host_free(m_suil_host); m_suil_host = nullptr; } } #endif // Open UI library... m_lv2_ui_module = ::dlopen(ui_binary_path, RTLD_LOCAL | RTLD_LAZY); // Get UI descriptor discovery function... LV2UI_DescriptorFunction pfnLv2UiDescriptor = (LV2UI_DescriptorFunction) ::dlsym(m_lv2_ui_module, "lv2ui_descriptor"); if (pfnLv2UiDescriptor == nullptr) { ::dlclose(m_lv2_ui_module); m_lv2_ui_module = nullptr; return false; } // Get UI descriptor... uint32_t ui_index = 0; m_lv2_ui_descriptor = (*pfnLv2UiDescriptor)(ui_index); while (m_lv2_ui_descriptor && ::strcmp(m_lv2_ui_descriptor->URI, ui_uri)) m_lv2_ui_descriptor = (*pfnLv2UiDescriptor)(++ui_index); if (m_lv2_ui_descriptor == nullptr) { ::dlclose(m_lv2_ui_module); m_lv2_ui_module = nullptr; return false; } // Add additional features implemented by host functions... m_lv2_ui_port_map.handle = this; m_lv2_ui_port_map.port_index = qtractor_lv2_ui_port_index; m_lv2_ui_port_map_feature.URI = LV2_UI__portMap; m_lv2_ui_port_map_feature.data = &m_lv2_ui_port_map; m_lv2_ui_features[iFeatures++] = &m_lv2_ui_port_map_feature; #ifdef CONFIG_LV2_UI_TOUCH m_lv2_ui_touch.handle = this; m_lv2_ui_touch.touch = qtractor_lv2_ui_touch; m_lv2_ui_touch_feature.URI = LV2_UI__touch; m_lv2_ui_touch_feature.data = &m_lv2_ui_touch; m_lv2_ui_features[iFeatures++] = &m_lv2_ui_touch_feature; #endif #ifdef CONFIG_LV2_UI_REQ_VALUE m_lv2_ui_req_value.handle = this; m_lv2_ui_req_value.request = qtractor_lv2_ui_request_value; m_lv2_ui_req_value_feature.URI = LV2_UI__requestValue; m_lv2_ui_req_value_feature.data = &m_lv2_ui_req_value; m_lv2_ui_features[iFeatures++] = &m_lv2_ui_req_value_feature; #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef CONFIG_LV2_UI_X11 if (m_lv2_ui_type == LV2_UI_TYPE_X11) { // Create the new parent frame... QWidget *pQtWidget = new QWidget(pParent, wflags); pQtWidget->setAttribute(Qt::WA_QuitOnClose, false); // Add/prepare some needed features... if (!m_lv2_ui_no_user_resize) { m_lv2_ui_resize.handle = pQtWidget; m_lv2_ui_resize.ui_resize = qtractor_lv2_ui_resize; m_lv2_ui_resize_feature.URI = LV2_UI__resize; m_lv2_ui_resize_feature.data = &m_lv2_ui_resize; m_lv2_ui_features[iFeatures++] = &m_lv2_ui_resize_feature; } m_lv2_ui_parent_feature.URI = LV2_UI__parent; m_lv2_ui_parent_feature.data = (void *) pQtWidget->winId(); m_lv2_ui_features[iFeatures++] = &m_lv2_ui_parent_feature; // Done. m_pQtWidget = pQtWidget; m_bQtDelete = true; } #endif // CONFIG_LV2_UI_X11 #endif m_lv2_ui_features[iFeatures] = nullptr; // Instantiate UI... m_lv2_ui_widget = nullptr; m_lv2_ui_handle = m_lv2_ui_descriptor->instantiate( m_lv2_ui_descriptor, plugin_uri, ui_bundle_path, qtractor_lv2_ui_port_write, this, &m_lv2_ui_widget, m_lv2_ui_features); // Failed to instantiate UI? if (m_lv2_ui_handle == nullptr) { m_lv2_ui_widget = nullptr; m_lv2_ui_descriptor = nullptr; ::dlclose(m_lv2_ui_module); m_lv2_ui_module = nullptr; return false; } return true; } #ifndef CONFIG_LIBSUIL #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic pop #endif #endif void qtractorLv2Plugin::lv2_ui_port_event ( uint32_t port_index, uint32_t buffer_size, uint32_t format, const void *buffer ) { #ifdef CONFIG_LIBSUIL if (m_suil_instance) { suil_instance_port_event(m_suil_instance, port_index, buffer_size, format, buffer); } else #endif if (m_lv2_ui_descriptor && m_lv2_ui_descriptor->port_event) { (*m_lv2_ui_descriptor->port_event)(m_lv2_ui_handle, port_index, buffer_size, format, buffer); } #ifdef CONFIG_LV2_ATOM if (format == g_lv2_urids.atom_eventTransfer) { const LV2_Atom *atom = (const LV2_Atom *) buffer; if (atom->type == g_lv2_urids.atom_Blank || atom->type == g_lv2_urids.atom_Object) { const LV2_Atom_Object *obj = (const LV2_Atom_Object *) buffer; #ifdef CONFIG_LV2_PATCH if (obj->body.otype == g_lv2_urids.patch_Set) { const LV2_Atom_URID *prop = nullptr; const LV2_Atom *value = nullptr; lv2_atom_object_get(obj, g_lv2_urids.patch_property, (const LV2_Atom *) &prop, g_lv2_urids.patch_value, &value, 0); if (prop && value && prop->atom.type == g_lv2_atom_forge->URID) lv2_property_changed(prop->body, value); } else if (obj->body.otype == g_lv2_urids.patch_Put) { const LV2_Atom_Object *body = nullptr; lv2_atom_object_get(obj, g_lv2_urids.patch_body, (const LV2_Atom *) &body, 0); if (body == nullptr) // HACK! body = obj; if (body && ( body->atom.type == g_lv2_urids.atom_Blank || body->atom.type == g_lv2_urids.atom_Object)) { LV2_ATOM_OBJECT_FOREACH(body, prop) lv2_property_changed(prop->key, &prop->value); } } else #endif // CONFIG_LV2_PATCH #ifdef CONFIG_LV2_PORT_EVENT if (obj->body.otype == g_lv2_urids.atom_PortEvent) { const LV2_Atom_Tuple *tup = nullptr; lv2_atom_object_get(obj, g_lv2_urids.atom_portTuple, (const LV2_Atom *) &tup, 0); if (tup == nullptr) tup = (const LV2_Atom_Tuple *) obj; uint32_t port_index = 0; LV2_ATOM_TUPLE_FOREACH(tup, iter) { if (iter->type == g_lv2_urids.atom_Int) port_index = *(uint32_t *) (iter + 1); else if (iter->type == g_lv2_urids.atom_Float) { // const uint32_t buffer_size = iter->size; const void *buffer = iter + 1; m_port_events.insert(port_index, *(float *) buffer); } } } #ifdef CONFIG_LV2_STATE else #endif #endif // CONFIG_LV2_PORT_EVENT #ifdef CONFIG_LV2_STATE if (obj->body.otype == g_lv2_urids.state_StateChanged) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->dirtyNotifySlot(); refreshForm(); } #endif // CONFIG_LV2_STATE } } #endif // CONFIG_LV2_ATOM } #ifdef CONFIG_LV2_PATCH // LV2 Patch/properties inventory. void qtractorLv2Plugin::lv2_patch_properties ( const char *pszPatch ) { const LilvPlugin *plugin = lv2_plugin(); if (plugin == nullptr) return; LilvNode *patch_uri = lilv_new_uri(g_lv2_world, pszPatch); LilvNodes *properties = lilv_world_find_nodes( g_lv2_world, lilv_plugin_get_uri(plugin), patch_uri, nullptr); LILV_FOREACH(nodes, iter, properties) { const LilvNode *property = lilv_nodes_get(properties, iter); const char *prop_uri = lilv_node_as_uri(property); const unsigned long iProperty = lv2_urid_map(prop_uri); if (iProperty && !qtractorPlugin::findProperty(iProperty)) { Property *pProp = new Property(this, iProperty, property); qtractorPlugin::addProperty(pProp); ++m_lv2_patch_changed; } } lilv_nodes_free(properties); lilv_node_free(patch_uri); } // LV2 Patch/properties changed, eventually from UI->plugin... void qtractorLv2Plugin::lv2_property_changed ( LV2_URID key, const LV2_Atom *value ) { Property *pProp = static_cast ( qtractorPlugin::findProperty(key)); if (pProp == nullptr) return; const LV2_URID type = value->type; const uint32_t size = value->size; const void *body = value + 1; if (type != pProp->type()) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2Plugin[%p]::lv2_property_changed(%u) [%s]", this, key, pProp->name().toUtf8().constData()); #endif if (type == g_lv2_urids.atom_Bool) pProp->setVariant(bool(*(const int32_t *) body)); else if (type == g_lv2_urids.atom_Int) pProp->setVariant(int(*(const int32_t *) body)); else if (type == g_lv2_urids.atom_Long) pProp->setVariant(qlonglong(*(const int64_t *) body)); else if (type == g_lv2_urids.atom_Float) pProp->setVariant(*(const float *) body); else if (type == g_lv2_urids.atom_Double) pProp->setVariant(*(const double *) body); else if (type == g_lv2_urids.atom_String || type == g_lv2_urids.atom_Path) pProp->setVariant(QByteArray((const char *) body, size)); // Update the stock/generic form if visible... refreshForm(); } // LV2 Patch/property updated, eventually from plugin->UI... void qtractorLv2Plugin::lv2_property_update ( LV2_URID key ) { qtractorLv2PluginType *pLv2Type = static_cast (type()); if (pLv2Type == nullptr) return; if (m_lv2_patch_port_in >= pLv2Type->atomIns()) return; Property *pProp = static_cast ( qtractorPlugin::findProperty(key)); if (pProp == nullptr) return; LV2_URID type = pProp->type(); const QVariant& value = pProp->variant(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2Plugin[%p]::lv2_property_update(%u) [%s]", this, key, pProp->name().toUtf8().constData()); #endif // Set up forge to write to temporary buffer on the stack LV2_Atom_Forge *forge = g_lv2_atom_forge; LV2_Atom_Forge_Frame frame; uint8_t buf[1024]; lv2_atom_forge_set_buffer(forge, buf, sizeof(buf)); // Serialize patch_Set message to set property... lv2_atom_forge_object(forge, &frame, 1, g_lv2_urids.patch_Set); lv2_atom_forge_key(forge, g_lv2_urids.patch_property); lv2_atom_forge_urid(forge, key); lv2_atom_forge_key(forge, g_lv2_urids.patch_value); if (type == g_lv2_urids.atom_Bool) lv2_atom_forge_bool(forge, value.toBool()); else if (type == g_lv2_urids.atom_Int) lv2_atom_forge_int(forge, value.toInt()); else if (type == g_lv2_urids.atom_Long) lv2_atom_forge_long(forge, value.toLongLong()); else if (type == g_lv2_urids.atom_Float) lv2_atom_forge_float(forge, value.toFloat()); else if (type == g_lv2_urids.atom_Double) lv2_atom_forge_double(forge, value.toDouble()); else if (type == g_lv2_urids.atom_String) { const QByteArray& aString = value.toByteArray(); lv2_atom_forge_string(forge, aString.data(), aString.size()); } else if (type == g_lv2_urids.atom_Path) { const QByteArray& aPath = value.toByteArray(); lv2_atom_forge_path(forge, aPath.data(), aPath.size()); } // Write message to UI... const LV2_Atom *atom = lv2_atom_forge_deref(forge, frame.ref); lv2_ui_port_write( m_piAtomIns[m_lv2_patch_port_in], lv2_atom_total_size(atom), g_lv2_urids.atom_eventTransfer, (const void *) atom); } #endif // CONFIG_LV2_PATCH const void *qtractorLv2Plugin::lv2_ui_extension_data ( const char *uri ) { #ifdef CONFIG_LIBSUIL if (m_suil_instance) return suil_instance_extension_data(m_suil_instance, uri); else #endif if (m_lv2_ui_descriptor && m_lv2_ui_descriptor->extension_data) return (*m_lv2_ui_descriptor->extension_data)(uri); return nullptr; } #endif // CONFIG_LV2_UI // Plugin configuration/state (save) snapshot. void qtractorLv2Plugin::freezeConfigs (void) { #ifdef CONFIG_LV2_UI // Update current editor position... saveEditorPos(); #endif if (!type()->isConfigure()) return; clearConfigs(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2Plugin[%p]::freezeConfigs()", this); #endif #ifdef CONFIG_LV2_STATE const QString& s = lv2_state_save(); if (!s.isEmpty()) { const QByteArray data(s.toUtf8()); const LV2_URID key = lv2_urid_map(QTRACTOR_LV2_STATE_KEY); const char *value = data.constData(); const uint32_t size = data.size(); const LV2_URID type = lv2_urid_map(QTRACTOR_LV2_STATE_TYPE); const uint32_t flags = LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE; if (lv2_state_store(key, value, size, type, flags) == LV2_STATE_SUCCESS) qtractorPlugin::clearValues(); } #endif // CONFIG_LV2_STATE } // Plugin configuration/state (load) realization. void qtractorLv2Plugin::realizeConfigs (void) { if (!type()->isConfigure()) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2Plugin[%p]::realizeConfigs()", this); #endif #ifdef CONFIG_LV2_STATE m_lv2_state_configs.clear(); m_lv2_state_ctypes.clear(); const Configs& configs = qtractorPlugin::configs(); const ConfigTypes& ctypes = qtractorPlugin::configTypes(); Configs::ConstIterator config = configs.constBegin(); const Configs::ConstIterator& config_end = configs.constEnd(); for ( ; config != config_end; ++config) { const QString& sKey = config.key(); QByteArray aType(LV2_ATOM__String); ConfigTypes::ConstIterator ctype = ctypes.constFind(sKey); if (ctype != ctypes.constEnd()) aType = ctype.value().toUtf8(); const char *pszType = aType.constData(); const LV2_URID type = lv2_urid_map(pszType); const bool bIsPath = (type == g_lv2_urids.atom_Path); const bool bIsString = (type == g_lv2_urids.atom_String); if (aType.isEmpty() || bIsPath || bIsString) m_lv2_state_configs.insert(sKey, config.value().toUtf8()); else if (type == g_lv2_urids.atom_Bool || type == g_lv2_urids.atom_Int) { const int32_t val = config.value().toInt(); m_lv2_state_configs.insert(sKey, QByteArray((const char *) &val, sizeof(int32_t))); } else if (type == g_lv2_urids.atom_Long) { const int64_t val = config.value().toLongLong(); m_lv2_state_configs.insert(sKey, QByteArray((const char *) &val, sizeof(int64_t))); } else if (type == g_lv2_urids.atom_Float) { const float val = config.value().toFloat(); m_lv2_state_configs.insert(sKey, QByteArray((const char *) &val, sizeof(float))); } else if (type == g_lv2_urids.atom_Double) { const double val = config.value().toDouble(); m_lv2_state_configs.insert(sKey, QByteArray((const char *) &val, sizeof(double))); } else { m_lv2_state_configs.insert(sKey, qUncompress( QByteArray::fromBase64(config.value().toUtf8()))); } if (!aType.isEmpty() && !bIsString) m_lv2_state_ctypes.insert(sKey, type); } const QString& s = m_lv2_state_configs.value(QTRACTOR_LV2_STATE_KEY); if (!s.isEmpty()) { qtractorPlugin::clearValues(); lv2_state_restore(s); } else { const unsigned short iInstances = instances(); for (unsigned short i = 0; i < iInstances; ++i) { const LV2_State_Interface *state = lv2_state_interface(i); if (state) { LV2_Handle handle = lv2_handle(i); if (handle) (*state->restore)(handle, qtractor_lv2_state_retrieve, this, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE, m_lv2_features); } } } #endif // CONFIG_LV2_STATE qtractorPlugin::realizeConfigs(); } // Plugin configuration/state release. void qtractorLv2Plugin::releaseConfigs (void) { if (!type()->isConfigure()) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorLv2Plugin[%p]::releaseConfigs()", this); #endif #ifdef CONFIG_LV2_STATE m_lv2_state_configs.clear(); m_lv2_state_ctypes.clear(); #endif qtractorPlugin::clearConfigs(); } #ifdef CONFIG_LV2_WORKER // LV2 Worker/Schedule extension data interface accessor. const LV2_Worker_Interface *qtractorLv2Plugin::lv2_worker_interface ( unsigned short iInstance ) const { const LilvInstance *instance = lv2_instance(iInstance); if (instance == nullptr) return nullptr; const LV2_Descriptor *descriptor = lilv_instance_get_descriptor(instance); if (descriptor == nullptr) return nullptr; if (descriptor->extension_data == nullptr) return nullptr; return (const LV2_Worker_Interface *) (*descriptor->extension_data)(LV2_WORKER__interface); } #endif // CONFIG_LV2_WORKER #ifdef CONFIG_LV2_STATE // Load default plugin state void qtractorLv2Plugin::lv2_state_load_default (void) { const LilvNode *default_state = lilv_plugin_get_uri(lv2_plugin()); if (default_state == nullptr) return; LilvState *state = lilv_state_new_from_world(g_lv2_world, &g_lv2_urid_map, default_state); if (state == nullptr) return; const unsigned short iInstances = instances(); for (unsigned short i = 0; i < iInstances; ++i) { lilv_state_restore(state, m_ppInstances[i], qtractor_lv2_set_port_value, this, 0, nullptr); } lilv_state_free(state); #ifdef CONFIG_LV2_PATCH ++m_lv2_patch_changed; #endif } // LV2 State extension data descriptor accessor. const LV2_State_Interface *qtractorLv2Plugin::lv2_state_interface ( unsigned short iInstance ) const { const LilvInstance *instance = lv2_instance(iInstance); if (instance == nullptr) return nullptr; const LV2_Descriptor *descriptor = lilv_instance_get_descriptor(instance); if (descriptor == nullptr) return nullptr; if (descriptor->extension_data == nullptr) return nullptr; return (const LV2_State_Interface *) (*descriptor->extension_data)(LV2_STATE__interface); } LV2_State_Status qtractorLv2Plugin::lv2_state_store ( uint32_t key, const void *value, size_t size, uint32_t type, uint32_t flags ) { if ((flags & (LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE)) == 0) return LV2_STATE_ERR_BAD_FLAGS; const char *pszKey = lv2_urid_unmap(key); if (pszKey == nullptr) return LV2_STATE_ERR_UNKNOWN; const char *pszType = lv2_urid_unmap(type); if (pszType == nullptr) return LV2_STATE_ERR_BAD_TYPE; const char *pchValue = (const char *) value; if (pchValue == nullptr) return LV2_STATE_ERR_UNKNOWN; const bool bIsPath = (type == g_lv2_urids.atom_Path); const bool bIsString = (type == g_lv2_urids.atom_String); const QString& sKey = QString::fromUtf8(pszKey); if (bIsPath || bIsString) setConfig(sKey, QString::fromUtf8(pchValue, ::strlen(pchValue))); else if (type == g_lv2_urids.atom_Bool || type == g_lv2_urids.atom_Int) setConfig(sKey, QString::number(int(*(const int32_t *) pchValue))); else if (type == g_lv2_urids.atom_Long) setConfig(sKey, QString::number(qlonglong(*(const int64_t *) pchValue))); else if (type == g_lv2_urids.atom_Float) setConfig(sKey, QString::number(*(const float *) pchValue)); else if (type == g_lv2_urids.atom_Double) setConfig(sKey, QString::number(*(const double *) pchValue)); else { QByteArray data = qCompress( QByteArray(pchValue, size)).toBase64(); for (int i = data.size() - (data.size() % 72); i >= 0; i -= 72) data.insert(i, "\n "); // Indentation. setConfig(sKey, data.constData()); } if (!bIsString) setConfigType(sKey, QString::fromUtf8(pszType)); return LV2_STATE_SUCCESS; } const void *qtractorLv2Plugin::lv2_state_retrieve ( uint32_t key, size_t *size, uint32_t *type, uint32_t *flags ) { const char *pszKey = lv2_urid_unmap(key); if (pszKey == nullptr) return nullptr; const QString& sKey = QString::fromUtf8(pszKey); if (sKey.isEmpty()) return nullptr; QHash::ConstIterator iter = m_lv2_state_configs.constFind(sKey); if (iter == m_lv2_state_configs.constEnd()) return nullptr; const QByteArray& data = iter.value(); if (size) *size = data.size(); if (type) { QHash::ConstIterator ctype = m_lv2_state_ctypes.constFind(sKey); if (ctype != m_lv2_state_ctypes.constEnd()) *type = ctype.value(); else *type = g_lv2_urids.atom_String; } if (flags) *flags = LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE; return data.constData(); } #endif // CONFIG_LV2_STATE #ifdef CONFIG_LV2_STATE_FILES // LV2 State save directory (when not the default session one). const QString& qtractorLv2Plugin::lv2_state_save_dir (void) const { return m_lv2_state_save_dir; } #endif // CONFIG_LV2_STATE_FILES // Provisional program/patch accessor. bool qtractorLv2Plugin::getProgram ( int iIndex, Program& program ) const { if (iIndex < 0 || iIndex >= m_programs.count()) return false; program = *m_programs.at(iIndex); return true; } // Provisional note name accessor. bool qtractorLv2Plugin::getNoteName ( int iIndex, NoteName& note ) const { if (iIndex < 0 || iIndex >= m_noteNames.count()) return false; note = *m_noteNames.at(iIndex); return true; } #ifdef CONFIG_LV2_PROGRAMS // LV2 Programs extension data descriptor accessor. const LV2_Programs_Interface *qtractorLv2Plugin::lv2_programs_descriptor ( unsigned short iInstance ) const { const LilvInstance *instance = lv2_instance(iInstance); if (instance == nullptr) return nullptr; const LV2_Descriptor *descriptor = lilv_instance_get_descriptor(instance); if (descriptor == nullptr) return nullptr; if (descriptor->extension_data == nullptr) return nullptr; return (const LV2_Programs_Interface *) (*descriptor->extension_data)(LV2_PROGRAMS__Interface); } // Bank/program selector. void qtractorLv2Plugin::selectProgram ( int iBank, int iProg ) { if (iBank < 0 || iProg < 0) return; // HACK: We don't change program-preset when // we're supposed to be multi-timbral... if (list()->isMidiBus()) return; #ifdef CONFIG_DEBUG qDebug("qtractorLv2Plugin[%p]::selectProgram(%d, %d)", this, iBank, iProg); #endif // For each plugin instance... const unsigned short iInstances = instances(); for (unsigned short i = 0; i < iInstances; ++i) { const LV2_Programs_Interface *programs = lv2_programs_descriptor(i); if (programs && programs->select_program) { LV2_Handle handle = lv2_handle(i); if (handle) { (*programs->select_program)(handle, iBank, iProg); } } } #ifdef CONFIG_LV2_UI const LV2_Programs_UI_Interface *ui_programs = (const LV2_Programs_UI_Interface *) lv2_ui_extension_data(LV2_PROGRAMS__UIInterface); if (ui_programs && ui_programs->select_program) { (*ui_programs->select_program)(m_lv2_ui_handle, iBank, iProg); } #endif // CONFIG_LV2_UI #if 0 // Reset parameters default value... const qtractorPlugin::Params& params = qtractorPlugin::params(); qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator& param_end = params.constEnd(); for ( ; param != param_end; ++param) { qtractorPlugin::Param *pParam = param.value(); pParam->setDefaultValue(pParam->value()); } #endif } // Program/patch notification. void qtractorLv2Plugin::lv2_program_changed ( int iIndex ) { qtractorPluginList *pList = list(); if (iIndex < 0) { qtractorMidiManager *pMidiManager = pList->midiManager(); if (pMidiManager) pMidiManager->updateInstruments(); } else { qtractorPlugin::Program program; if (getProgram(iIndex, program)) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { pSession->execute( new qtractorPluginProgramCommand( this, program.bank, program.prog)); } } } } #endif // CONFIG_LV2_PROGRAMS #ifdef CONFIG_LV2_MIDNAM // LV2 MIDNAM extension data descriptor accessor. const LV2_Midnam_Interface *qtractorLv2Plugin::lv2_midnam_descriptor ( unsigned short iInstance ) const { const LilvInstance *instance = lv2_instance(iInstance); if (instance == nullptr) return nullptr; const LV2_Descriptor *descriptor = lilv_instance_get_descriptor(instance); if (descriptor == nullptr) return nullptr; if (descriptor->extension_data == nullptr) return nullptr; return (const LV2_Midnam_Interface *) (*descriptor->extension_data)(LV2_MIDNAM__interface); } // LV2 MIDNAME update notification. void qtractorLv2Plugin::lv2_midnam_update (void) { ++m_lv2_midnam_update; } #endif // CONFIG_LV2_MIDNAM // Update instrument/programs cache. void qtractorLv2Plugin::updateInstruments (void) { clearInstruments(); // Only first one instance should matter... LV2_Handle handle = lv2_handle(0); if (!handle) return; #ifdef CONFIG_LV2_PROGRAMS const LV2_Programs_Interface *programs = lv2_programs_descriptor(0); if (programs && programs->get_program) { for (int iIndex = 0;; ++iIndex) { const LV2_Program_Descriptor *pLv2Program = (*programs->get_program)(handle, iIndex); if (pLv2Program == nullptr) break; // Map this to that... Program *program = new Program; program->bank = pLv2Program->bank; program->prog = pLv2Program->program; program->name = pLv2Program->name; m_programs.append(program); } } #endif // CONFIG_LV2_PROGRAMS #ifdef CONFIG_LV2_MIDNAM if (!m_programs.isEmpty()) return; const LV2_Midnam_Interface *interface = lv2_midnam_descriptor(0); if (interface == nullptr) return; char *midnam = (*interface->midnam)(handle); if (midnam == nullptr) return; const QString sMidnam = QString::fromUtf8(midnam); (*interface->free)(midnam); QString sModel; char *model = (*interface->model)(handle); if (model) { sModel = QString::fromUtf8(model); (*interface->free)(model); } QDomDocument doc; if (!doc.setContent(sMidnam)) return; qtractorInstrumentList instruments; if (!instruments.loadMidiNameDocument(doc)) return; qtractorInstrumentList::ConstIterator iter = instruments.constBegin(); const qtractorInstrumentList::ConstIterator& iter_end = instruments.constEnd(); for ( ; iter != iter_end; ++iter) { const QString& sInstrumentName = iter.key(); const qtractorInstrument& instr = iter.value(); if (!sModel.isEmpty() && !sInstrumentName.contains(sModel)) continue; const qtractorInstrumentPatches& patches = instr.patches(); qtractorInstrumentPatches::ConstIterator patch_iter = patches.constBegin(); const qtractorInstrumentPatches::ConstIterator& patch_end = patches.constEnd(); for ( ; patch_iter != patch_end; ++patch_iter) { const int iBank = patch_iter.key(); const qtractorInstrumentData& progs = patch_iter.value(); const QString& sBankName = progs.name(); if (iBank < 0 || sBankName.isEmpty()) continue; qtractorInstrumentData::ConstIterator prog_iter = progs.constBegin(); const qtractorInstrumentData::ConstIterator& prog_end = progs.constEnd(); for ( ; prog_iter != prog_end; ++prog_iter) { const int iProg = prog_iter.key(); if (iProg < 0) continue; Program *program = new Program; program->bank = iBank; program->prog = iProg; program->name = prog_iter.value(); m_programs.append(program); } } const qtractorInstrumentKeys& keys = instr.keys(); qtractorInstrumentKeys::ConstIterator key_iter = keys.constBegin(); const qtractorInstrumentKeys::ConstIterator& key_end = keys.constEnd(); for ( ; key_iter != key_end; ++key_iter) { const int iBank = key_iter.key(); const qtractorInstrumentNotes& progs = key_iter.value(); qtractorInstrumentNotes::ConstIterator prog_iter = progs.constBegin(); const qtractorInstrumentNotes::ConstIterator& prog_end = progs.constEnd(); for ( ; prog_iter != prog_end; ++prog_iter) { const int iProg = prog_iter.key(); const qtractorInstrumentData& notes = prog_iter.value(); qtractorInstrumentData::ConstIterator note_iter = notes.constBegin(); const qtractorInstrumentData::ConstIterator& note_end = notes.constEnd(); for ( ; note_iter != note_end; ++note_iter) { NoteName *note = new NoteName; note->bank = iBank; note->prog = iProg; note->note = note_iter.key(); note->name = note_iter.value(); m_noteNames.append(note); } } } if (!sModel.isEmpty()) break; } #endif // CONFIG_LV2_MIDNAM } // Clear instrument/programs cache. void qtractorLv2Plugin::clearInstruments (void) { qDeleteAll(m_programs); m_programs.clear(); qDeleteAll(m_noteNames); m_noteNames.clear(); } #ifdef CONFIG_LV2_TIME // Update LV2 Time from JACK transport position. (static) inline void qtractor_lv2_time_update ( int i, float fValue ) { qtractorLv2Time& member = g_lv2_time[i]; if (member.value != fValue) { member.value = fValue; ++member.changed; #ifdef CONFIG_LV2_TIME_POSITION ++g_lv2_time_position_changed; #endif } } void qtractorLv2Plugin::updateTime ( qtractorAudioEngine *pAudioEngine ) { if (g_lv2_time_refcount < 1) return; #ifdef CONFIG_LV2_TIME_POSITION g_lv2_time_position_changed = 0; #endif const qtractorAudioEngine::TimeInfo& timeInfo = pAudioEngine->timeInfo(); #if 0//QTRACTOR_LV2_TIME_POSITION_FRAME_0 qtractor_lv2_time_update( qtractorLv2Time::frame, float(timeInfo.frame)); #endif qtractor_lv2_time_update( qtractorLv2Time::framesPerSecond, float(timeInfo.sampleRate)); qtractor_lv2_time_update( qtractorLv2Time::speed, (timeInfo.playing ? 1.0f : 0.0f)); qtractor_lv2_time_update( qtractorLv2Time::bar, float(timeInfo.bar)); qtractor_lv2_time_update( qtractorLv2Time::beat, float(timeInfo.beat)); #if 0//QTRACTOR_LV2_TIME_POSITION_BARBEAT_0 qtractor_lv2_time_update( qtractorLv2Time::barBeat, timeInfo.barBeats); #endif qtractor_lv2_time_update( qtractorLv2Time::beatUnit, float(timeInfo.beatType)); qtractor_lv2_time_update( qtractorLv2Time::beatsPerBar, float(timeInfo.beatsPerBar)); qtractor_lv2_time_update( qtractorLv2Time::beatsPerMinute, timeInfo.tempo); #ifdef CONFIG_LV2_TIME_POSITION if (g_lv2_time_position_changed > 0 && g_lv2_time_position_plugins && g_lv2_time_position_buffer) { // Build LV2 Time position object to report change to plugin. LV2_Atom_Forge *forge = g_lv2_atom_forge; uint8_t *buffer = g_lv2_time_position_buffer; lv2_atom_forge_set_buffer(forge, buffer, 256); LV2_Atom_Forge_Frame frame; lv2_atom_forge_object(forge, &frame, 0, g_lv2_urids.time_Position); qtractorLv2Time& time_frame = g_lv2_time[qtractorLv2Time::frame]; #if 1//QTRACTOR_LV2_TIME_POSITION_FRAME_1 time_frame.value = float(timeInfo.frame); #endif lv2_atom_forge_key(forge, time_frame.urid); lv2_atom_forge_long(forge, long(time_frame.value)); const qtractorLv2Time& time_speed = g_lv2_time[qtractorLv2Time::speed]; lv2_atom_forge_key(forge, time_speed.urid); lv2_atom_forge_float(forge, time_speed.value); const qtractorLv2Time& time_bar = g_lv2_time[qtractorLv2Time::bar]; lv2_atom_forge_key(forge, time_bar.urid); lv2_atom_forge_long(forge, long(time_bar.value) - 1); // WTF? const qtractorLv2Time& time_beat = g_lv2_time[qtractorLv2Time::beat]; lv2_atom_forge_key(forge, time_beat.urid); lv2_atom_forge_double(forge, double(time_beat.value)); qtractorLv2Time& time_barBeat = g_lv2_time[qtractorLv2Time::barBeat]; #if 1//QTRACTOR_LV2_TIME_POSITION_BARBEAT_1 time_barBeat.value = timeInfo.barBeats; #endif lv2_atom_forge_key(forge, time_barBeat.urid); lv2_atom_forge_float(forge, time_barBeat.value); const qtractorLv2Time& time_beatUnit = g_lv2_time[qtractorLv2Time::beatUnit]; lv2_atom_forge_key(forge, time_beatUnit.urid); lv2_atom_forge_int(forge, int(time_beatUnit.value)); const qtractorLv2Time& time_beatsPerBar = g_lv2_time[qtractorLv2Time::beatsPerBar]; lv2_atom_forge_key(forge, time_beatsPerBar.urid); lv2_atom_forge_float(forge, time_beatsPerBar.value); const qtractorLv2Time& time_beatsPerMinute = g_lv2_time[qtractorLv2Time::beatsPerMinute]; lv2_atom_forge_key(forge, time_beatsPerMinute.urid); lv2_atom_forge_float(forge, time_beatsPerMinute.value); lv2_atom_forge_pop(forge, &frame); // Make all supporting plugins ready... QListIterator iter(*g_lv2_time_position_plugins); while (iter.hasNext()) iter.next()->lv2_time_position_changed(); } #endif // CONFIG_LV2_TIME_POSITION } void qtractorLv2Plugin::updateTimePost (void) { if (g_lv2_time_refcount < 1) return; for (int i = 0; i < int(qtractorLv2Time::numOfMembers); ++i) { qtractorLv2Time& member = g_lv2_time[i]; if (member.changed > 0 && member.params && member.params->count() > 0) { QListIterator iter(*member.params); while (iter.hasNext()) iter.next()->setValue(member.value, true); member.changed = 0; } } } #ifdef CONFIG_LV2_TIME_POSITION // Make ready LV2 Time position. void qtractorLv2Plugin::lv2_time_position_changed (void) { ++m_lv2_time_position_changed; } #endif #endif // CONFIG_LV2_TIME #ifdef CONFIG_LV2_PRESETS // Refresh and load preset labels listing. (virtual) QStringList qtractorLv2Plugin::presetList (void) const { QStringList list(qtractorPlugin::presetList()); QHash::ConstIterator iter = m_lv2_presets.constBegin(); const QHash::ConstIterator& iter_end = m_lv2_presets.constEnd(); for ( ; iter != iter_end; ++iter) list.append(iter.key()); std::sort(list.begin(), list.end()); return list; } // Load plugin state from a named preset. bool qtractorLv2Plugin::loadPreset ( const QString& sPreset ) { const QString& sUri = m_lv2_presets.value(sPreset); if (sUri.isEmpty()) return false; LilvNode *preset_uri = lilv_new_uri(g_lv2_world, sUri.toUtf8().constData()); LilvState *state = nullptr; const QString sPath = QUrl(sUri).toLocalFile(); const QFileInfo fi(sPath); if (!sPath.isEmpty() && fi.exists()) { #ifdef CONFIG_LV2_STATE_FILES m_lv2_state_save_dir = fi.absolutePath(); #endif state = lilv_state_new_from_file(g_lv2_world, &g_lv2_urid_map, preset_uri, sPath.toUtf8().constData()); } else { state = lilv_state_new_from_world(g_lv2_world, &g_lv2_urid_map, preset_uri); } if (state == nullptr) { lilv_node_free(preset_uri); #ifdef CONFIG_LV2_STATE_FILES m_lv2_state_save_dir.clear(); #endif return false; } const unsigned short iInstances = instances(); for (unsigned short i = 0; i < iInstances; ++i) { lilv_state_restore(state, m_ppInstances[i], qtractor_lv2_set_port_value, this, 0, m_lv2_features); } lilv_state_free(state); lilv_node_free(preset_uri); #ifdef CONFIG_LV2_STATE_FILES m_lv2_state_save_dir.clear(); #endif #ifdef CONFIG_LV2_PATCH ++m_lv2_patch_changed; #endif return true; } // Save current plugin state as a named preset. bool qtractorLv2Plugin::savePreset ( const QString& sPreset ) { qtractorLv2PluginType *pLv2Type = static_cast (type()); if (pLv2Type == nullptr) return false; const QString sDotLv2(".lv2"); const QString& sep = QDir::separator(); QString sDir; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) sDir = pOptions->sLv2PresetDir; if (sDir.isEmpty()) sDir = QDir::homePath() + sep + sDotLv2; sDir += sep + pLv2Type->label(); sDir += '_' + QString::number(pLv2Type->uniqueID(), 16); sDir += '-' + sPreset + sDotLv2; #ifdef CONFIG_LV2_STATE_FILES m_lv2_state_save_dir = sDir; #endif LilvState *state = lilv_state_new_from_instance( lv2_plugin(), m_ppInstances[0], &g_lv2_urid_map, nullptr, nullptr, nullptr, nullptr, qtractor_lv2_get_port_value, this, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE, m_lv2_features); if (state == nullptr) { #ifdef CONFIG_LV2_STATE_FILES m_lv2_state_save_dir.clear(); #endif return false; } lilv_state_set_label(state, sPreset.toUtf8().constData()); const QString sFile = sPreset + ".ttl"; int ret = lilv_state_save(g_lv2_world, &g_lv2_urid_map, &g_lv2_urid_unmap, state, nullptr, sDir.toUtf8().constData(), sFile.toUtf8().constData()); lilv_state_free(state); #ifdef CONFIG_LV2_STATE_FILES m_lv2_state_save_dir.clear(); #endif if (ret == 0) { m_lv2_presets.insert(sPreset, QUrl::fromLocalFile(sDir + sep + sFile).toString()); } return (ret == 0); } // Delete plugin state preset (from file-system). bool qtractorLv2Plugin::deletePreset ( const QString& sPreset ) { const QString& sUri = m_lv2_presets.value(sPreset); if (sUri.isEmpty()) return false; const QString& sPath = QUrl(sUri).toLocalFile(); if (!sPath.isEmpty()) { QFileInfo info(sPath); if (!info.isDir()) info.setFile(info.absolutePath()); qtractor_lv2_remove_file(info); m_lv2_presets.remove(sPreset); } return true; } // Whether given preset is internal/read-only. bool qtractorLv2Plugin::isReadOnlyPreset ( const QString& sPreset ) const { const QString& sUri = m_lv2_presets.value(sPreset); if (sUri.isEmpty()) return false; const QString& sPath = QUrl(sUri).toLocalFile(); if (sPath.isEmpty()) return true; const QFileInfo info(sPath); return !info.exists() || !info.isWritable(); } #endif // CONFIG_LV2_PRESETS #ifdef CONFIG_LV2_STATE // Save plugin complete state into a string. QString qtractorLv2Plugin::lv2_state_save (void) { QString s; qtractorLv2PluginType *pLv2Type = static_cast (type()); if (pLv2Type == nullptr) return s; LilvState *state = lilv_state_new_from_instance( lv2_plugin(), m_ppInstances[0], &g_lv2_urid_map, nullptr, nullptr, nullptr, nullptr, qtractor_lv2_get_port_value, this, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE, m_lv2_features); if (state == nullptr) return s; const char *uri = QTRACTOR_LV2_STATE_PREFIX; const char *pszState = lilv_state_to_string(g_lv2_world, &g_lv2_urid_map, &g_lv2_urid_unmap, state, uri, nullptr); if (pszState == nullptr) { lilv_state_free(state); return s; } s = QString::fromUtf8(pszState); ::free((void *) pszState); lilv_state_free(state); return s; } // Restore complete plugin state from a string. bool qtractorLv2Plugin::lv2_state_restore ( const QString& s ) { LilvState *state = lilv_state_new_from_string(g_lv2_world, &g_lv2_urid_map, s.toUtf8().constData()); if (state == nullptr) return false; const unsigned short iInstances = instances(); for (unsigned short i = 0; i < iInstances; ++i) { lilv_state_restore(state, m_ppInstances[i], qtractor_lv2_set_port_value, this, 0, m_lv2_features); } lilv_state_free(state); return true; } #endif // CONFIG_LV2_STATE //---------------------------------------------------------------------------- // qtractorLv2PluginParam -- LV2 plugin control input port instance. // // Constructors. qtractorLv2Plugin::Param::Param ( qtractorLv2Plugin *pLv2Plugin, unsigned long iIndex ) : qtractorPlugin::Param(pLv2Plugin, iIndex), m_iPortHints(None) { const LilvPlugin *plugin = pLv2Plugin->lv2_plugin(); const LilvPort *port = lilv_plugin_get_port_by_index(plugin, iIndex); // Set nominal parameter name... LilvNode *name = lilv_port_get_name(plugin, port); setName(lilv_node_as_string(name)); lilv_node_free(name); // Get port properties and set hints... if (lilv_port_has_property(plugin, port, g_lv2_toggled_prop)) m_iPortHints |= Toggled; if (lilv_port_has_property(plugin, port, g_lv2_integer_prop)) m_iPortHints |= Integer; if (lilv_port_has_property(plugin, port, g_lv2_sample_rate_prop)) m_iPortHints |= SampleRate; if (lilv_port_has_property(plugin, port, g_lv2_logarithmic_prop)) m_iPortHints |= Logarithmic; // Initialize range values... LilvNode *vdef; LilvNode *vmin; LilvNode *vmax; lilv_port_get_range(plugin, port, &vdef, &vmin, &vmax); setMinValue(vmin ? lilv_node_as_float(vmin) : 0.0f); setMaxValue(vmax ? lilv_node_as_float(vmax) : 1.0f); setDefaultValue(vdef ? lilv_node_as_float(vdef) : 0.0f); if (vdef) lilv_node_free(vdef); if (vmin) lilv_node_free(vmin); if (vmax) lilv_node_free(vmax); // Have scale points (display values) // m_display.clear(); LilvScalePoints *points = lilv_port_get_scale_points(plugin, port); if (points) { LILV_FOREACH(scale_points, iter, points) { const LilvScalePoint *point = lilv_scale_points_get(points, iter); const LilvNode *value = lilv_scale_point_get_value(point); const LilvNode *label = lilv_scale_point_get_label(point); if (value && label) { float fValue = lilv_node_as_float(value); QString sLabel = lilv_node_as_string(label); m_display.insert(QString::number(fValue), sLabel); } } lilv_scale_points_free(points); } // Initialize port value... // reset(); -- deferred to qtractorPlugin::addParam(); } // Port range hints predicate methods. bool qtractorLv2Plugin::Param::isBoundedBelow (void) const { return true; } bool qtractorLv2Plugin::Param::isBoundedAbove (void) const { return true; } bool qtractorLv2Plugin::Param::isDefaultValue (void) const { return true; } bool qtractorLv2Plugin::Param::isLogarithmic (void) const { return (m_iPortHints & Logarithmic); } bool qtractorLv2Plugin::Param::isSampleRate (void) const { return (m_iPortHints & SampleRate); } bool qtractorLv2Plugin::Param::isInteger (void) const { return (m_iPortHints & Integer); } bool qtractorLv2Plugin::Param::isToggled (void) const { return (m_iPortHints & Toggled); } bool qtractorLv2Plugin::Param::isDisplay (void) const { return !m_display.isEmpty(); } // Current display value. QString qtractorLv2Plugin::Param::display (void) const { // Check if current value is mapped... if (isDisplay()) { float fValue = value(); if (isInteger()) fValue = ::rintf(fValue); const QString& sValue = QString::number(fValue); if (m_display.contains(sValue)) return m_display.value(sValue); } // Default parameter display value... return qtractorPlugin::Param::display(); } #ifdef CONFIG_LV2_PATCH //---------------------------------------------------------------------- // qtractorLv2Plugin::Property -- LV2 Patch/property registry item. // // Constructor. qtractorLv2Plugin::Property::Property ( qtractorLv2Plugin *pLv2Plugin, unsigned long iProperty, const LilvNode *property ) : qtractorPlugin::Property(pLv2Plugin, iProperty) { static const char *s_types[] = { LV2_ATOM__Bool, LV2_ATOM__Int, LV2_ATOM__Long, LV2_ATOM__Float, LV2_ATOM__Double, LV2_ATOM__String, LV2_ATOM__Path, nullptr }; LilvNode *label_uri = lilv_new_uri(g_lv2_world, LILV_NS_RDFS "label"); LilvNode *range_uri = lilv_new_uri(g_lv2_world, LILV_NS_RDFS "range"); const char *prop_uri = lilv_node_as_uri(property); setKey(prop_uri); LilvNodes *nodes = lilv_world_find_nodes( g_lv2_world, property, label_uri, nullptr); LilvNode *label = (nodes ? lilv_nodes_get_first(nodes) : label_uri); setName(label ? lilv_node_as_string(label) : prop_uri); m_type = 0; for (int i = 0; s_types[i]; ++i) { const char *type = s_types[i]; LilvNode *range = lilv_new_uri(g_lv2_world, type); const bool has_range = lilv_world_ask( g_lv2_world, property, range_uri, range); lilv_node_free(range); if (has_range) { m_type = qtractorLv2Plugin::lv2_urid_map(type); break; } } LilvNode *prop_min = lilv_world_get( g_lv2_world, property, g_lv2_minimum_prop, nullptr); LilvNode *prop_max = lilv_world_get( g_lv2_world, property, g_lv2_maximum_prop, nullptr); LilvNode *prop_def = lilv_world_get( g_lv2_world, property, g_lv2_default_prop, nullptr); if (m_type == g_lv2_urids.atom_Bool) { setMinValue(float(prop_min ? lilv_node_as_bool(prop_min) : false)); setMaxValue(float(prop_max ? lilv_node_as_bool(prop_max) : true)); setDefaultValue(float(prop_def ? lilv_node_as_bool(prop_def) : false)); } else if (m_type == g_lv2_urids.atom_Int || m_type == g_lv2_urids.atom_Long) { setMinValue(float(prop_min ? lilv_node_as_int(prop_min) : 0)); setMaxValue(float(prop_max ? lilv_node_as_int(prop_max) : INT32_MAX)); setDefaultValue(float(prop_def ? lilv_node_as_int(prop_def) : 0)); } else if (m_type == g_lv2_urids.atom_Float || m_type == g_lv2_urids.atom_Double) { setMinValue(prop_min ? lilv_node_as_float(prop_min) : 0.0f); setMaxValue(prop_max ? lilv_node_as_float(prop_max) : 1.0f); setDefaultValue(prop_def ? lilv_node_as_float(prop_def) : 0.0f); } if (prop_min) lilv_node_free(prop_min); if (prop_max) lilv_node_free(prop_max); if (prop_def) lilv_node_free(prop_def); if (nodes) lilv_nodes_free(nodes); lilv_node_free(label_uri); lilv_node_free(range_uri); } // Property predicates. bool qtractorLv2Plugin::Property::isToggled (void) const { return (m_type == g_lv2_urids.atom_Bool); } bool qtractorLv2Plugin::Property::isInteger (void) const { return (m_type == g_lv2_urids.atom_Int || m_type == g_lv2_urids.atom_Long); } bool qtractorLv2Plugin::Property::isString (void) const { return (m_type == g_lv2_urids.atom_String); } bool qtractorLv2Plugin::Property::isPath (void) const { return (m_type == g_lv2_urids.atom_Path); } // Fake property predicates. bool qtractorLv2Plugin::Property::isBoundedBelow (void) const { return !isString() && !isPath(); } bool qtractorLv2Plugin::Property::isBoundedAbove (void) const { return !isString() && !isPath(); } bool qtractorLv2Plugin::Property::isDefaultValue (void) const { return !isString() && !isPath(); } bool qtractorLv2Plugin::Property::isLogarithmic (void) const { return false; } bool qtractorLv2Plugin::Property::isSampleRate (void) const { return false; } bool qtractorLv2Plugin::Property::isDisplay (void) const { return false; } // Virtual observer updater. void qtractorLv2Plugin::Property::update ( float fValue, bool bUpdate ) { qtractorPlugin::Property::update(fValue, bUpdate); if (bUpdate) { qtractorPlugin *pPlugin = plugin(); qtractorPluginType *pType = pPlugin->type(); if (pType->typeHint() == qtractorPluginType::Lv2) { qtractorLv2Plugin *pLv2Plugin = static_cast (pPlugin); if (pLv2Plugin) pLv2Plugin->lv2_property_update(index()); } } } #endif // CONFIG_LV2_PATCH #endif // CONFIG_LV2 // end of qtractorLv2Plugin.cpp qtractor-1.5.11/src/PaxHeaders/qtractorFileListView.h0000644000000000000000000000013215124701674017611 xustar0030 mtime=1767080892.785263487 30 atime=1767080892.785263487 30 ctime=1767080892.785263487 qtractor-1.5.11/src/qtractorFileListView.h0000644000175000001440000002174415124701674017611 0ustar00rncbcusers// qtractorFileListView.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorFileListView_h #define __qtractorFileListView_h #include "qtractorFileList.h" #include // Forward declarations. class qtractorFileListView; class qtractorFileListItem; class qtractorFileGroupItem; class qtractorRubberBand; class qtractorDocument; class QDomElement; //---------------------------------------------------------------------------- // qtractorFileListView -- Group/File list view, supporting drag-n-drop. // class qtractorFileListView : public QTreeWidget { Q_OBJECT public: // Constructor. qtractorFileListView(qtractorFileList::Type iFileType, QWidget *pParent = nullptr); // Default destructor. virtual ~qtractorFileListView(); // File list type property. void setFileType(qtractorFileList::Type iFileType); qtractorFileList::Type fileType() const; // QListViewItem::type() return values. enum ItemType { GroupItem = 1001, FileItem = 1002, ChannelItem = 1003 }; // Prompt for proper file list open. QStringList openFileNames(); // Add a new group/file item, optionally under a given group. qtractorFileGroupItem *addGroupItem(const QString& sName, qtractorFileGroupItem *pParentItem = nullptr); qtractorFileListItem *addFileItem(const QString& sPath, qtractorFileGroupItem *pParentItem = nullptr); // Remove existing group/file item. void removeGroupItem(const QString& sName); void removeFileItem(const QString& sPath); // Current group/file item accessors... qtractorFileGroupItem *currentGroupItem() const; qtractorFileListItem *currentFileItem() const; // Find a group/file item, given its name. qtractorFileGroupItem *findGroupItem(const QString& sName) const; qtractorFileListItem *findFileItem(const QString& sPath) const; // Make as current selection an existing file item. qtractorFileListItem *selectFileItem( const QString& sPath, int iChannel = -1); // Add a new group item below the current one. void newGroup(); // Add a new file item below the current group one. void openFile(); // Copy/cut current file item(s) to clipboard. void copyItem(bool bCut = false); // Paste file item(s) from clipboard. void pasteItem(); // Rename current group/file item. void renameItem(); // Remove current group/file item(s). void removeItem(); // Clean-up unused file items. void cleanupItem(QTreeWidgetItem *pItem); void cleanup(); // Emit actiovation signal for given item... void activateItem(QTreeWidgetItem *pItem = nullptr); // Master clean-up. void clear(); // Auto-open timer methods. void setAutoOpenTimeout(int iAutoOpenTimeout); int autoOpenTimeout() const; // Recently used directory, if any. void setRecentDir(const QString& sRecentDir); const QString& recentDir() const; // Elemental loader/saver... bool loadElement(qtractorDocument *pDocument, QDomElement *pElement); bool saveElement(qtractorDocument *pDocument, QDomElement *pElement); signals: // File entry activated. void selected(const QString& sFilename, int iTrackChannel, bool bSelect); // File entry activated. void activated(const QString& sFilename, int iTrackChannel); // Contents change signal; void contentsChanged(); protected slots: // In-place toggle slot. void itemClickedSlot(QTreeWidgetItem *pItem); // In-place activate slot. void itemActivatedSlot(QTreeWidgetItem *pItem); // In-place open/close slot. void itemExpandedSlot(QTreeWidgetItem *pItem); void itemCollapsedSlot(QTreeWidgetItem *pItem); // Tracking of item changes (e.g in-place edits). void itemRenamedSlot(); // Auto-open timeout slot. void timeoutSlot(); protected: // Find and return the nearest group item... qtractorFileGroupItem *groupItem(QTreeWidgetItem *pItem) const; // Find a list view item, given its type and name. QTreeWidgetItem *findItem(const QString& sText, int iType) const; // Which column is the complete file path? virtual int pathColumn() const = 0; // Pure virtual file item creation; // must be implemented in derived classes. virtual qtractorFileListItem *createFileItem(const QString& sPath) = 0; // Prompt for proper file list open (pure virtual). virtual QStringList getOpenFileNames() = 0; // Trap for help/tool-tip events. bool eventFilter(QObject *pObject, QEvent *pEvent); // Drag-n-drop stuff -- reimplemented virtual methods. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseMoveEvent(QMouseEvent *pMouseEvent); void mouseReleaseEvent(QMouseEvent *pMouseEvent); void dragEnterEvent(QDragEnterEvent *pDragEnterEvent); void dragMoveEvent(QDragMoveEvent *pDragMoveEvent); void dragLeaveEvent(QDragLeaveEvent *pDragLeaveEvent); void dropEvent(QDropEvent *pDropEvent); // Drag-n-drop stuff. bool canDecodeEvent(QDropEvent *pDropEvent); bool canDropItem(QTreeWidgetItem *pDropItem) const; QTreeWidgetItem *dragDropItem(const QPoint& pos); // Ensure given item is brought to viewport visibility... void ensureVisibleItem(QTreeWidgetItem *pItem); // Show and move rubber-band item. void moveRubberBand(QTreeWidgetItem *pDropItem, bool bOutdent = false); // Drag-and-drop target method... QTreeWidgetItem *dropItem(QTreeWidgetItem *pDropItem, QTreeWidgetItem *pDragItem, bool bOutdent = false); // Internal recursive loaders/savers... bool loadListElement(qtractorDocument *pDocument, QDomElement *pElement, QTreeWidgetItem *pItem); bool saveListElement(qtractorDocument *pDocument, QDomElement *pElement, QTreeWidgetItem *pItem); private: // Major file type property. qtractorFileList::Type m_iFileType; // Auto-open timer. int m_iAutoOpenTimeout; QTimer *m_pAutoOpenTimer; // The point from where drag started. QPoint m_posDrag; // Item we'll eventually drag around. QTreeWidgetItem *m_pDragItem; // Item we'll eventually drop something. QTreeWidgetItem *m_pDropItem; // To show the point where drop will go. qtractorRubberBand *m_pRubberBand; // Last recently used directory. QString m_sRecentDir; }; //---------------------------------------------------------------------- // class qtractorFileGroupItem -- custom group list view item. // class qtractorFileGroupItem : public QTreeWidgetItem { public: // Constructors. qtractorFileGroupItem(const QString& sName, int iType = qtractorFileListView::GroupItem); // Default destructor. virtual ~qtractorFileGroupItem(); // Instance accessors. void setName(const QString& sName); QString name() const; qtractorFileListView *listView() const; qtractorFileGroupItem *groupItem() const; // To show up whether its open or not. void setOpen(bool bOpen); bool isOpen() const; // Virtual tooltip renderer. virtual QString toolTip() const; }; //---------------------------------------------------------------------- // class qtractorFileListItem -- custom file list view item. // class qtractorFileListItem : public qtractorFileGroupItem { public: // Constructors. qtractorFileListItem(const QString& sPath); // Default destructor. ~qtractorFileListItem(); // Full path accessor. const QString& path() const; private: // File item full path. QString m_sPath; }; //---------------------------------------------------------------------- // class qtractorFileChannelItem -- custom channel list view item. // class qtractorFileChannelItem : public qtractorFileGroupItem { public: // Constructors. qtractorFileChannelItem(qtractorFileListItem *pFileItem, const QString& sName, unsigned short iChannel); // Default destructor. ~qtractorFileChannelItem(); // File chhannel accessor. unsigned short channel() const; // Full path accessor. const QString path() const; private: // File channel identifier. unsigned short m_iChannel; }; //---------------------------------------------------------------------- // class qtractorFileChannelDrag -- custom file channel drag object. // class qtractorFileChannelDrag { public: struct Item { QString path; unsigned short channel; }; typedef QList List; // Encode method. static void encode(QMimeData *pMimeData, const List& items); // Decode methods. static bool canDecode(const QMimeData *pMimeData); static List decode(const QMimeData *pMimeData); }; #endif // __qtractorFileListView_h // end of qtractorFileListView.h qtractor-1.5.11/src/PaxHeaders/qtractorMidiTimer.h0000644000000000000000000000013215124701674017126 xustar0030 mtime=1767080892.795263445 30 atime=1767080892.795263445 30 ctime=1767080892.795263445 qtractor-1.5.11/src/qtractorMidiTimer.h0000644000175000001440000000504315124701674017120 0ustar00rncbcusers// qtractorMidiTimer.h // /**************************************************************************** Copyright (C) 2005-2019, 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 __qtractorMidiTimer_h #define __qtractorMidiTimer_h #include //---------------------------------------------------------------------- // class qtractorMidiTimer -- ALSA sequencer timer stuff (singleton). // class qtractorMidiTimer { public: // Constructor. qtractorMidiTimer(); // Destructor. ~qtractorMidiTimer(); // Returns the number of available timers. int count() const; // Index of the a timer key. int indexOf(int iKey) const; // Key from index. int key(int iIndex) const; // Name from index. const QString& name(int iIndex) const; // ALSA sequencer timer key stuff. class Key { public: // Constructors. Key(int iAlsaTimer = 0) { setAlsaTimer(iAlsaTimer); } Key(int iClass, int iCard, int iDevice, int iSubDev) { setAlsaTimer(iClass, iCard, iDevice, iSubDev); } Key(const Key& key) { setAlsaTimer(key.alsaTimer()); } // Getters. int alsaTimer() const { return m_iAlsaTimer; } int alsaTimerClass() const { return int((m_iAlsaTimer & 0x7f000000) >> 24); } int alsaTimerCard() const { return int((m_iAlsaTimer & 0x007f0000) >> 16); } int alsaTimerDevice() const { return int((m_iAlsaTimer & 0x00007f00) >> 8); } int alsaTimerSubDev() const { return int(m_iAlsaTimer & 0x0000007f); } protected: // Setters. void setAlsaTimer(int iAlsaTimer); void setAlsaTimer(int iClass, int iCard, int iDevice, int iSubDev); private: // Queue timer int m_iAlsaTimer; }; private: // Instance variables; QStringList m_names; QList m_keys; }; #endif // __qtractorMidiTimer_h // end of qtractorMidiTimer.h qtractor-1.5.11/src/PaxHeaders/qtractorComboBox.h0000644000000000000000000000012415124701674016754 xustar0028 mtime=1767080892.7822635 28 atime=1767080892.7822635 28 ctime=1767080892.7822635 qtractor-1.5.11/src/qtractorComboBox.h0000644000175000001440000000450215124701674016744 0ustar00rncbcusers// qtractorComboBox.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorComboBox_h #define __qtractorComboBox_h #include //------------------------------------------------------------------------- // qtractorAudioFileTypeComboBox - A simple combo-box custom widget. class qtractorAudioFileTypeComboBox : public QComboBox { public: // Constructor. qtractorAudioFileTypeComboBox(QWidget *pParent = nullptr); // Current type accessors. void setCurrentType(const QString& sExt, int iType = 0); const void *currentHandle() const; int currentType(const void *handle = nullptr) const; QString currentExt(const void *handle = nullptr) const; // Indexed-type accessors. int indexOf(const QString& sExt, int iType = 0) const; const void *handleOf(int iIndex) const; }; //------------------------------------------------------------------------- // qtractorAudioFileFormatComboBox - A simple combo-box custom widget. class qtractorAudioFileFormatComboBox : public QComboBox { public: // Constructor. qtractorAudioFileFormatComboBox(QWidget *pParent = nullptr); }; //------------------------------------------------------------------------- // qtractorMidiFileFormatComboBox - A simple combo-box custom widget. class qtractorMidiFileFormatComboBox : public QComboBox { public: // Constructor. qtractorMidiFileFormatComboBox(QWidget *pParent = nullptr); }; #endif // __qtractorComboBox_h // end of qtractorComboBox.h qtractor-1.5.11/src/PaxHeaders/qtractorZipFile.h0000644000000000000000000000013215124701674016605 xustar0030 mtime=1767080892.805263403 30 atime=1767080892.805263403 30 ctime=1767080892.805263403 qtractor-1.5.11/src/qtractorZipFile.h0000644000175000001440000000516115124701674016600 0ustar00rncbcusers// qtractorZipFile.h // /**************************************************************************** Copyright (C) 2010-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. *****************************************************************************/ /* Most of this code was originally borrowed, stirred, mangled * and finally adapted from the Qt 4.6 source code (LGPL). * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(ies). * All rights reserved. * Contact: Nokia Corporation (qt-info@nokia.com) */ #ifndef __qtractorZipFile_h #define __qtractorZipFile_h #include //---------------------------------------------------------------------------- // qtractorZipFile -- Custom ZIP file archive class. // class qtractorZipDevice; class qtractorZipFile { public: // Constructors. qtractorZipFile(const QString& sFilename, QIODevice::OpenMode mode = QIODevice::ReadOnly); explicit qtractorZipFile(QIODevice *pDdevice); // Destructor. ~qtractorZipFile(); enum Status { NoError = 0, FileOpenError, FileReadError, FileWriteError, FilePermissionsError, FileError }; Status status() const; bool isReadable() const; bool isWritable() const; bool exists() const; bool extractFile(const QString& sFilename); bool extractAll(); void setPrefix(const QString& sPrefix); const QString& prefix () const; QString alias(const QString& sFilename, const QString& sPrefix = QString(), bool bTemp = false) const; bool addFile(const QString& sFilename, const QString& sAlias = QString()); bool addDirectory(const QString& sDirectory); bool processAll(); void close(); unsigned int totalUncompressed() const; unsigned int totalCompressed() const; unsigned int totalProcessed() const; private: // Disable copy constructor. qtractorZipFile(const qtractorZipFile&); // Implementation device. qtractorZipDevice *m_pZip; }; #endif // __qtractorZipFile_h // end of qtractorZipFile.h qtractor-1.5.11/src/PaxHeaders/qtractorMixer.h0000644000000000000000000000013215124701674016327 xustar0030 mtime=1767080892.796263441 30 atime=1767080892.796263441 30 ctime=1767080892.796263441 qtractor-1.5.11/src/qtractorMixer.h0000644000175000001440000002646615124701674016335 0ustar00rncbcusers// qtractorMixer.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorMixer_h #define __qtractorMixer_h #include "qtractorEngine.h" #include "qtractorTrackButton.h" #include #include #include #include #include // Forward declarations. class qtractorMixer; class qtractorMixerRack; class qtractorMixerRackWidget; class qtractorMixerStrip; class qtractorMixerMeter; class qtractorPluginListView; class qtractorMidiManager; class qtractorAudioBus; class QHBoxLayout; class QVBoxLayout; class QGridLayout; class QPushButton; class QLabel; //---------------------------------------------------------------------------- // qtractorMonitorButton -- Monitor observer tool button. class qtractorMonitorButton : public qtractorMidiControlButton { Q_OBJECT public: // Constructors. qtractorMonitorButton(qtractorTrack *pTrack, QWidget *pParent = nullptr); qtractorMonitorButton(qtractorBus *pBus, QWidget *pParent = nullptr); // Destructor. ~qtractorMonitorButton(); // Specific track accessors. void setTrack(qtractorTrack *pTrack); qtractorTrack *track() const { return m_pTrack; } // Specific bus accessors. void setBus(qtractorBus *pBus); qtractorBus *bus() const { return m_pBus; } protected slots: // Special toggle slot. void toggledSlot(bool bOn); protected: // Common initializer. void initMonitorButton(); // Visitor setup. void updateMonitor(); // Visitors overload. void updateValue(float fValue); private: // Instance variables. qtractorTrack *m_pTrack; qtractorBus *m_pBus; }; //---------------------------------------------------------------------------- // qtractorMixerStrip -- Mixer strip widget. class qtractorMixerStrip : public QFrame { Q_OBJECT public: // Constructors. qtractorMixerStrip(qtractorMixerRack *pRack, qtractorBus *pBus, qtractorBus::BusMode busMode); qtractorMixerStrip(qtractorMixerRack *pRack, qtractorTrack *pTrack); // Default destructor. ~qtractorMixerStrip(); // Clear/suspend delegates. void clear(); // Delegated properties accessors. void setMonitor(qtractorMonitor *pMonitor); qtractorMonitor *monitor() const; // Child accessors. qtractorPluginListView *pluginListView() const { return m_pPluginListView; } qtractorMixerMeter *meter() const { return m_pMixerMeter; } // Bus property accessors. void setBus(qtractorBus *pBus); qtractorBus *bus() const { return m_pBus; } qtractorBus::BusMode busMode() const { return m_busMode; } // Track property accessors. void setTrack(qtractorTrack *pTrack); qtractorTrack *track() const { return m_pTrack; } // Selection methods. void setSelected(bool bSelected); bool isSelected() const { return m_bSelected; } // Hacko-list-management marking... void setMark(int iMark) { m_iMark = iMark; } int mark() const { return m_iMark; } // Special bus dispatchers. void busConnections(qtractorBus::BusMode busMode); void busMonitor(bool bMonitor); // Track monitor dispatcher. void trackMonitor(bool bMonitor); // Update a MIDI mixer strip, given its MIDI manager handle. void updateMidiManager(qtractorMidiManager *pMidiManager); // Retrieve the MIDI manager from a mixer strip, if any.... qtractorMidiManager *midiManager() const; public slots: // Bus context menu slots. void busInputsSlot(); void busOutputsSlot(); void busMonitorSlot(); void busPropertiesSlot(); protected slots: // Bus connections button notification. void busButtonSlot(); // Meter slider change slots. void panningChangedSlot(float); void gainChangedSlot(float); protected: // Common mixer-strip initializer. void initMixerStrip(); void updateMidiLabel(); void updateName(); // Mouse selection event handlers. void mousePressEvent(QMouseEvent *); // Mouse selection event handlers. void mouseDoubleClickEvent(QMouseEvent *); private: // Local instance variables. qtractorMixerRack *m_pRack; qtractorBus *m_pBus; qtractorBus::BusMode m_busMode; qtractorTrack *m_pTrack; // Local widgets. class IconLabel; QVBoxLayout *m_pLayout; IconLabel *m_pLabel; QFrame *m_pRibbon; qtractorPluginListView *m_pPluginListView; QHBoxLayout *m_pButtonLayout; qtractorMonitorButton *m_pMonitorButton; qtractorTrackButton *m_pRecordButton; qtractorTrackButton *m_pMuteButton; qtractorTrackButton *m_pSoloButton; qtractorMixerMeter *m_pMixerMeter; QPushButton *m_pBusButton; QLabel *m_pMidiLabel; // Selection stuff. bool m_bSelected; // Hacko-list-management mark... int m_iMark; }; //---------------------------------------------------------------------------- // qtractorMixerRackWidget -- Mixer strip rack widget decl. class qtractorMixerRackWidget : public QScrollArea { Q_OBJECT public: // Constructor. qtractorMixerRackWidget(qtractorMixerRack *pRack); // Default destructor. ~qtractorMixerRackWidget(); // The mixer strip workspace widget. QWidget *workspace() const { return m_pWorkspaceWidget; } // Add/remove a mixer strip to/from rack workspace. void addStrip(qtractorMixerStrip *pStrip); void removeStrip( qtractorMixerStrip *pStrip); // Multi-row workspace layout method. void updateWorkspace(); protected: // Resize event handler. void resizeEvent(QResizeEvent *); // Context menu request event handler. void contextMenuEvent(QContextMenuEvent *); // Mouse click event handler. void mousePressEvent(QMouseEvent *); // Initial minimum widget extents. QSize sizeHint() const { return QSize(80, 280); } private: // Instance variables. qtractorMixerRack *m_pRack; // Layout widgets. QGridLayout *m_pWorkspaceLayout; QWidget *m_pWorkspaceWidget; }; //---------------------------------------------------------------------------- // qtractorMixerRack -- Mixer strip rack. class qtractorMixerRack : public QDockWidget { Q_OBJECT public: // Constructor. qtractorMixerRack(qtractorMixer *pMixer, const QString& sTitle); // Default destructor. ~qtractorMixerRack(); // The main mixer widget accessor. qtractorMixer *mixer() const { return m_pMixer; } // The mixer strip workspace widget accessor. QWidget *workspace() const { return m_pRackWidget->workspace(); } // Strip list primitive methods. void addStrip(qtractorMixerStrip *pStrip); void removeStrip(qtractorMixerStrip *pStrip); // Find a mixer strip, given its rack workspace position. qtractorMixerStrip *stripAt(const QPoint& pos) const; // Find a mixer strip, given its monitor handle. qtractorMixerStrip *findStrip(qtractorMonitor *pMonitor) const; // Update a mixer strip on rack list. void updateStrip(qtractorMixerStrip *pStrip, qtractorMonitor *pMonitor); // Complete rack recycle. void clear(); void setSelectedStrip(qtractorMixerStrip *pStrip); qtractorMixerStrip *selectedStrip() const { return m_pSelectedStrip; } void setSelectedStrip2(qtractorMixerStrip *pStrip); qtractorMixerStrip *selectedStrip2() const { return m_pSelectedStrip2; } // Hacko-list-management marking... void markStrips(int iMark); void cleanStrips(int iMark); // Multi-row workspace layout method. void updateWorkspace() { m_pRackWidget->updateWorkspace(); } // Find a mixer strip, given its MIDI-manager handle. qtractorMixerStrip *findMidiManagerStrip( qtractorMidiManager *pMidiManager) const; // Find all the MIDI mixer strip, given an audio output bus handle. QList findAudioOutputBusStrips( qtractorAudioBus *pAudioOutputBus) const; // Whether a strip type is hidden. void setStripTypeHidden(qtractorTrack::TrackType stripType); bool isStripTypeHidden(qtractorTrack::TrackType stripType) const; // Check for this being any special rack. bool isInputRack() const; bool isTrackRack() const; bool isOutputRack() const; signals: // Selection changed signal. void selectionChanged(); public slots: // Bus context menu slots. void busPropertiesSlot(); void busAudioStripsSlot(); void busMidiStripsSlot(); private: // Instance properties. qtractorMixer *m_pMixer; // The Strips list. typedef QHash Strips; Strips m_strips; // Selection stuff. qtractorMixerStrip *m_pSelectedStrip; qtractorMixerStrip *m_pSelectedStrip2; // The inner rack scroll-area/workspace widget. qtractorMixerRackWidget *m_pRackWidget; // Current hidden-strip type. qtractorTrack::TrackType m_hiddenStripType; }; //---------------------------------------------------------------------------- // qtractorMixer -- Mixer widget. class qtractorMixer : public QMainWindow { Q_OBJECT public: // Constructor. qtractorMixer(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()); // Default destructor. ~qtractorMixer(); // The mixer strips rack accessors. qtractorMixerRack *inputRack() const { return m_pInputRack; } qtractorMixerRack *trackRack() const { return m_pTrackRack; } qtractorMixerRack *outputRack() const { return m_pOutputRack; } // Current selected track accessors. void setCurrentTrack(qtractorTrack *pTrack); qtractorTrack *currentTrack() const; // Update buses and tracks'racks. void updateBuses(bool bReset = false); void updateTracks(bool bReset = false); // Update mixer rack, checking whether the monitor actually exists. void updateBusStrip(qtractorMixerRack *pRack, qtractorBus *pBus, qtractorBus::BusMode busMode, bool bReset = false); void updateTrackStrip(qtractorTrack *pTrack, bool bReset = false); // Update a MIDI mixer strip, given its MIDI manager handle. void updateMidiManagerStrip(qtractorMidiManager *pMidiManager); // Find a MIDI mixer strip, given its MIDI manager handle. QList findAudioOutputBusStrips( qtractorAudioBus *pAudioOutputBus) const; // Complete mixer recycle. void clear(); // Multi-row workspace layout method. void updateWorkspaces(); // Current selected output bus (usually an Aux-Send target) accessors. void setSelectedOutputBus(qtractorBus *pOutputBus); qtractorBus *selectedOutputBus() const; protected: // Just about to notify main-window that we're closing. void closeEvent(QCloseEvent *); // Keyboard event handler. void keyPressEvent(QKeyEvent *); // Special dockables persistence methods. void loadMixerState(); void saveMixerState(); // Initial minimum widget extents. QSize sizeHint() const { return QSize(640, 420); } private: qtractorMixerRack *m_pInputRack; qtractorMixerRack *m_pTrackRack; qtractorMixerRack *m_pOutputRack; }; #endif // __qtractorMixer_h // end of qtractorMixer.h qtractor-1.5.11/src/PaxHeaders/qtractorPluginCommand.cpp0000644000000000000000000000013215124701674020333 xustar0030 mtime=1767080892.798263432 30 atime=1767080892.798263432 30 ctime=1767080892.798263432 qtractor-1.5.11/src/qtractorPluginCommand.cpp0000644000175000001440000006236015124701674020332 0ustar00rncbcusers// qtractorPluginCommand.cpp // /**************************************************************************** Copyright (C) 2005-2025, 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 "qtractorAbout.h" #include "qtractorPluginCommand.h" #include "qtractorPluginForm.h" #include "qtractorPluginListView.h" #include "qtractorInsertPlugin.h" #include "qtractorSession.h" #include "qtractorMidiManager.h" #include "qtractorMainForm.h" #include "qtractorTracks.h" #include "qtractorMixer.h" //---------------------------------------------------------------------- // class qtractorPluginCommand - implementation // // Constructor. qtractorPluginCommand::qtractorPluginCommand ( const QString& sName, qtractorPlugin *pPlugin ) : qtractorCommand(sName) { if (pPlugin) m_plugins.append(pPlugin); setRefresh(false); } // Destructor. qtractorPluginCommand::~qtractorPluginCommand (void) { if (isAutoDelete()) qDeleteAll(m_plugins); m_plugins.clear(); } // Add new plugin(s) command methods. bool qtractorPluginCommand::addPlugins (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // pSession->lock(); // Add all listed plugins, in order... QListIterator iter(m_plugins); while (iter.hasNext()) { qtractorPlugin *pPlugin = iter.next(); qtractorPluginList *pPluginList = pPlugin->list(); if (pPluginList) pPluginList->addPlugin(pPlugin); } // Avoid the disposal of the plugin reference(s). setAutoDelete(false); // pSession->unlock(); return true; } // Remove existing plugin(s) command methods. bool qtractorPluginCommand::removePlugins (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // pSession->lock(); // Unlink all listed plugins, in order... QListIterator iter(m_plugins); iter.toBack(); while (iter.hasPrevious()) { qtractorPlugin *pPlugin = iter.previous(); qtractorPluginList *pPluginList = pPlugin->list(); if (pPluginList) pPluginList->removePlugin(pPlugin); } // Allow the disposal of the plugin reference(s). setAutoDelete(true); // pSession->unlock(); return true; } //---------------------------------------------------------------------- // class qtractorAddPluginCommand - implementation // // Constructor. qtractorAddPluginCommand::qtractorAddPluginCommand ( qtractorPlugin *pPlugin ) : qtractorPluginCommand(QObject::tr("add plugin"), pPlugin) { } // Plugin insertion command methods. bool qtractorAddPluginCommand::redo (void) { return addPlugins(); } bool qtractorAddPluginCommand::undo (void) { return removePlugins(); } //---------------------------------------------------------------------- // class qtractorAddInsertPluginCommand - implementation // // Constructor. qtractorAddInsertPluginCommand::qtractorAddInsertPluginCommand ( qtractorPlugin *pPlugin ) : qtractorPluginCommand( QObject::tr("add insert"), pPlugin) { } // Plugin insertion command methods. bool qtractorAddInsertPluginCommand::redo (void) { return addPlugins(); } bool qtractorAddInsertPluginCommand::undo (void) { return removePlugins(); } //---------------------------------------------------------------------- // class qtractorAddAuxSendPluginCommand - implementation // // Constructor. qtractorAddAuxSendPluginCommand::qtractorAddAuxSendPluginCommand ( qtractorPlugin *pPlugin ) : qtractorPluginCommand( QObject::tr("add aux-send"), pPlugin) { } // Plugin insertion command methods. bool qtractorAddAuxSendPluginCommand::redo (void) { return addPlugins(); } bool qtractorAddAuxSendPluginCommand::undo (void) { return removePlugins(); } //---------------------------------------------------------------------- // class qtractorAddMidiControlPluginCommand - implementation // // Constructor. qtractorAddMidiControlPluginCommand::qtractorAddMidiControlPluginCommand ( qtractorPlugin *pPlugin ) : qtractorPluginCommand( QObject::tr("add MIDI controller"), pPlugin) { } // Plugin insertion command methods. bool qtractorAddMidiControlPluginCommand::redo (void) { return addPlugins(); } bool qtractorAddMidiControlPluginCommand::undo (void) { return removePlugins(); } //---------------------------------------------------------------------- // class qtractorAuxSendPluginCommand - implementation // // Constructor. qtractorAuxSendPluginCommand::qtractorAuxSendPluginCommand ( qtractorPlugin *pPlugin, const QString& sAuxSendBusName ) : qtractorPluginCommand(QObject::tr("aux-send bus"), pPlugin), m_sAuxSendBusName(sAuxSendBusName) { } // Plugin command methods. bool qtractorAuxSendPluginCommand::redo (void) { qtractorPlugin *pPlugin = plugins().first(); if (pPlugin == nullptr) return false; if ((pPlugin->type())->index() > 0) { qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (pPlugin); if (pAudioAuxSendPlugin == nullptr) return false; const QString sAudioBusName = pAudioAuxSendPlugin->audioBusName(); pAudioAuxSendPlugin->setAudioBusName(m_sAuxSendBusName, true); m_sAuxSendBusName = sAudioBusName; } else { qtractorMidiAuxSendPlugin *pMidiAuxSendPlugin = static_cast (pPlugin); if (pMidiAuxSendPlugin == nullptr) return false; const QString sMidiBusName = pMidiAuxSendPlugin->midiBusName(); pMidiAuxSendPlugin->setMidiBusName(m_sAuxSendBusName); m_sAuxSendBusName = sMidiBusName; } pPlugin->updateFormAuxSendBusName(); return true; } bool qtractorAuxSendPluginCommand::undo (void) { return redo(); } //---------------------------------------------------------------------- // class qtractorAuxSendPluginCommand - implementation // // Constructor. qtractorAuxSendIOMatrixCommand::qtractorAuxSendIOMatrixCommand ( qtractorPlugin *pPlugin, const QList& matrix ) : qtractorPluginCommand(QObject::tr("aux-send matrix"), pPlugin), m_matrix(matrix) { } // Plugin command methods. bool qtractorAuxSendIOMatrixCommand::redo (void) { qtractorPlugin *pPlugin = plugins().first(); if (pPlugin == nullptr) return false; if ((pPlugin->type())->index() == 0) return false; qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (pPlugin); if (pAudioAuxSendPlugin == nullptr) return false; const QList matrix = pAudioAuxSendPlugin->audioBusMatrix(); pAudioAuxSendPlugin->setAudioBusMatrix(m_matrix); m_matrix = matrix; return true; } bool qtractorAuxSendIOMatrixCommand::undo (void) { return redo(); } //---------------------------------------------------------------------- // class qtractorRemovePluginCommand - implementation // // Constructor. qtractorRemovePluginCommand::qtractorRemovePluginCommand ( qtractorPlugin *pPlugin ) : qtractorPluginCommand(QObject::tr("remove plugin"), pPlugin) { } // Plugin-removal command methods. bool qtractorRemovePluginCommand::redo (void) { return removePlugins(); } bool qtractorRemovePluginCommand::undo (void) { return addPlugins(); } //---------------------------------------------------------------------- // class qtractorInsertPluginCommand - implementation // // Constructor. qtractorInsertPluginCommand::qtractorInsertPluginCommand ( const QString& sName, qtractorPlugin *pPlugin, qtractorPlugin *pNextPlugin ) : qtractorPluginCommand(sName, pPlugin) { m_pNextPlugin = pNextPlugin; } // Plugin-insert command methods. bool qtractorInsertPluginCommand::redo (void) { qtractorPlugin *pPlugin = plugins().first(); if (pPlugin == nullptr) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Save the previous plugin alright... qtractorPluginList *pPluginList = pPlugin->list(); if (pPluginList == nullptr) return false; // pSession->lock(); qtractorPlugin *pNextPlugin = pPlugin->next(); // Insert it... pPluginList->insertPlugin(pPlugin, m_pNextPlugin); // Swap it nice, finally. m_pNextPlugin = pNextPlugin; // Whether to allow the disposal of the plugin reference. setAutoDelete(false); // pSession->unlock(); return true; } bool qtractorInsertPluginCommand::undo (void) { qtractorPlugin *pPlugin = plugins().first(); if (pPlugin == nullptr) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Save the previous track alright... qtractorPluginList *pPluginList = pPlugin->list(); if (pPluginList == nullptr) return false; // pSession->lock(); qtractorPlugin *pNextPlugin = pPlugin->next(); // Insert it... pPluginList->removePlugin(pPlugin); // Swap it nice, finally. m_pNextPlugin = pNextPlugin; // Whether to allow the disposal of the plugin reference. setAutoDelete(true); // pSession->unlock(); return true; } //---------------------------------------------------------------------- // class qtractorMovePluginCommand - implementation // // Constructor. qtractorMovePluginCommand::qtractorMovePluginCommand ( qtractorPlugin *pPlugin, qtractorPlugin *pNextPlugin, qtractorPluginList *pPluginList ) : qtractorInsertPluginCommand(QObject::tr("move plugin"), pPlugin, pNextPlugin) { m_pPluginList = pPluginList; // Special case for aux-sends moved into output buses // and not of same plugin chain-list... m_pAuxSendPlugin = nullptr; qtractorPluginType *pType = pPlugin->type(); if (pType && (pType->typeHint() == qtractorPluginType::AuxSend) && (pPluginList != pPlugin->list())) { if ((pPluginList->flags() & qtractorPluginList::AudioOutBus) && (pType->index() > 0)) { // index == channels > 0 => Audio aux-send. qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (pPlugin); if (pAudioAuxSendPlugin) { m_pAuxSendPlugin = pAudioAuxSendPlugin; m_sAuxSendBusName = pAudioAuxSendPlugin->audioBusName(); } } else // index == 0 => MIDI aux-send. if (pPluginList->flags() & qtractorPluginList::MidiOutBus) { qtractorMidiAuxSendPlugin *pMidiAuxSendPlugin = static_cast (pPlugin); if (pMidiAuxSendPlugin) { m_pAuxSendPlugin = pMidiAuxSendPlugin; m_sAuxSendBusName = pMidiAuxSendPlugin->midiBusName(); } } } } // Plugin-move command methods. bool qtractorMovePluginCommand::redo (void) { qtractorPlugin *pPlugin = plugins().first(); if (pPlugin == nullptr) return false; qtractorPluginType *pType = pPlugin->type(); if (pType == nullptr) return false; if (m_pPluginList == nullptr) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // pSession->lock(); // If we're changing plugin-lists, close the generic editor, // but reopen it here later, if it's currently visible... bool bReopenForm = false; if (m_pPluginList != pPlugin->list()) { bReopenForm = pPlugin->isFormVisible(); pPlugin->closeForm(true); } // Save the previous track alright... qtractorPlugin *pNextPlugin = pPlugin->next(); qtractorPluginList *pPluginList = pPlugin->list(); // Move it... m_pPluginList->movePlugin(pPlugin, nextPlugin()); // Special case for audio Aux-sends moved into output buses... if (m_pAuxSendPlugin) { if (pType->index() > 0) { // index == channels > 0 => Audio aux-send. qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (m_pAuxSendPlugin); if (pAudioAuxSendPlugin) { const QString sAuxSendBusName = pAudioAuxSendPlugin->audioBusName(); if (sAuxSendBusName.isEmpty()) pAudioAuxSendPlugin->setAudioBusName(m_sAuxSendBusName); else pAudioAuxSendPlugin->setAudioBusName(QString()); } } else { // index == 0 => MIDI aux-send. qtractorMidiAuxSendPlugin *pMidiAuxSendPlugin = static_cast (m_pAuxSendPlugin); if (pMidiAuxSendPlugin) { const QString sAuxSendBusName = pMidiAuxSendPlugin->midiBusName(); pMidiAuxSendPlugin->setMidiBusName(m_sAuxSendBusName); m_sAuxSendBusName = sAuxSendBusName; } } } // Swap it nice, finally. m_pPluginList = pPluginList; setNextPlugin(pNextPlugin); if (bReopenForm) pPlugin->openForm(); // pSession->unlock(); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) { pTracks->clearSelect(); pTracks->updateTrackList(); pTracks->updateTrackView(); } } return true; } bool qtractorMovePluginCommand::undo (void) { // As we swap the prev/state this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorActivatePluginCommand - implementation // // Constructor. qtractorActivatePluginCommand::qtractorActivatePluginCommand ( qtractorPlugin *pPlugin, bool bActivated ) : qtractorPluginCommand(QObject::tr("activate plugin"), pPlugin) { m_bActivated = bActivated; } // Plugin-activate command methods. bool qtractorActivatePluginCommand::redo (void) { // Save the toggled state alright... const bool bActivated = !m_bActivated; QListIterator iter(plugins()); while (iter.hasNext()) iter.next()->setActivatedEx(m_bActivated); // Swap it nice, finally. m_bActivated = bActivated; return true; } bool qtractorActivatePluginCommand::undo (void) { // As we toggle the prev/state this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorPresetPluginCommand - implementation // // Constructor. qtractorPresetPluginCommand::qtractorPresetPluginCommand ( qtractorPlugin *pPlugin, const QString& sPreset, const QStringList& vlist ) : qtractorPluginCommand(QObject::tr("preset plugin"), pPlugin) { m_sPreset = sPreset; m_vlist = vlist; } // Plugin-preset command methods. bool qtractorPresetPluginCommand::redo (void) { qtractorPlugin *pPlugin = plugins().first(); if (pPlugin == nullptr) return false; // Save the current toggled state alright... const QString sPreset = pPlugin->preset(); const QStringList vlist = pPlugin->valueList(); pPlugin->setPreset(m_sPreset); pPlugin->setValueList(m_vlist); pPlugin->realizeValues(); pPlugin->releaseValues(); // Swap it nice, finally. m_sPreset = sPreset; m_vlist = vlist; // Update the form, showing it up as necessary... pPlugin->refreshForm(); return true; } bool qtractorPresetPluginCommand::undo (void) { // As we swap the prev/state this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorResetPluginCommand - implementation // // Constructor. qtractorResetPluginCommand::qtractorResetPluginCommand ( qtractorPlugin *pPlugin ) : qtractorPluginCommand(QObject::tr("reset plugin"), pPlugin) { } // Plugin-reset command methods. bool qtractorResetPluginCommand::redo (void) { qtractorPlugin *pPlugin = plugins().first(); if (pPlugin == nullptr) return false; // Toggle/swap it nice... const QString sPreset = pPlugin->preset(); const QStringList vlist = pPlugin->valueList(); pPlugin->setPreset(m_sPreset); if (m_sPreset.isEmpty() || m_vlist.isEmpty()) { pPlugin->reset(); } else { pPlugin->setValueList(m_vlist); pPlugin->releaseValues(); } // Swap it nice. m_sPreset = sPreset; m_vlist = vlist; // Update the form, showing it up as necessary... pPlugin->refreshForm(); return true; } bool qtractorResetPluginCommand::undo (void) { // As we swap the prev/state this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorProgramPluginCommand - implementation // // Constructor. qtractorPluginProgramCommand::qtractorPluginProgramCommand ( qtractorPlugin *pPlugin, int iBank, int iProg ) : qtractorPluginCommand(QObject::tr("plugin program"), pPlugin) { m_iBank = iBank; m_iProg = iProg; } // Plugin-preset command methods. bool qtractorPluginProgramCommand::redo (void) { qtractorPlugin *pPlugin = plugins().first(); if (pPlugin == nullptr) return false; // Save the current toggled state alright... qtractorPluginList::MidiProgramSubject *pMidiProgramSubject = (pPlugin->list())->midiProgramSubject(); if (pMidiProgramSubject == nullptr) return false; const int iBank = pMidiProgramSubject->bank(); const int iProg = pMidiProgramSubject->prog(); pMidiProgramSubject->setProgram(m_iBank, m_iProg); m_iBank = iBank; m_iProg = iProg; return true; } bool qtractorPluginProgramCommand::undo (void) { // As we swap the prev/state this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorPluginAliasCommand - implementation // // Constructor. qtractorPluginAliasCommand::qtractorPluginAliasCommand ( qtractorPlugin *pPlugin, const QString& sAlias ) : qtractorPluginCommand(QObject::tr("plugin alias"), pPlugin) { m_sAlias = sAlias; } // Plugin-alias command methods. bool qtractorPluginAliasCommand::redo (void) { qtractorPlugin *pPlugin = plugins().first(); if (pPlugin == nullptr) return false; // Save the previous alias alright... const QString sAlias = pPlugin->alias(); pPlugin->setAlias(m_sAlias); pPlugin->updateEditorTitle(); pPlugin->updateListViews(true); pPlugin->refreshForm(); // Swap it nice, finally. m_sAlias = sAlias; return true; } bool qtractorPluginAliasCommand::undo (void) { // As we toggle the prev/state this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorPluginParamCommand - implementation // // Constructor. qtractorPluginParamCommand::qtractorPluginParamCommand ( qtractorPlugin::Param *pParam, float fValue, bool bUpdate ) : qtractorCommand(pParam->name()), m_pParam(pParam), m_fValue(fValue), m_bUpdate(bUpdate), m_pLastUpdatedParam(pParam) { setRefresh(false); } // Plugin-reset command methods. bool qtractorPluginParamCommand::redo (void) { qtractorPlugin *pPlugin = m_pParam->plugin(); if (pPlugin == nullptr) return false; // Set plugin parameter value... const float fValue = m_pParam->value(); m_pParam->setValue(m_fValue, m_bUpdate); // Set undo value... m_fValue = fValue; m_bUpdate = true; qtractorPlugin::Param *pLastUpdatedParam = pPlugin->lastUpdatedParam(); pPlugin->setLastUpdatedParam(m_pLastUpdatedParam); m_pLastUpdatedParam = pLastUpdatedParam; // Update the form, showing it up as necessary... pPlugin->updateFormDirtyCount(); // Update any GUI editor... // pPlugin->idleEditor(); return true; } bool qtractorPluginParamCommand::undo (void) { // As we swap the prev/value this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorPluginParamValuesCommand - impl. // // Constructor. qtractorPluginParamValuesCommand::qtractorPluginParamValuesCommand ( const QString& sName ) : qtractorCommand(sName) { setRefresh(false); } // Destructor. qtractorPluginParamValuesCommand::~qtractorPluginParamValuesCommand (void) { qDeleteAll(m_paramCommands); m_paramCommands.clear(); } // Param-values list builder. void qtractorPluginParamValuesCommand::updateParamValue ( qtractorPlugin::Param *pParam, float fValue, bool bUpdate ) { qtractorPlugin *pPlugin = pParam->plugin(); if (pPlugin == nullptr) return; if (pPlugin->isLastUpdatedParam(pParam)) { pParam->setValue(fValue, bUpdate); pPlugin->updateFormDirtyCount(); } else { m_paramCommands.append( new qtractorPluginParamCommand(pParam, fValue, bUpdate)); // pPlugin->setLastUpdatedParam(pParam); } } // Composite predicate. bool qtractorPluginParamValuesCommand::isEmpty (void) const { return m_paramCommands.isEmpty(); } // Plugin-values command methods. bool qtractorPluginParamValuesCommand::redo (void) { bool bRedo = true; QListIterator iter(m_paramCommands); while (bRedo && iter.hasNext()) bRedo = iter.next()->redo(); return bRedo; } bool qtractorPluginParamValuesCommand::undo (void) { bool bUndo = true; QListIterator iter(m_paramCommands); iter.toBack(); while (bUndo && iter.hasPrevious()) bUndo = iter.previous()->undo(); return bUndo; } //---------------------------------------------------------------------- // class qtractorPluginPropertyCommand - implementation // // Constructor. qtractorPluginPropertyCommand::qtractorPluginPropertyCommand ( qtractorPlugin::Property *pProp, const QVariant& value ) : qtractorCommand(pProp->name()), m_pProp(pProp), m_value(value), m_pLastUpdatedProperty(pProp) { setRefresh(false); } // Plugin-property command methods. bool qtractorPluginPropertyCommand::redo (void) { qtractorPlugin *pPlugin = m_pProp->plugin(); if (pPlugin == nullptr) return false; // Save the current toggled state alright... const QVariant value = m_pProp->variant(); m_pProp->setVariant(m_value, true); // Set undo value. m_value = value; qtractorPlugin::Property *pLastUpdatedProperty = pPlugin->lastUpdatedProperty(); pPlugin->setLastUpdatedProperty(m_pLastUpdatedProperty); m_pLastUpdatedProperty = pLastUpdatedProperty; #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_PATCH if (!m_pProp->isAutomatable()) { qtractorPluginType *pType = pPlugin->type(); if (pType->typeHint() == qtractorPluginType::Lv2) { qtractorLv2Plugin *pLv2Plugin = static_cast (pPlugin); if (pLv2Plugin) pLv2Plugin->lv2_property_update(m_pProp->index()); } } #endif #endif // Update the form, showing it up as necessary... pPlugin->updateFormDirtyCount(); // Update any GUI editor... // pPlugin->idleEditor(); // FIXME: Might no work the first time... pPlugin->refreshForm(); return true; } bool qtractorPluginPropertyCommand::undo (void) { // As we swap the prev/state this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorAudioOutputBusCommand - declaration. // // Constructor. qtractorAudioOutputBusCommand::qtractorAudioOutputBusCommand ( qtractorMidiManager *pMidiManager, bool bAudioOutputBus, bool bAudioOutputAutoConnect, const QString& sAudioOutputBusName ) : qtractorCommand(QObject::tr("dedicated audio outputs")), m_pMidiManager(pMidiManager), m_bAudioOutputBus(bAudioOutputBus), m_bAudioOutputAutoConnect(bAudioOutputAutoConnect), m_sAudioOutputBusName(sAudioOutputBusName) { } // Plugin audio ouput bus command methods. bool qtractorAudioOutputBusCommand::redo (void) { if (m_pMidiManager == nullptr) return false; const bool bAudioOutputBus = m_pMidiManager->isAudioOutputBus(); const bool bAudioOutputAutoConnect = m_pMidiManager->isAudioOutputAutoConnect(); const QString sAudioOutputBusName = m_pMidiManager->audioOutputBusName(); m_pMidiManager->setAudioOutputBusName(m_sAudioOutputBusName); m_pMidiManager->setAudioOutputAutoConnect(m_bAudioOutputAutoConnect); m_pMidiManager->setAudioOutputBus(m_bAudioOutputBus); m_sAudioOutputBusName = sAudioOutputBusName; m_bAudioOutputAutoConnect = bAudioOutputAutoConnect; m_bAudioOutputBus = bAudioOutputBus; return true; } bool qtractorAudioOutputBusCommand::undo (void) { return redo(); } //---------------------------------------------------------------------- // class qtractorDirectAccessParamCommand - implementation // // Constructor. qtractorDirectAccessParamCommand::qtractorDirectAccessParamCommand ( qtractorPlugin *pPlugin, long iDirectAccessParamIndex ) : qtractorPluginCommand(QObject::tr("direct access param"), pPlugin) { m_iDirectAccessParamIndex = iDirectAccessParamIndex; } // Plugin-change command methods. bool qtractorDirectAccessParamCommand::redo (void) { qtractorPlugin *pPlugin = plugins().first(); if (pPlugin == nullptr) return false; const long iDirectAccessParamIndex = pPlugin->directAccessParamIndex(); pPlugin->setDirectAccessParamIndex(m_iDirectAccessParamIndex); m_iDirectAccessParamIndex = iDirectAccessParamIndex; return true; } bool qtractorDirectAccessParamCommand::undo (void) { return redo(); } //---------------------------------------------------------------------- // class qtractorImportPluginsCommand - declaration. // // Constructor. qtractorImportPluginsCommand::qtractorImportPluginsCommand (void) : qtractorCommand(QObject::tr("import plugins")) { m_pAddCommand = new qtractorAddPluginCommand(); m_pRemoveCommand = new qtractorRemovePluginCommand(); } // Destructor. qtractorImportPluginsCommand::~qtractorImportPluginsCommand (void) { delete m_pRemoveCommand; delete m_pAddCommand; } // Import plugins command methods. bool qtractorImportPluginsCommand::redo (void) { return (m_pRemoveCommand->redo() && m_pAddCommand->redo()); } bool qtractorImportPluginsCommand::undo (void) { return (m_pAddCommand->undo() && m_pRemoveCommand->undo()); } // end of qtractorPluginCommand.cpp qtractor-1.5.11/src/PaxHeaders/qtractorEditRangeForm.h0000644000000000000000000000013215124701674017731 xustar0030 mtime=1767080892.784263491 30 atime=1767080892.784263491 30 ctime=1767080892.784263491 qtractor-1.5.11/src/qtractorEditRangeForm.h0000644000175000001440000000514715124701674017730 0ustar00rncbcusers// qtractorEditRangeForm.h // /**************************************************************************** Copyright (C) 2005-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. *****************************************************************************/ #ifndef __qtractorEditRangeForm_h #define __qtractorEditRangeForm_h #include "ui_qtractorEditRangeForm.h" // Forward declarations. class qtractorTimeScale; //---------------------------------------------------------------------------- // qtractorEditRangeForm -- UI wrapper form. class qtractorEditRangeForm : public QDialog { Q_OBJECT public: // Constructor. qtractorEditRangeForm(QWidget *pParent = nullptr); // Destructor. ~qtractorEditRangeForm(); // Set the current initial selection range. void setSelectionRange(unsigned long iSelectStart, unsigned long iSelectEnd); // Retrieve the current range, if the case arises. unsigned long rangeStart() const; unsigned long rangeEnd() const; // Range option flags. enum Option { None = 0, Clips = 1, Automation = 2, Loop = 4, Punch = 8, Markers = 16, TempoMap = 32 }; // Retrieve range option flags. unsigned int rangeOptions() const; protected: // Option flags accessors. void setOption(Option option, bool bOn); bool isOption(Option option) const; // Update options settings. void updateOptions(); protected slots: void optionsChanged(); void rangeChanged(); void formatChanged(int); void valueChanged(); void stabilizeForm(); void accept(); private: // The Qt-designer UI struct... Ui::qtractorEditRangeForm m_ui; // Instance variables... qtractorTimeScale *m_pTimeScale; // Initial static selection range. unsigned long m_iSelectStart; unsigned long m_iSelectEnd; // Applicable options; unsigned int m_options; // Pseudo-mutex. unsigned int m_iUpdate; }; #endif // __qtractorEditRangeForm_h // end of qtractorEditRangeForm.h qtractor-1.5.11/src/PaxHeaders/qtractorCurveCommand.h0000644000000000000000000000013215124701674017626 xustar0030 mtime=1767080892.783263496 30 atime=1767080892.783263496 30 ctime=1767080892.783263496 qtractor-1.5.11/src/qtractorCurveCommand.h0000644000175000001440000002071215124701674017620 0ustar00rncbcusers// qtractorCurveCommand.h // /**************************************************************************** Copyright (C) 2005-2025, 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 __qtractorCurveCommand_h #define __qtractorCurveCommand_h #include "qtractorCommand.h" #include "qtractorCurve.h" //---------------------------------------------------------------------- // class qtractorCurveBaseCommand - declaration. // class qtractorCurveBaseCommand : public qtractorCommand { public: // Constructor. qtractorCurveBaseCommand(const QString& sName); // Virtual command methods. bool redo(); bool undo(); protected: // Common executive method. virtual bool execute(bool bRedo); }; //---------------------------------------------------------------------- // class qtractorCurveCommand - declaration. // class qtractorCurveCommand : public qtractorCurveBaseCommand { public: // Constructor. qtractorCurveCommand(const QString& sName, qtractorCurve *pCurve); // Accessor. qtractorCurve *curve() const { return m_pCurve; } protected: // Instance variables. qtractorCurve *m_pCurve; }; //---------------------------------------------------------------------- // class qtractorCurveListCommand - declaration. // class qtractorCurveListCommand : public qtractorCurveBaseCommand { public: // Constructor. qtractorCurveListCommand(const QString& sName, qtractorCurveList *pCurveList); protected: // Instance variables. qtractorCurveList *m_pCurveList; }; //---------------------------------------------------------------------- // class qtractorCurveSelectCommand - declaration. // class qtractorCurveSelectCommand : public qtractorCurveListCommand { public: // Constructor. qtractorCurveSelectCommand( qtractorCurveList *pCurveList, qtractorCurve *pCurrentCurve); protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. qtractorCurve *m_pCurrentCurve; }; //---------------------------------------------------------------------- // class qtractorCurveModeCommand - declaration. // class qtractorCurveModeCommand : public qtractorCurveCommand { public: // Constructor. qtractorCurveModeCommand( qtractorCurve *pCurve, qtractorCurve::Mode mode); protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. qtractorCurve::Mode m_mode; }; //---------------------------------------------------------------------- // class qtractorCurveProcessCommand - declaration. // class qtractorCurveProcessCommand : public qtractorCurveCommand { public: // Constructor. qtractorCurveProcessCommand( qtractorCurve *pCurve, bool bProcess); protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. bool m_bProcess; }; //---------------------------------------------------------------------- // class qtractorCurveCaptureCommand - declaration. // class qtractorCurveCaptureCommand : public qtractorCurveCommand { public: // Constructor. qtractorCurveCaptureCommand( qtractorCurve *pCurve, bool bCapture); protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. bool m_bCapture; }; //---------------------------------------------------------------------- // class qtractorCurveLogarithmicCommand - declaration. // class qtractorCurveLogarithmicCommand : public qtractorCurveCommand { public: // Constructor. qtractorCurveLogarithmicCommand( qtractorCurve *pCurve, bool bLogarithmic); protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. bool m_bLogarithmic; }; //---------------------------------------------------------------------- // class qtractorCurveColorCommand - declaration. // class qtractorCurveColorCommand : public qtractorCurveCommand { public: // Constructor. qtractorCurveColorCommand( qtractorCurve *pCurve, const QColor& color); protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. QColor m_color; }; //---------------------------------------------------------------------- // class qtractorCurveProcessAllCommand - declaration. // class qtractorCurveProcessAllCommand : public qtractorCurveListCommand { public: // Constructor. qtractorCurveProcessAllCommand( qtractorCurveList *pCurveList, bool bProcessAll); protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. bool m_bProcessAll; }; //---------------------------------------------------------------------- // class qtractorCurveCaptureCommand - declaration. // class qtractorCurveCaptureAllCommand : public qtractorCurveListCommand { public: // Constructor. qtractorCurveCaptureAllCommand( qtractorCurveList *pCurveList, bool bCaptureAll); protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. bool m_bCaptureAll; }; //---------------------------------------------------------------------- // class qtractorCurveEditCommand - declaration. // class qtractorCurveEditCommand : public qtractorCurveCommand { public: // Constructor. qtractorCurveEditCommand( const QString& sName, qtractorCurve *pCurve); qtractorCurveEditCommand(qtractorCurve *pCurve); // Destructor. virtual ~qtractorCurveEditCommand(); // Primitive command methods. void addNode(qtractorCurve::Node *pNode); void moveNode(qtractorCurve::Node *pNode, unsigned long iFrame, float fValue); void removeNode(qtractorCurve::Node *pNode); void addEditList(qtractorCurveEditList *pEditList); // Composite predicate. bool isEmpty() const; protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. qtractorCurveEditList m_edits; }; //---------------------------------------------------------------------- // class qtractorCurveClearCommand - declaration. // class qtractorCurveClearCommand : public qtractorCurveEditCommand { public: // Constructor. qtractorCurveClearCommand(qtractorCurve *pCurve); }; //---------------------------------------------------------------------- // class qtractorCurveClearAllCommand - declaration. // class qtractorCurveClearAllCommand : public qtractorCurveListCommand { public: // Constructor. qtractorCurveClearAllCommand(qtractorCurveList *pCurveList); // Destructor. ~qtractorCurveClearAllCommand(); // Composite predicate. bool isEmpty() const; protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. QList m_commands; }; //---------------------------------------------------------------------- // class qtractorCurveEditListCommand - declaration. // class qtractorCurveEditListCommand : public qtractorCurveListCommand { public: // Constructor. qtractorCurveEditListCommand(qtractorCurveList *pCurveList); // Destructor. ~qtractorCurveEditListCommand(); // Composite predicate. bool isEmpty() const; protected: // Virtual executive method. bool execute(bool bRedo); private: // Instance variables. QList m_curveEditCommands; }; //---------------------------------------------------------------------- // class qtractorCurveCaptureListCommand - declaration. // class qtractorCurveCaptureListCommand : public qtractorCommand { public: // Constructor. qtractorCurveCaptureListCommand(); // Destructor. ~qtractorCurveCaptureListCommand(); // Curve list adder. void addCurveList(qtractorCurveList *pCurveList); // Composite predicate. bool isEmpty() const; // Virtual command methods. bool redo(); bool undo(); private: // Instance variables. QList m_commands; }; #endif // __qtractorCurveCommand_h // end of qtractorCurveCommand.h qtractor-1.5.11/src/PaxHeaders/vst30000644000000000000000000000013015124701674014132 xustar0029 mtime=1767080892.81732752 30 atime=1767080892.816263357 29 ctime=1767080892.81732752 qtractor-1.5.11/src/vst3/0000755000175000001440000000000015124701674014201 5ustar00rncbcusersqtractor-1.5.11/src/vst3/PaxHeaders/public.sdk0000644000000000000000000000013115124701742016165 xustar0030 mtime=1767080930.097106362 29 atime=1767080892.81732752 30 ctime=1767080930.097106362 qtractor-1.5.11/src/vst3/public.sdk/0000755000175000001440000000000015124701742016233 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/PaxHeaders/LICENSE.txt0000644000000000000000000000013215124701711020062 xustar0030 mtime=1767080905.201211203 30 atime=1767080905.201211203 30 ctime=1767080905.201211203 qtractor-1.5.11/src/vst3/public.sdk/LICENSE.txt0000644000175000001440000000235115124701711020053 0ustar00rncbcusers//----------------------------------------------------------------------------- MIT License Copyright (c) 2025, Steinberg Media Technologies GmbH Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //---------------------------------------------------------------------------------qtractor-1.5.11/src/vst3/public.sdk/PaxHeaders/README.md0000644000000000000000000000013215124701711017516 xustar0030 mtime=1767080905.201211203 30 atime=1767080905.201211203 30 ctime=1767080905.201211203 qtractor-1.5.11/src/vst3/public.sdk/README.md0000644000175000001440000000071115124701711017505 0ustar00rncbcusers# Welcome to VST 3 SDK public_sdk Here are located: - helper classes implementing **VST 3** Interfaces - samples of **VST 3** Hosting and **VST 3** plug-ins - **AAX** Wrapper - **AU** Wrapper - **AUv3** Wrapper - InterAppAudio ## License & Usage guidelines More details are found at [VST 3 SDK public_sdk License](https://forums.steinberg.net/t/vst-3-sdk-public-sdk-license/695592) ---- Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) qtractor-1.5.11/src/vst3/public.sdk/PaxHeaders/source0000644000000000000000000000012615124701711017465 xustar0029 mtime=1767080905.28721084 28 atime=1767080905.2763013 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/0000755000175000001440000000000015124701711017527 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/PaxHeaders/vst3stdsdk.cpp0000644000000000000000000000012715124701711022363 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst3stdsdk.cpp0000644000175000001440000000207715124701711022355 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst3stdsdk.cpp // Created by : Steinberg, 09/2008 // Description : include basic helpers cpp files // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/vstaudioeffect.cpp" #include "public.sdk/source/vst/vstcomponent.cpp" #include "public.sdk/source/vst/vstcomponentbase.cpp" #include "public.sdk/source/vst/vsteditcontroller.cpp" #include "public.sdk/source/vst/vstbus.cpp" #include "public.sdk/source/vst/vstparameters.cpp" qtractor-1.5.11/src/vst3/public.sdk/source/PaxHeaders/main0000644000000000000000000000013215124701711020406 xustar0030 mtime=1767080905.277062199 30 atime=1767080905.276536456 30 ctime=1767080905.277062199 qtractor-1.5.11/src/vst3/public.sdk/source/main/0000755000175000001440000000000015124701711020453 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/main/PaxHeaders/pluginfactory.h0000644000000000000000000000013215124701711023522 xustar0030 mtime=1767080905.277062199 30 atime=1767080905.277062199 30 ctime=1767080905.277062199 qtractor-1.5.11/src/vst3/public.sdk/source/main/pluginfactory.h0000644000175000001440000002030115124701711023506 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Core // // Category : Common Base Classes // Filename : public.sdk/source/main/pluginfactory.h // Created by : Steinberg, 01/2004 // Description : Standard Plug-In Factory // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ipluginbase.h" #include namespace Steinberg { //------------------------------------------------------------------------ class IPluginFactoryInternal : public FUnknown { public: using HostContextCallbackFunc = void (*) (FUnknown*); virtual void PLUGIN_API addHostContextCallback (HostContextCallbackFunc func) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IPluginFactoryInternal, 0x5A6AD11A, 0x22AF40F3, 0xBCA1C147, 0x506C88D9) //------------------------------------------------------------------------ /** Default Class Factory implementation. \ingroup sdkBase \see classFactoryMacros */ class CPluginFactory : public IPluginFactory3, public IPluginFactoryInternal { public: //------------------------------------------------------------------------ CPluginFactory (const PFactoryInfo& info); virtual ~CPluginFactory (); //--- --------------------------------------------------------------------- /** Registers a plug-in class with classInfo version 1, returns true for success. */ bool registerClass (const PClassInfo* info, FUnknown* (*createFunc) (void*), void* context = nullptr); /** Registers a plug-in class with classInfo version 2, returns true for success. */ bool registerClass (const PClassInfo2* info, FUnknown* (*createFunc) (void*), void* context = nullptr); /** Registers a plug-in class with classInfo Unicode version, returns true for success. */ bool registerClass (const PClassInfoW* info, FUnknown* (*createFunc) (void*), void* context = nullptr); /** Check if a class for a given classId is already registered. */ bool isClassRegistered (const FUID& cid); /** Remove all classes (no class exported) */ void removeAllClasses (); //------------------------------------------------------------------------ DECLARE_FUNKNOWN_METHODS //---from IPluginFactory------ tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) SMTG_OVERRIDE; int32 PLUGIN_API countClasses () SMTG_OVERRIDE; tresult PLUGIN_API getClassInfo (int32 index, PClassInfo* info) SMTG_OVERRIDE; tresult PLUGIN_API createInstance (FIDString cid, FIDString _iid, void** obj) SMTG_OVERRIDE; //---from IPluginFactory2----- tresult PLUGIN_API getClassInfo2 (int32 index, PClassInfo2* info) SMTG_OVERRIDE; //---from IPluginFactory3----- tresult PLUGIN_API getClassInfoUnicode (int32 index, PClassInfoW* info) SMTG_OVERRIDE; tresult PLUGIN_API setHostContext (FUnknown* context) SMTG_OVERRIDE; //---from IPluginFactoryInternal void PLUGIN_API addHostContextCallback (HostContextCallbackFunc func) SMTG_OVERRIDE; //------------------------------------------------------------------------ protected: /// @cond struct PClassEntry { //----------------------------------- PClassInfo2 info8; PClassInfoW info16; FUnknown* (*createFunc) (void*); void* context; bool isUnicode; //----------------------------------- }; /// @endcond PFactoryInfo factoryInfo; PClassEntry* classes; int32 classCount; int32 maxClassCount; std::vector hostContextCallbacks; bool growClasses (); }; extern CPluginFactory* gPluginFactory; //------------------------------------------------------------------------ } // namespace Steinberg //------------------------------------------------------------------------ /** \defgroup classFactoryMacros Macros for defining the class factory \ingroup sdkBase \b Example - How to use the class factory macros: \code BEGIN_FACTORY ("Steinberg Technologies", "http://www.steinberg.de", "mailto:info@steinberg.de", PFactoryInfo::kNoFlags) DEF_CLASS (INLINE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000), PClassInfo::kManyInstances, "Service", "Test Service", TestService::newInstance) END_FACTORY \endcode @{*/ #define BEGIN_FACTORY_CLASS(FactoryClass,vendor,url,email,flags) using namespace Steinberg; \ SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory () { \ if (!gPluginFactory) \ { static PFactoryInfo factoryInfo (vendor,url,email,flags); \ gPluginFactory = new FactoryClass (factoryInfo); \ #define BEGIN_FACTORY(vendor,url,email,flags) using namespace Steinberg; \ SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory () { \ if (!gPluginFactory) \ { static PFactoryInfo factoryInfo (vendor,url,email,flags); \ gPluginFactory = new CPluginFactory (factoryInfo); \ #define DEF_CLASS(cid,cardinality,category,name,createMethod) \ { TUID lcid = cid; static PClassInfo componentClass (lcid,cardinality,category,name); \ gPluginFactory->registerClass (&componentClass,createMethod); } #define DEF_CLASS1(cid,cardinality,category,name,createMethod) \ { static PClassInfo componentClass (cid,cardinality,category,name); \ gPluginFactory->registerClass (&componentClass,createMethod); } #define DEF_CLASS2(cid,cardinality,category,name,classFlags,subCategories,version,sdkVersion,createMethod) \ { TUID lcid = cid; static PClassInfo2 componentClass (lcid,cardinality,category,name,classFlags,subCategories,nullptr,version,sdkVersion);\ gPluginFactory->registerClass (&componentClass,createMethod); } #define DEF_CLASS_W(cid,cardinality,category,name,classFlags,subCategories,version,sdkVersion,createMethod) \ { TUID lcid = cid; static PClassInfoW componentClass (lcid,cardinality,category,name,classFlags,subCategories,nullptr,version,sdkVersion);\ gPluginFactory->registerClass (&componentClass,createMethod); } #define DEF_CLASS_W2(cid,cardinality,category,name,classFlags,subCategories,vendor,version,sdkVersion,createMethod) \ { TUID lcid = cid; static PClassInfoW componentClass (lcid,cardinality,category,name,classFlags,subCategories,vendor,version,sdkVersion);\ gPluginFactory->registerClass (&componentClass,createMethod); } #define END_FACTORY } else gPluginFactory->addRef (); \ return gPluginFactory; } #define DEF_VST3_CLASS(pluginName, pluginVst3Categories, classFlags, pluginVersion, processorCID, \ processorCreateFunc, controllerCID, controllerCreateFunc) \ { \ { \ const Steinberg::TUID lcid = processorCID; \ static Steinberg::PClassInfo2 processorClass ( \ lcid, Steinberg::PClassInfo::kManyInstances, kVstAudioEffectClass, pluginName, \ classFlags, pluginVst3Categories, 0, pluginVersion, kVstVersionString); \ gPluginFactory->registerClass (&processorClass, processorCreateFunc); \ } \ { \ const Steinberg::TUID lcid = controllerCID; \ static Steinberg::PClassInfo2 controllerClass ( \ lcid, Steinberg::PClassInfo::kManyInstances, kVstComponentControllerClass, \ pluginName, 0, "", 0, pluginVersion, kVstVersionString); \ gPluginFactory->registerClass (&controllerClass, controllerCreateFunc); \ } \ } /** @} */ qtractor-1.5.11/src/vst3/public.sdk/source/main/PaxHeaders/moduleinit.h0000644000000000000000000000013215124701711023005 xustar0030 mtime=1767080905.277062199 30 atime=1767080905.277062199 30 ctime=1767080905.277062199 qtractor-1.5.11/src/vst3/public.sdk/source/main/moduleinit.h0000644000175000001440000000674515124701711023011 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : Common Base Classes // Filename : public.sdk/source/main/moduleinit.h // Created by : Steinberg, 11/2020 // Description : Module Initializers/Terminators // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ftypes.h" #include #include #include #include //------------------------------------------------------------------------ /** A replacement for InitModule and DeinitModule * * If you link this file the InitModule and DeinitModule functions are * implemented and you can use this to register functions that will be * called when the module is loaded and before the module is unloaded. * * Use this for one time initializers or cleanup functions. * For example: if you depend on a 3rd party library that needs * initialization before you can use it you can write an initializer like this: * * static ModuleInitializer InitMyExternalLib ([] () { MyExternalLib::init (); }); * * Or you have a lazy create wavetable you need to free the allocated memory later: * * static ModuleTerminator FreeWaveTableMemory ([] () { MyWaveTable::free (); }); */ //------------------------------------------------------------------------ #if SMTG_OS_WINDOWS using HINSTANCE = struct HINSTANCE__*; namespace Steinberg { using PlatformModuleHandle = HINSTANCE; } //------------------------------------------------------------------------ #elif SMTG_OS_OSX || SMTG_OS_IOS typedef struct __CFBundle* CFBundleRef; namespace Steinberg { using PlatformModuleHandle = CFBundleRef; } //------------------------------------------------------------------------ #elif SMTG_OS_LINUX namespace Steinberg { using PlatformModuleHandle = void*; } #endif //------------------------------------------------------------------------ namespace Steinberg { using ModuleInitFunction = std::function; using ModuleInitPriority = uint32; static constexpr ModuleInitPriority DefaultModulePriority = std::numeric_limits::max () / 2; //------------------------------------------------------------------------ struct ModuleInitializer { /** * Register a function which is called when the module is loaded * @param func function to call * @param prio priority */ ModuleInitializer (ModuleInitFunction&& func, ModuleInitPriority prio = DefaultModulePriority); }; //------------------------------------------------------------------------ struct ModuleTerminator { /** * Register a function which is called when the module is unloaded * @param func function to call * @param prio priority */ ModuleTerminator (ModuleInitFunction&& func, ModuleInitPriority prio = DefaultModulePriority); }; //------------------------------------------------------------------------ PlatformModuleHandle getPlatformModuleHandle (); //------------------------------------------------------------------------ } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/main/PaxHeaders/pluginfactory.cpp0000644000000000000000000000013215124701711024055 xustar0030 mtime=1767080905.277062199 30 atime=1767080905.277062199 30 ctime=1767080905.277062199 qtractor-1.5.11/src/vst3/public.sdk/source/main/pluginfactory.cpp0000644000175000001440000002165715124701711024060 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : Common Base Classes // Filename : public.sdk/source/main/pluginfactory.cpp // Created by : Steinberg, 01/2004 // Description : Standard Plug-In Factory // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginfactory.h" #if SMTG_OS_LINUX #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/gui/iplugview.h" #include "base/source/timer.h" #endif #include #include namespace Steinberg { DEF_CLASS_IID (IPluginFactoryInternal); CPluginFactory* gPluginFactory = nullptr; //------------------------------------------------------------------------ // CPluginFactory implementation //------------------------------------------------------------------------ CPluginFactory::CPluginFactory (const PFactoryInfo& info) : classes (nullptr), classCount (0), maxClassCount (0) { FUNKNOWN_CTOR factoryInfo = info; } //------------------------------------------------------------------------ CPluginFactory::~CPluginFactory () { if (gPluginFactory == this) gPluginFactory = nullptr; if (classes) free (classes); FUNKNOWN_DTOR } //------------------------------------------------------------------------ IMPLEMENT_REFCOUNT (CPluginFactory) //------------------------------------------------------------------------ tresult PLUGIN_API CPluginFactory::queryInterface (FIDString _iid, void** obj) { QUERY_INTERFACE (_iid, obj, IPluginFactory::iid, IPluginFactory) QUERY_INTERFACE (_iid, obj, IPluginFactory2::iid, IPluginFactory2) QUERY_INTERFACE (_iid, obj, IPluginFactory3::iid, IPluginFactory3) QUERY_INTERFACE (_iid, obj, IPluginFactoryInternal::iid, IPluginFactoryInternal) QUERY_INTERFACE (_iid, obj, FUnknown::iid, IPluginFactory) *obj = nullptr; return kNoInterface; } //------------------------------------------------------------------------ bool CPluginFactory::registerClass (const PClassInfo* info, FUnknown* (*createFunc) (void*), void* context) { if (!info || !createFunc) return false; PClassInfo2 info2; memcpy (&info2, info, sizeof (PClassInfo)); return registerClass (&info2, createFunc, context); } //------------------------------------------------------------------------ bool CPluginFactory::registerClass (const PClassInfo2* info, FUnknown* (*createFunc) (void*), void* context) { if (!info || !createFunc) return false; if (classCount >= maxClassCount) { if (!growClasses ()) return false; } PClassEntry& entry = classes[classCount]; entry.info8 = *info; entry.info16.fromAscii (*info); entry.createFunc = createFunc; entry.context = context; entry.isUnicode = false; classCount++; return true; } //------------------------------------------------------------------------ bool CPluginFactory::registerClass (const PClassInfoW* info, FUnknown* (*createFunc) (void*), void* context) { if (!info || !createFunc) return false; if (classCount >= maxClassCount) { if (!growClasses ()) return false; } PClassEntry& entry = classes[classCount]; entry.info16 = *info; entry.createFunc = createFunc; entry.context = context; entry.isUnicode = true; classCount++; return true; } //------------------------------------------------------------------------ bool CPluginFactory::growClasses () { static const int32 delta = 10; size_t size = (maxClassCount + delta) * sizeof (PClassEntry); void* memory = classes; if (!memory) memory = malloc (size); else memory = realloc (memory, size); if (!memory) return false; classes = static_cast (memory); maxClassCount += delta; return true; } //------------------------------------------------------------------------ bool CPluginFactory::isClassRegistered (const FUID& cid) { for (int32 i = 0; i < classCount; i++) { if (FUnknownPrivate::iidEqual (cid, classes[i].info16.cid)) return true; } return false; } //------------------------------------------------------------------------ void CPluginFactory::removeAllClasses () { classCount = 0; } //------------------------------------------------------------------------ tresult PLUGIN_API CPluginFactory::getFactoryInfo (PFactoryInfo* info) { if (info) memcpy (info, &factoryInfo, sizeof (PFactoryInfo)); return kResultOk; } //------------------------------------------------------------------------ int32 PLUGIN_API CPluginFactory::countClasses () { return classCount; } //------------------------------------------------------------------------ tresult PLUGIN_API CPluginFactory::getClassInfo (int32 index, PClassInfo* info) { if (info && (index >= 0 && index < classCount)) { if (classes[index].isUnicode) { memset (info, 0, sizeof (PClassInfo)); return kResultFalse; } memcpy (info, &classes[index].info8, sizeof (PClassInfo)); return kResultOk; } return kInvalidArgument; } //------------------------------------------------------------------------ tresult PLUGIN_API CPluginFactory::getClassInfo2 (int32 index, PClassInfo2* info) { if (info && (index >= 0 && index < classCount)) { if (classes[index].isUnicode) { memset (info, 0, sizeof (PClassInfo2)); return kResultFalse; } memcpy (info, &classes[index].info8, sizeof (PClassInfo2)); return kResultOk; } return kInvalidArgument; } //------------------------------------------------------------------------ tresult PLUGIN_API CPluginFactory::getClassInfoUnicode (int32 index, PClassInfoW* info) { if (info && (index >= 0 && index < classCount)) { memcpy (info, &classes[index].info16, sizeof (PClassInfoW)); return kResultOk; } return kInvalidArgument; } //------------------------------------------------------------------------ tresult PLUGIN_API CPluginFactory::createInstance (FIDString cid, FIDString _iid, void** obj) { for (int32 i = 0; i < classCount; i++) { if (memcmp (classes[i].info16.cid, cid, sizeof (TUID)) == 0) { FUnknown* instance = classes[i].createFunc (classes[i].context); if (instance) { if (instance->queryInterface (_iid, obj) == kResultOk) { instance->release (); return kResultOk; } instance->release (); } break; } } *obj = nullptr; return kNoInterface; } #if SMTG_OS_LINUX //------------------------------------------------------------------------ namespace /*anonymous*/ { //------------------------------------------------------------------------ class LinuxPlatformTimer : public U::Extends> { public: ~LinuxPlatformTimer () noexcept override { stop (); } tresult init (ITimerCallback* cb, uint32 timeout) { if (!runLoop || cb == nullptr || timeout == 0) return kResultFalse; auto result = runLoop->registerTimer (this, timeout); if (result == kResultTrue) { callback = cb; timerRegistered = true; } return result; } void PLUGIN_API onTimer () override { callback->onTimer (this); } void stop () override { if (timerRegistered) { if (runLoop) runLoop->unregisterTimer (this); timerRegistered = false; } } bool timerRegistered {false}; ITimerCallback* callback; static IPtr runLoop; }; IPtr LinuxPlatformTimer::runLoop; //------------------------------------------------------------------------ Timer* createLinuxTimer (ITimerCallback* cb, uint32 milliseconds) { if (!LinuxPlatformTimer::runLoop) return nullptr; auto timer = NEW LinuxPlatformTimer; if (timer->init (cb, milliseconds) == kResultTrue) return timer; timer->release (); return nullptr; } } // anonymous #endif // SMTG_OS_LINUX //------------------------------------------------------------------------ tresult PLUGIN_API CPluginFactory::setHostContext (FUnknown* context) { std::for_each (hostContextCallbacks.begin (), hostContextCallbacks.end (), [context] (const auto& cb) { cb (context); }); #if SMTG_OS_LINUX if (auto runLoop = U::cast (context)) { LinuxPlatformTimer::runLoop = runLoop; InjectCreateTimerFunction (createLinuxTimer); } else { LinuxPlatformTimer::runLoop.reset (); InjectCreateTimerFunction (nullptr); } return kResultTrue; #else (void) context; return kNotImplemented; #endif } //------------------------------------------------------------------------ void PLUGIN_API CPluginFactory::addHostContextCallback (HostContextCallbackFunc func) { hostContextCallbacks.push_back (func); } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/main/PaxHeaders/moduleinit.cpp0000644000000000000000000000013215124701711023340 xustar0030 mtime=1767080905.277062199 30 atime=1767080905.277062199 30 ctime=1767080905.277062199 qtractor-1.5.11/src/vst3/public.sdk/source/main/moduleinit.cpp0000644000175000001440000000670515124701711023340 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : Common Base Classes // Filename : public.sdk/source/main/moduleinit.cpp // Created by : Steinberg, 11/2020 // Description : Module Initializers/Terminators // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "moduleinit.h" #include #include extern void* moduleHandle; // from dllmain.cpp, linuxmain.cpp or macmain.cpp //------------------------------------------------------------------------ namespace Steinberg { namespace { //------------------------------------------------------------------------ using FunctionVector = std::vector>; //------------------------------------------------------------------------ FunctionVector& getInitFunctions () { static FunctionVector gInitVector; return gInitVector; } //------------------------------------------------------------------------ FunctionVector& getTermFunctions () { static FunctionVector gTermVector; return gTermVector; } //------------------------------------------------------------------------ void addInitFunction (ModuleInitFunction&& func, ModuleInitPriority prio) { getInitFunctions ().emplace_back (prio, std::move (func)); } //------------------------------------------------------------------------ void addTerminateFunction (ModuleInitFunction&& func, ModuleInitPriority prio) { getTermFunctions ().emplace_back (prio, std::move (func)); } //------------------------------------------------------------------------ void sortAndRunFunctions (FunctionVector& array) { std::sort (array.begin (), array.end (), [] (const FunctionVector::value_type& v1, const FunctionVector::value_type& v2) { return v1.first < v2.first; }); for (auto& entry : array) entry.second (); } //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ ModuleInitializer::ModuleInitializer (ModuleInitFunction&& func, ModuleInitPriority prio) { addInitFunction (std::move (func), prio); } //------------------------------------------------------------------------ ModuleTerminator::ModuleTerminator (ModuleInitFunction&& func, ModuleInitPriority prio) { addTerminateFunction (std::move (func), prio); } //------------------------------------------------------------------------ PlatformModuleHandle getPlatformModuleHandle () { return reinterpret_cast (moduleHandle); } //------------------------------------------------------------------------ } // Steinberg //------------------------------------------------------------------------ bool InitModule () { Steinberg::sortAndRunFunctions (Steinberg::getInitFunctions ()); return true; } //------------------------------------------------------------------------ bool DeinitModule () { Steinberg::sortAndRunFunctions (Steinberg::getTermFunctions ()); return true; } qtractor-1.5.11/src/vst3/public.sdk/source/main/PaxHeaders/dllmain.cpp0000644000000000000000000000013215124701711022607 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/main/dllmain.cpp0000644000175000001440000000617515124701711022610 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // Version : 1.0 // // Category : Common Base Classes // Filename : public.sdk/source/main/dllmain.cpp // Created by : Steinberg, 01/2004 // Description : Windows DLL Entry // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginterfaces/base/ftypes.h" #include "pluginterfaces/base/fstrdefs.h" #include #if defined(_MSC_VER) && defined(DEVELOPMENT) #include #endif //------------------------------------------------------------------------ HINSTANCE ghInst = nullptr; void* moduleHandle = nullptr; #define VST_MAX_PATH 2048 Steinberg::tchar gPath[VST_MAX_PATH] = {0}; //------------------------------------------------------------------------ extern bool InitModule (); ///< must be provided by plug-in: called when the library is loaded extern bool DeinitModule (); ///< must be provided by plug-in: called when the library is unloaded //------------------------------------------------------------------------ #ifdef __cplusplus extern "C" { #endif static int moduleCounter {0}; // counting for InitDll/ExitDll pairs //------------------------------------------------------------------------ /** must be called from host right after loading dll, must be provided by the plug-in! Note: this could be called more than one time! */ SMTG_EXPORT_SYMBOL bool InitDll () { if (++moduleCounter == 1) return InitModule (); return true; } //------------------------------------------------------------------------ /** must be called from host right before unloading dll must be provided by the plug-in! Note: this could be called more than one time! */ SMTG_EXPORT_SYMBOL bool ExitDll () { if (--moduleCounter == 0) return DeinitModule (); if (moduleCounter < 0) return false; return true; } #ifdef __cplusplus } // extern "C" #endif //------------------------------------------------------------------------ BOOL WINAPI DllMain (HINSTANCE hInst, DWORD dwReason, LPVOID /*lpvReserved*/) { if (dwReason == DLL_PROCESS_ATTACH) { #if defined(_MSC_VER) && defined(DEVELOPMENT) _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG); _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG); _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG); int flag = _CrtSetDbgFlag (_CRTDBG_REPORT_FLAG); _CrtSetDbgFlag (flag | _CRTDBG_LEAK_CHECK_DF); #endif moduleHandle = ghInst = hInst; // gets the path of the component if (GetModuleFileName (ghInst, Steinberg::wscast (gPath), MAX_PATH) > 0) { Steinberg::tchar* bkslash = Steinberg::wscast (wcsrchr (Steinberg::wscast (gPath), L'\\')); if (bkslash) gPath[bkslash - gPath + 1] = 0; } } return TRUE; } qtractor-1.5.11/src/vst3/public.sdk/source/main/PaxHeaders/macmain.cpp0000644000000000000000000000013215124701711022574 xustar0030 mtime=1767080905.277062199 30 atime=1767080905.277062199 30 ctime=1767080905.277062199 qtractor-1.5.11/src/vst3/public.sdk/source/main/macmain.cpp0000644000175000001440000000605515124701711022572 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // Version : 1.0 // // Category : Common Base Classes // Filename : public.sdk/source/main/macmain.cpp // Created by : Steinberg, 01/2004 // Description : Mac OS X Bundle Entry // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginterfaces/base/fplatform.h" #ifndef __CF_USE_FRAMEWORK_INCLUDES__ #define __CF_USE_FRAMEWORK_INCLUDES__ 1 #endif #include //------------------------------------------------------------------------ CFBundleRef ghInst = nullptr; int bundleRefCounter = 0; // counting for bundleEntry/bundleExit pairs void* moduleHandle = nullptr; #define VST_MAX_PATH 2048 char gPath[VST_MAX_PATH] = {0}; //------------------------------------------------------------------------ bool InitModule (); ///< must be provided by plug-in: called when the library is loaded bool DeinitModule (); ///< must be provided by plug-in: called when the library is unloaded //------------------------------------------------------------------------ extern "C" { /** bundleEntry and bundleExit must be provided by the plug-in! */ SMTG_EXPORT_SYMBOL bool bundleEntry (CFBundleRef); SMTG_EXPORT_SYMBOL bool bundleExit (void); } #include std::vector gBundleRefs; //------------------------------------------------------------------------ /** must be called from host right after loading bundle Note: this could be called more than one time! */ bool bundleEntry (CFBundleRef ref) { if (ref) { bundleRefCounter++; CFRetain (ref); // hold all bundle refs until plug-in is fully uninitialized gBundleRefs.push_back (ref); if (!moduleHandle) { ghInst = ref; moduleHandle = ref; // obtain the bundle path CFURLRef tempURL = CFBundleCopyBundleURL (ref); CFURLGetFileSystemRepresentation (tempURL, true, reinterpret_cast (gPath), VST_MAX_PATH); CFRelease (tempURL); } if (bundleRefCounter == 1) return InitModule (); } return true; } //------------------------------------------------------------------------ /** must be called from host right before unloading bundle Note: this could be called more than one time! */ bool bundleExit (void) { if (--bundleRefCounter == 0) { DeinitModule (); // release the CFBundleRef's once all bundleExit clients called in // there is no way to identify the proper CFBundleRef of the bundleExit call for (size_t i = 0; i < gBundleRefs.size (); i++) CFRelease (gBundleRefs[i]); gBundleRefs.clear (); } else if (bundleRefCounter < 0) return false; return true; } qtractor-1.5.11/src/vst3/public.sdk/source/main/PaxHeaders/linuxmain.cpp0000644000000000000000000000013215124701711023173 xustar0030 mtime=1767080905.277062199 30 atime=1767080905.276536456 30 ctime=1767080905.277062199 qtractor-1.5.11/src/vst3/public.sdk/source/main/linuxmain.cpp0000644000175000001440000000412515124701711023165 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // Version : 1.0 // // Category : Common Base Classes // Filename : public.sdk/source/main/linuxmain.cpp // Created by : Steinberg, 03/2017 // Description : Linux Component Entry // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginterfaces/base/fplatform.h" void* moduleHandle = nullptr; //------------------------------------------------------------------------ bool InitModule (); ///< must be provided by plug-in: called when the library is loaded bool DeinitModule (); ///< must be provided by plug-in: called when the library is unloaded //------------------------------------------------------------------------ extern "C" { /** must be provided by the plug-in! */ SMTG_EXPORT_SYMBOL bool ModuleEntry (void*); SMTG_EXPORT_SYMBOL bool ModuleExit (void); } static int moduleCounter {0}; // counting for ModuleEntry/ModuleExit pairs //------------------------------------------------------------------------ /** must be called from host right after loading dll Note: this could be called more than one time! */ bool ModuleEntry (void* sharedLibraryHandle) { if (++moduleCounter == 1) { moduleHandle = sharedLibraryHandle; return InitModule (); } return true; } //------------------------------------------------------------------------ /** must be called from host right before unloading dll Note: this could be called more than one time! */ bool ModuleExit (void) { if (--moduleCounter == 0) { moduleHandle = nullptr; return DeinitModule (); } else if (moduleCounter < 0) return false; return true; } qtractor-1.5.11/src/vst3/public.sdk/source/main/PaxHeaders/macexport.exp0000644000000000000000000000013215124701711023203 xustar0030 mtime=1767080905.277062199 30 atime=1767080905.277062199 30 ctime=1767080905.277062199 qtractor-1.5.11/src/vst3/public.sdk/source/main/macexport.exp0000644000175000001440000000005315124701711023171 0ustar00rncbcusers_GetPluginFactory _bundleEntry _bundleExit qtractor-1.5.11/src/vst3/public.sdk/source/main/PaxHeaders/pluginfactory_constexpr.h0000644000000000000000000000013215124701711025627 xustar0030 mtime=1767080905.277062199 30 atime=1767080905.277062199 30 ctime=1767080905.277062199 qtractor-1.5.11/src/vst3/public.sdk/source/main/pluginfactory_constexpr.h0000644000175000001440000001645215124701711025627 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : SDK Core // // Category : Common Base Classes // Filename : public.sdk/source/main/pluginfactory_constexpr.h // Created by : Steinberg, 10/2021 // Description : Standard Plug-In Factory (constexpr variant) // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/base/ipluginbase.h" #if !SMTG_CPP17 #error "C++17 is required for this header" #endif #include namespace Steinberg { //------------------------------------------------------------------------ /** IPluginFactory implementation with compile time provided factory and class infos. \ingroup sdkBase \see \ref constexprClassFactoryMacros You can use this factory when your number of classes are known during compile time. The advantage here is that during runtime no unnecessary setup is needed. Please note that this only supports ASCII names and thus if you need to support Unicode names you must use another implementation. This only works when compiling with c++17 or newer. */ template class PluginFactory : public U::ImplementsNonDestroyable, U::Indirectly> { public: //------------------------------------------------------------------------ tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) override { if (info == nullptr) return kInvalidArgument; *info = T::factoryInfo; return kResultTrue; } int32 PLUGIN_API countClasses () override { return static_cast (T::classInfos.size ()); } tresult PLUGIN_API getClassInfo (int32 index, PClassInfo* info) override { if (index < 0 || index >= static_cast (T::classInfos.size ()) || info == nullptr) return kInvalidArgument; *info = {}; const auto& ci = T::classInfos[index]; copyTUID (info->cid, ci.cid); info->cardinality = ci.cardinality; if (ci.category) strncpy (info->category, ci.category, PClassInfo::kCategorySize); if (ci.name) strncpy (info->name, ci.name, PClassInfo::kNameSize); return kResultTrue; } tresult PLUGIN_API createInstance (FIDString cid, FIDString iid, void** obj) override { for (const auto& e : T::classInfos) { if (FUnknownPrivate::iidEqual (e.cid, cid)) { if (auto instance = e.create (e.context)) { if (instance->queryInterface (iid, obj) == kResultOk) { instance->release (); return kResultOk; } else instance->release (); } } } *obj = nullptr; return kNoInterface; } tresult PLUGIN_API getClassInfo2 (int32 index, PClassInfo2* info) override { if (index < 0 || index >= static_cast (T::classInfos.size ()) || info == nullptr) return kInvalidArgument; *info = T::classInfos[index]; return kResultTrue; } }; //------------------------------------------------------------------------ namespace PluginFactoryDetail { //------------------------------------------------------------------------ struct ClassInfo2WithCreateFunc : PClassInfo2 { using CreateInstanceFunc = FUnknown* (*)(void*); CreateInstanceFunc create {nullptr}; void* context {nullptr}; }; //------------------------------------------------------------------------ inline constexpr ClassInfo2WithCreateFunc makeClassInfo2 ( const TUID cid, int32 cardinality, const char8* category, const char8* name, int32 classFlags, const char8* subCategories, const char8* vendor, const char8* version, const char8* sdkVersion, ClassInfo2WithCreateFunc::CreateInstanceFunc func, void* context = nullptr) { ClassInfo2WithCreateFunc classInfo {}; copyTUID (classInfo.cid, cid); classInfo.cardinality = cardinality; strncpy8 (classInfo.category, category, PClassInfo::kCategorySize); strncpy8 (classInfo.name, name, PClassInfo::kNameSize); classInfo.classFlags = classFlags; if (subCategories) strncpy8 (classInfo.subCategories, subCategories, PClassInfo2::kSubCategoriesSize); if (vendor) strncpy8 (classInfo.vendor, vendor, PClassInfo2::kVendorSize); if (version) strncpy8 (classInfo.version, version, PClassInfo2::kVersionSize); if (sdkVersion) strncpy8 (classInfo.sdkVersion, sdkVersion, PClassInfo2::kVersionSize); classInfo.create = func; classInfo.context = context; return classInfo; } //------------------------------------------------------------------------ } // namespace PluginFactoryDetail } // namespace Steinberg #ifdef BEGIN_FACTORY_DEF #undef BEGIN_FACTORY_DEF #endif /** \defgroup constexprClassFactoryMacros Macros for defining the compile time class factory \ingroup sdkBase \b Example: \code static constexpr size_t numberOfClasses = 1; static DECLARE_UID (TestPluginUID, 0x00000001, 0x00000002, 0x00000003, 0x00000004); BEGIN_FACTORY_DEF ("MyCompany", "mycompany.com", "info@mycompany.com", numberOfClasses) DEF_CLASS (TestPluginUID, PClassInfo::kManyInstances, "PlugIn", "Test PlugIn", 0, "SubCategory", "1.0.0", stringSDKVersion, TestPlugin::newInstance, nullptr) END_FACTORY \endcode @{*/ // clang-format off #define BEGIN_FACTORY_DEF(company, url, email, noClasses) \ struct SMTG_HIDDEN_SYMBOL FactoryData \ { \ static constexpr Steinberg::PFactoryInfo factoryInfo = { \ company, url, email, Steinberg::PFactoryInfo::kUnicode}; \ \ static constexpr std::array \ classInfos = { #define DEF_CLASS(cid, cardinality, category, name, classFlags, subCategories, version, \ sdkVersion, createMethod, createContext) \ Steinberg::PluginFactoryDetail::makeClassInfo2 (cid, cardinality, category, name, classFlags, \ subCategories, nullptr, version, sdkVersion, \ createMethod, createContext), #define END_FACTORY \ };}; \ SMTG_EXPORT_SYMBOL Steinberg::IPluginFactory* PLUGIN_API GetPluginFactory () \ { \ static Steinberg::PluginFactory factory; \ return &factory; \ } // clang-format on /** @} */ qtractor-1.5.11/src/vst3/public.sdk/source/PaxHeaders/common0000644000000000000000000000013015124701711020750 xustar0030 mtime=1767080905.276536456 28 atime=1767080905.2763013 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/0000755000175000001440000000000015124701711021017 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/commonstringconvert.h0000644000000000000000000000013215124701711025320 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/commonstringconvert.h0000644000175000001440000000420415124701711025310 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : stringconvert // Filename : public.sdk/source/common/commonstringconvert.h // Created by : Steinberg, 07/2024 // Description : read file routine // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include #include namespace Steinberg { namespace StringConvert { //------------------------------------------------------------------------ /** * convert an UTF-8 string to an UTF-16 string * * @param utf8Str UTF-8 string * * @return UTF-16 string */ std::u16string convert (const std::string& utf8Str); //------------------------------------------------------------------------ /** * convert an UTF-16 string to an UTF-8 string * * @param str UTF-16 string * * @return UTF-8 string */ std::string convert (const std::u16string& str); //------------------------------------------------------------------------ /** * convert a ASCII string buffer to an UTF-8 string * * @param str ASCII string buffer * @param max maximum characters in str * * @return UTF-8 string */ std::string convert (const char* str, uint32_t max); //------------------------------------------------------------------------ /** * convert a number to an UTF-16 string * * @param value number * * @return UTF-16 string */ template std::u16string toString (NumberT value) { auto u8str = std::to_string (value); return StringConvert::convert (u8str); } //------------------------------------------------------------------------ } // namespace StringConvert } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/threadchecker_win32.cpp0000644000000000000000000000013215124701711025351 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/threadchecker_win32.cpp0000644000175000001440000000332215124701711025341 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Examples // Filename : public.sdk/source/common/threadchecker_win32.cpp // Created by : Steinberg, 01/2019 // Description : win32 thread checker // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "threadchecker.h" #if SMTG_OS_WINDOWS #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ class Win32ThreadChecker : public ThreadChecker { public: bool test (const char* failmessage = nullptr, bool exit = false) override { if (threadID == GetCurrentThreadId ()) return true; if (failmessage) OutputDebugStringA (failmessage); if (exit) std::terminate (); return false; } DWORD threadID {GetCurrentThreadId ()}; }; //------------------------------------------------------------------------ std::unique_ptr ThreadChecker::create () { return std::unique_ptr (new Win32ThreadChecker); } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg #endif // SMTG_OS_WINDOWS qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/threadchecker.h0000644000000000000000000000013215124701711023774 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/threadchecker.h0000644000175000001440000000253415124701711023770 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Examples // Filename : public.sdk/source/common/threadchecker.h // Created by : Steinberg, 01/2019 // Description : thread checker // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ftypes.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ class ThreadChecker { public: static std::unique_ptr create (); virtual bool test (const char* failmessage = nullptr, bool exit = false) = 0; virtual ~ThreadChecker () noexcept = default; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/openurl.cpp0000644000000000000000000000013215124701711023217 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/openurl.cpp0000644000175000001440000000344315124701711023213 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // // Category : Helpers // Project : Steinberg Plug-In SDK // Filename : public.sdk/source/common/openurl.cpp // Created by : Steinberg 04.2020 // Description : Simple helper allowing to open a URL in the default associated application // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "openurl.h" #include "pluginterfaces/base/ftypes.h" #if SMTG_OS_WINDOWS // keep this order #include #include #else #include #endif //----------------------------------------------------------------------------- namespace Steinberg { //----------------------------------------------------------------------------- bool openURLInDefaultApplication (const String& address) { bool res = false; #if SMTG_OS_WINDOWS auto r = ShellExecuteA (nullptr, "open", address.text8 (), nullptr, nullptr, SW_SHOWNORMAL); res = (r != nullptr); #elif SMTG_OS_OSX String cmd; cmd += "open \""; cmd += address.text8 (); cmd += "\""; res = (system (cmd) == 0); #elif SMTG_OS_LINUX String cmd; cmd += "xdg-open \""; cmd += address.text8 (); cmd += "\""; res = (system (cmd) == 0); #endif return res; } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/commoniids.cpp0000644000000000000000000000013215124701711023674 xustar0030 mtime=1767080905.276484509 30 atime=1767080905.276484509 30 ctime=1767080905.276484509 qtractor-1.5.11/src/vst3/public.sdk/source/common/commoniids.cpp0000644000175000001440000000252415124701711023667 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // Version : 1.0 // // Category : Common Base Classes // Filename : public.sdk/source/common/commoniids.cpp // Created by : Steinberg, 01/2019 // Description : Define some IIDs // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginterfaces/gui/iplugview.h" #include "pluginterfaces/gui/iplugviewcontentscalesupport.h" namespace Steinberg { //----VST 3.0-------------------------------- DEF_CLASS_IID (IPlugView) DEF_CLASS_IID (IPlugFrame) //----VST 3.6.0-------------------------------- DEF_CLASS_IID (IPlugViewContentScaleSupport) #if SMTG_OS_LINUX DEF_CLASS_IID (Linux::IEventHandler) DEF_CLASS_IID (Linux::ITimerHandler) DEF_CLASS_IID (Linux::IRunLoop) #endif //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/threadchecker_mac.mm0000644000000000000000000000013215124701711024776 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/threadchecker_mac.mm0000644000175000001440000000327015124701711024770 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Examples // Filename : public.sdk/source/common/threadchecker_mac.mm // Created by : Steinberg, 01/2019 // Description : macOS thread checker // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "threadchecker.h" #if SMTG_OS_MACOS #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ class MacThreadChecker : public ThreadChecker { public: bool test (const char* failmessage = nullptr, bool exit = false) override { if (threadID == pthread_self ()) return true; if (failmessage) NSLog (@"%s", failmessage); if (exit) std::terminate (); return false; } pthread_t threadID {pthread_self ()}; }; //------------------------------------------------------------------------ std::unique_ptr ThreadChecker::create () { return std::unique_ptr (new MacThreadChecker); } //------------------------------------------------------------------------ } // Vst } // Steinberg #endif qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/commonstringconvert.cpp0000644000000000000000000000013215124701711025653 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276484509 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/commonstringconvert.cpp0000644000175000001440000000562015124701711025646 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/common/commonstringconvert.cpp // Created by : Steinberg, 07/2024 // Description : c++11 unicode string convert functions // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "commonstringconvert.h" #include #include #include #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #elif defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4996) #endif //------------------------------------------------------------------------ namespace Steinberg { namespace StringConvert { //------------------------------------------------------------------------ namespace { #if defined(_MSC_VER) && _MSC_VER >= 1900 #define USE_WCHAR_AS_UTF16TYPE using UTF16Type = wchar_t; #else using UTF16Type = char16_t; #endif using Converter = std::wstring_convert, UTF16Type>; //------------------------------------------------------------------------ Converter& converter () { static Converter conv; return conv; } //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ std::u16string convert (const std::string& utf8Str) { #if defined(USE_WCHAR_AS_UTF16TYPE) auto wstr = converter ().from_bytes (utf8Str); return {wstr.data (), wstr.data () + wstr.size ()}; #else return converter ().from_bytes (utf8Str); #endif } //------------------------------------------------------------------------ std::string convert (const std::u16string& str) { return converter ().to_bytes (reinterpret_cast (str.data ()), reinterpret_cast (str.data () + str.size ())); } //------------------------------------------------------------------------ std::string convert (const char* str, uint32_t max) { std::string result; if (str) { result.reserve (max); for (uint32_t i = 0; i < max; ++i, ++str) { if (*str == 0) break; result += *str; } } return result; } //------------------------------------------------------------------------ } // StringConvert } // Steinberg #ifdef __clang__ #pragma clang diagnostic pop #elif defined(_MSC_VER) #pragma warning(pop) #endif qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/pluginview.cpp0000644000000000000000000000013215124701711023724 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/pluginview.cpp0000644000175000001440000000445515124701711023724 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : Common Base Classes // Filename : public.sdk/source/common/pluginview.cpp // Created by : Steinberg, 01/2004 // Description : Plug-In View Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginview.h" namespace Steinberg { //------------------------------------------------------------------------ // CPluginView implementation //------------------------------------------------------------------------ CPluginView::CPluginView (const ViewRect* _rect) : rect (0, 0, 0, 0) { if (_rect) rect = *_rect; } //------------------------------------------------------------------------ CPluginView::~CPluginView () { } //------------------------------------------------------------------------ tresult PLUGIN_API CPluginView::isPlatformTypeSupported (FIDString /*type*/) { return kNotImplemented; } //------------------------------------------------------------------------ tresult PLUGIN_API CPluginView::attached (void* parent, FIDString /*type*/) { systemWindow = parent; attachedToParent (); return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API CPluginView::removed () { systemWindow = nullptr; removedFromParent (); return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API CPluginView::onSize (ViewRect* newSize) { if (newSize) rect = *newSize; return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API CPluginView::getSize (ViewRect* size) { if (size) { *size = rect; return kResultTrue; } return kInvalidArgument; } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/threadchecker_linux.cpp0000644000000000000000000000013215124701711025546 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/threadchecker_linux.cpp0000644000175000001440000000333515124701711025542 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Examples // Filename : public.sdk/source/common/threadchecker_linux.cpp // Created by : Steinberg, 01/2019 // Description : linux thread checker // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "threadchecker.h" #if SMTG_OS_LINUX #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ class LinuxThreadChecker : public ThreadChecker { public: bool test (const char* failmessage = nullptr, bool exit = false) override { if (threadID == pthread_self ()) return true; if (failmessage) fprintf (stderr, "%s", failmessage); if (exit) std::terminate (); return false; } pthread_t threadID {pthread_self ()}; }; //------------------------------------------------------------------------ std::unique_ptr ThreadChecker::create () { return std::unique_ptr (new LinuxThreadChecker); } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg #endif // SMTG_OS_LINUX qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/systemclipboard.h0000644000000000000000000000013215124701711024404 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/systemclipboard.h0000644000175000001440000000322215124701711024373 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // // Project : Steinberg Plug-In SDK // Filename : public.sdk/source/common/systemclipboard.h // Created by : Steinberg 04.2020 // Description : Simple helper allowing to copy/retrieve text to/from the system clipboard // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include //------------------------------------------------------------------------ namespace Steinberg { namespace SystemClipboard { //----------------------------------------------------------------------------- /** Copies the given text into the system clipboard \ingroup sdkBase \param text UTF-8 encoded text \return true on success */ bool copyTextToClipboard (const std::string& text); //----------------------------------------------------------------------------- /** Retrieves the current text from the system clipboard \ingroup sdkBase \param text UTF-8 encoded text \return true on success */ bool getTextFromClipboard (std::string& text); //----------------------------------------------------------------------------- } // namespace SystemClipboard } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/openurl.h0000644000000000000000000000013215124701711022664 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/openurl.h0000644000175000001440000000255715124701711022665 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // // Category : Helpers // Project : Steinberg Plug-In SDK // Filename : public.sdk/source/common/openurl.h // Created by : Steinberg 04.2020 // Description : Simple helper allowing to open a URL in the default associated application // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "base/source/fstring.h" namespace Steinberg { /** Open the given URL into the default web browser. \ingroup sdkBase It returns true if a default application is found and opened else false. Example: \code{.cpp} if (openURLInDefaultApplication ("https://www.steinberg.net/")) { // everything seems to be ok } \endcode */ bool openURLInDefaultApplication (const String& address); //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/memorystream.cpp0000644000000000000000000000013215124701711024257 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/memorystream.cpp0000644000175000001440000001416715124701711024260 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : Common Classes // Filename : public.sdk/source/common/memorystream.cpp // Created by : Steinberg, 03/2008 // Description : IBStream Implementation for memory blocks // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "memorystream.h" #include "pluginterfaces/base/futils.h" #include namespace Steinberg { //----------------------------------------------------------------------------- IMPLEMENT_FUNKNOWN_METHODS (MemoryStream, IBStream, IBStream::iid) static const TSize kMemGrowAmount = 4096; //----------------------------------------------------------------------------- MemoryStream::MemoryStream (void* data, TSize length) : memory ((char*)data) , memorySize (length) , size (length) , cursor (0) , ownMemory (false) , allocationError (false) { FUNKNOWN_CTOR } //----------------------------------------------------------------------------- MemoryStream::MemoryStream () : memory (nullptr) , memorySize (0) , size (0) , cursor (0) , ownMemory (true) , allocationError (false) { FUNKNOWN_CTOR } //----------------------------------------------------------------------------- MemoryStream::~MemoryStream () { if (ownMemory && memory) ::free (memory); FUNKNOWN_DTOR } //----------------------------------------------------------------------------- tresult PLUGIN_API MemoryStream::read (void* data, int32 numBytes, int32* numBytesRead) { if (memory == nullptr) { if (allocationError) return kOutOfMemory; numBytes = 0; } else { // Does read exceed size ? if (cursor + numBytes > size) { int32 maxBytes = int32 (size - cursor); // Has length become zero or negative ? if (maxBytes <= 0) { cursor = size; numBytes = 0; } else numBytes = maxBytes; } if (numBytes) { memcpy (data, &memory[cursor], static_cast (numBytes)); cursor += numBytes; } } if (numBytesRead) *numBytesRead = numBytes; return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API MemoryStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) { if (allocationError) return kOutOfMemory; if (buffer == nullptr) return kInvalidArgument; // Does write exceed size ? TSize requiredSize = cursor + numBytes; if (requiredSize > size) { if (requiredSize > memorySize) setSize (requiredSize); else size = requiredSize; } // Copy data if (memory && cursor >= 0 && numBytes > 0) { memcpy (&memory[cursor], buffer, static_cast (numBytes)); // Update cursor cursor += numBytes; } else numBytes = 0; if (numBytesWritten) *numBytesWritten = numBytes; return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API MemoryStream::seek (int64 pos, int32 mode, int64* result) { switch (mode) { case kIBSeekSet: cursor = pos; break; case kIBSeekCur: cursor = cursor + pos; break; case kIBSeekEnd: cursor = size + pos; break; } if (ownMemory == false) if (cursor > memorySize) cursor = memorySize; if (result) *result = cursor; return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API MemoryStream::tell (int64* pos) { if (!pos) return kInvalidArgument; *pos = cursor; return kResultTrue; } //------------------------------------------------------------------------ TSize MemoryStream::getSize () const { return size; } //------------------------------------------------------------------------ void MemoryStream::setSize (TSize s) { if (s <= 0) { if (ownMemory && memory) free (memory); memory = nullptr; memorySize = 0; size = 0; cursor = 0; return; } TSize newMemorySize = (((Max (memorySize, s) - 1) / kMemGrowAmount) + 1) * kMemGrowAmount; if (newMemorySize == memorySize) { size = s; return; } if (memory && ownMemory == false) { allocationError = true; return; } ownMemory = true; char* newMemory = nullptr; if (memory) { newMemory = (char*)realloc (memory, (size_t)newMemorySize); if (newMemory == nullptr && newMemorySize > 0) { newMemory = (char*)malloc ((size_t)newMemorySize); if (newMemory) { memcpy (newMemory, memory, (size_t)Min (newMemorySize, memorySize)); free (memory); } } } else newMemory = (char*)malloc ((size_t)newMemorySize); if (newMemory == nullptr) { if (newMemorySize > 0) allocationError = true; memory = nullptr; memorySize = 0; size = 0; cursor = 0; } else { memory = newMemory; memorySize = newMemorySize; size = s; } } //------------------------------------------------------------------------ char* MemoryStream::getData () const { return memory; } //------------------------------------------------------------------------ char* MemoryStream::detachData () { if (ownMemory) { char* result = memory; memory = nullptr; memorySize = 0; size = 0; cursor = 0; return result; } return nullptr; } //------------------------------------------------------------------------ bool MemoryStream::truncate () { if (ownMemory == false) return false; if (memorySize == size) return true; memorySize = size; if (memorySize == 0) { if (memory) { free (memory); memory = nullptr; } } else { if (memory) { char* newMemory = (char*)realloc (memory, (size_t)memorySize); if (newMemory) memory = newMemory; } } return true; } //------------------------------------------------------------------------ bool MemoryStream::truncateToCursor () { size = cursor; return truncate (); } } // namespace qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/systemclipboard_win32.cpp0000644000000000000000000000013215124701711025761 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/systemclipboard_win32.cpp0000644000175000001440000001041515124701711025752 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // // Project : Steinberg Plug-In SDK // Filename : public.sdk/source/common/systemclipboard_win32.cpp // Created by : Steinberg 04.2020 // Description : Simple helper allowing to copy/retrieve text to/from the system clipboard // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "systemclipboard.h" #include "pluginterfaces/base/fplatform.h" #if SMTG_OS_WINDOWS #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace SystemClipboard { namespace { //------------------------------------------------------------------------ struct Clipboard { Clipboard () { open = OpenClipboard (nullptr); } ~Clipboard () { if (open) CloseClipboard (); } bool open {false}; }; //------------------------------------------------------------------------ std::vector convertToWide (const std::string& text) { std::vector wideStr; auto numChars = MultiByteToWideChar (CP_UTF8, 0, text.data (), static_cast (text.size ()), nullptr, 0); if (numChars) { wideStr.resize (static_cast (numChars) + 1); numChars = MultiByteToWideChar (CP_UTF8, 0, text.data (), static_cast (text.size ()), wideStr.data (), static_cast (wideStr.size ())); } wideStr[numChars] = 0; wideStr.resize (static_cast (numChars) + 1); return wideStr; } //------------------------------------------------------------------------ std::string convertToUTF8 (const WCHAR* data, const SIZE_T& dataSize) { std::string text; auto numChars = WideCharToMultiByte (CP_UTF8, 0, data, static_cast (dataSize / sizeof (WCHAR)), nullptr, 0, nullptr, nullptr); text.resize (static_cast (numChars) + 1); numChars = WideCharToMultiByte (CP_UTF8, 0, data, static_cast (dataSize / sizeof (WCHAR)), const_cast (text.data ()), static_cast (text.size ()), nullptr, nullptr); text.resize (numChars); return text; } //------------------------------------------------------------------------ } // anonymous //----------------------------------------------------------------------------- bool copyTextToClipboard (const std::string& text) { Clipboard cb; if (text.empty () || !cb.open) return false; if (!EmptyClipboard ()) return false; bool result = false; auto wideStr = convertToWide (text); auto byteSize = wideStr.size () * sizeof (WCHAR); if (auto memory = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, byteSize)) { if (auto* data = static_cast (GlobalLock (memory))) { #if defined(__MINGW32__) memcpy (data, wideStr.data (), byteSize); #else memcpy_s (data, byteSize, wideStr.data (), byteSize); #endif GlobalUnlock (memory); auto handle = SetClipboardData (CF_UNICODETEXT, memory); result = handle != nullptr; } } return result; } //----------------------------------------------------------------------------- bool getTextFromClipboard (std::string& text) { Clipboard cb; if (!cb.open) return false; if (!IsClipboardFormatAvailable (CF_UNICODETEXT)) return false; bool result = false; // Get handle of clipboard object for unicode text if (auto hData = GetClipboardData (CF_UNICODETEXT)) { // Lock the handle to get the actual text pointer if (auto* data = (const WCHAR*)GlobalLock (hData)) { auto dataSize = GlobalSize (hData); text = convertToUTF8 (data, dataSize); // Release the lock GlobalUnlock (hData); result = true; } } return result; } //------------------------------------------------------------------------ } // namespace SystemClipboard } // namespace Steinberg #endif // SMTG_OS_WINDOWS qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/pluginview.h0000644000000000000000000000013215124701711023371 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/pluginview.h0000644000175000001440000000645415124701711023372 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : Common Base Classes // Filename : public.sdk/source/common/pluginview.h // Created by : Steinberg, 01/2004 // Description : Plug-In View Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/gui/iplugview.h" #include "base/source/fobject.h" namespace Steinberg { //------------------------------------------------------------------------ /** Plug-In view default implementation. \ingroup sdkBase Can be used as base class for an IPlugView implementation. */ class CPluginView : public FObject, public IPlugView { public: //------------------------------------------------------------------------ CPluginView (const ViewRect* rect = nullptr); ~CPluginView () SMTG_OVERRIDE; /** Returns its current frame rectangle. */ const ViewRect& getRect () const { return rect; } /** Sets a new frame rectangle. */ void setRect (const ViewRect& r) { rect = r; } /** Checks if this view is attached to its parent view. */ bool isAttached () const { return systemWindow != nullptr; } /** Calls when this view will be attached to its parent view. */ virtual void attachedToParent () {} /** Calls when this view will be removed from its parent view. */ virtual void removedFromParent () {} //---from IPlugView------- tresult PLUGIN_API isPlatformTypeSupported (FIDString type) SMTG_OVERRIDE; tresult PLUGIN_API attached (void* parent, FIDString type) SMTG_OVERRIDE; tresult PLUGIN_API removed () SMTG_OVERRIDE; tresult PLUGIN_API onWheel (float /*distance*/) SMTG_OVERRIDE { return kResultFalse; } tresult PLUGIN_API onKeyDown (char16 /*key*/, int16 /*keyMsg*/, int16 /*modifiers*/) SMTG_OVERRIDE { return kResultFalse; } tresult PLUGIN_API onKeyUp (char16 /*key*/, int16 /*keyMsg*/, int16 /*modifiers*/) SMTG_OVERRIDE { return kResultFalse; } tresult PLUGIN_API getSize (ViewRect* size) SMTG_OVERRIDE; tresult PLUGIN_API onSize (ViewRect* newSize) SMTG_OVERRIDE; tresult PLUGIN_API onFocus (TBool /*state*/) SMTG_OVERRIDE { return kResultFalse; } tresult PLUGIN_API setFrame (IPlugFrame* frame) SMTG_OVERRIDE { plugFrame = frame; return kResultTrue; } tresult PLUGIN_API canResize () SMTG_OVERRIDE { return kResultFalse; } tresult PLUGIN_API checkSizeConstraint (ViewRect* /*rect*/) SMTG_OVERRIDE { return kResultFalse; } //---Interface------ OBJ_METHODS (CPluginView, FObject) DEFINE_INTERFACES DEF_INTERFACE (IPlugView) END_DEFINE_INTERFACES (FObject) REFCOUNT_METHODS (FObject) //------------------------------------------------------------------------ protected: ViewRect rect; void* systemWindow {nullptr}; IPtr plugFrame; }; //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/readfile.cpp0000644000000000000000000000013215124701711023306 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/readfile.cpp0000644000175000001440000000361615124701711023304 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // // Category : readfile // Filename : public.sdk/source/common/readfile.cpp // Created by : Steinberg, 3/2023 // Description : read file routine // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "readfile.h" #include "pluginterfaces/base/fplatform.h" #if SMTG_OS_WINDOWS #include "commonstringconvert.h" #endif #include #if !SMTG_CPP17 #include #endif namespace Steinberg { //------------------------------------------------------------------------ std::string readFile (const std::string& path) { #if SMTG_OS_WINDOWS auto u16Path = StringConvert::convert (path); std::ifstream file (reinterpret_cast (u16Path.data ()), std::ios_base::in | std::ios_base::binary); #else std::ifstream file (path, std::ios_base::in | std::ios_base::binary); #endif if (!file.is_open ()) return {}; #if SMTG_CPP17 auto size = file.seekg (0, std::ios_base::end).tellg (); file.seekg (0, std::ios_base::beg); std::string data; data.resize (size); file.read (data.data (), data.size ()); if (file.bad ()) return {}; return data; #else std::stringstream buffer; buffer << file.rdbuf (); return buffer.str (); #endif // SMTG_CPP17 } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/systemclipboard_linux.cpp0000644000000000000000000000013215124701711026156 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/systemclipboard_linux.cpp0000644000175000001440000000303215124701711026144 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // // Project : Steinberg Plug-In SDK // Filename : public.sdk/source/common/systemclipboard_linux.cpp // Created by : Steinberg 04.2023 // Description : Simple helper allowing to copy/retrieve text to/from the system clipboard // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "systemclipboard.h" #include "pluginterfaces/base/fplatform.h" #if SMTG_OS_LINUX //------------------------------------------------------------------------ namespace Steinberg { namespace SystemClipboard { //----------------------------------------------------------------------------- bool copyTextToClipboard (const std::string& text) { // TODO return false; } //----------------------------------------------------------------------------- bool getTextFromClipboard (std::string& text) { // TODO return false; } //------------------------------------------------------------------------ } // namespace SystemClipboard } // namespace Steinberg #endif // SMTG_OS_LINUX qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/systemclipboard_mac.mm0000644000000000000000000000013215124701711025406 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/systemclipboard_mac.mm0000644000175000001440000000444715124701711025407 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // // Project : Steinberg Plug-In SDK // Filename : public.sdk/source/common/systemclipboard_mac.mm // Created by : Steinberg 04.2020 // Description : Simple helper allowing to copy/retrieve text to/from the system clipboard // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "systemclipboard.h" #include "pluginterfaces/base/fplatform.h" #if SMTG_OS_OSX #import //------------------------------------------------------------------------ namespace Steinberg { namespace SystemClipboard { //----------------------------------------------------------------------------- bool copyTextToClipboard (const std::string& text) { auto pb = [NSPasteboard generalPasteboard]; [pb clearContents]; auto nsString = [NSString stringWithUTF8String:text.data ()]; return [pb setString:nsString forType:NSPasteboardTypeString]; } //----------------------------------------------------------------------------- bool getTextFromClipboard (std::string& text) { auto pb = [NSPasteboard generalPasteboard]; if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeString]]) { if (auto items = [pb readObjectsForClasses:@[[NSString class]] options:nil]) { if (items.count > 0) { text = [items[0] UTF8String]; return true; } } } return false; } //------------------------------------------------------------------------ } // namespace SystemClipboard } // namespace Steinberg #elif SMTG_OS_IOS //------------------------------------------------------------------------ bool copyTextToClipboard (const std::string& text) { return false; } //------------------------------------------------------------------------ bool getTextFromClipboard (std::string& text) { return false; } #endif // SMTG_OS_MACOS qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/readfile.h0000644000000000000000000000013215124701711022753 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/readfile.h0000644000175000001440000000226515124701711022750 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : readfile // Filename : public.sdk/source/common/readfile.h // Created by : Steinberg, 3/2023 // Description : read file routine // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include namespace Steinberg { //------------------------------------------------------------------------ /** Reads entire file content \ingroup sdkBase Returns entire file content at the given path \endcode */ std::string readFile (const std::string& path); //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/common/PaxHeaders/memorystream.h0000644000000000000000000000013215124701711023724 xustar0030 mtime=1767080905.276536456 30 atime=1767080905.276536456 30 ctime=1767080905.276536456 qtractor-1.5.11/src/vst3/public.sdk/source/common/memorystream.h0000644000175000001440000000475615124701711023730 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : Common Classes // Filename : public.sdk/source/common/memorystream.h // Created by : Steinberg, 03/2008 // Description : IBStream Implementation for memory blocks // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ibstream.h" namespace Steinberg { //------------------------------------------------------------------------ /** Memory based Stream for IBStream implementation (using malloc). \ingroup sdkBase */ class MemoryStream : public IBStream { public: //------------------------------------------------------------------------ MemoryStream (); MemoryStream (void* memory, TSize memorySize); ///< reuse a given memory without getting ownership virtual ~MemoryStream (); //---IBStream--------------------------------------- tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead) SMTG_OVERRIDE; tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten) SMTG_OVERRIDE; tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result) SMTG_OVERRIDE; tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; TSize getSize () const; ///< returns the current memory size void setSize (TSize size); ///< set the memory size, a realloc will occur if memory already used char* getData () const; ///< returns the memory pointer char* detachData (); ///< returns the memory pointer and give up ownership bool truncate (); ///< realloc to the current use memory size if needed bool truncateToCursor (); ///< truncate memory at current cursor position //------------------------------------------------------------------------ DECLARE_FUNKNOWN_METHODS protected: char* memory; // memory block TSize memorySize; // size of the memory block TSize size; // size of the stream int64 cursor; // stream pointer bool ownMemory; // stream has allocated memory itself bool allocationError; // stream invalid }; } // namespace qtractor-1.5.11/src/vst3/public.sdk/source/PaxHeaders/vst0000644000000000000000000000013015124701711020274 xustar0029 mtime=1767080905.28721084 30 atime=1767080905.277062199 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/0000755000175000001440000000000015124701711020343 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstpresetfile.h0000644000000000000000000000012715124701711023427 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstpresetfile.h0000644000175000001440000003005215124701711023413 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstpresetfile.h // Created by : Steinberg, 03/2006 // Description : VST 3 Preset File Format // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstcomponent.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstunits.h" #include "pluginterfaces/base/ibstream.h" #include "base/source/fbuffer.h" #include #include //------------------------------------------------------------------------ /* VST 3 Preset File Format Definition =================================== 0 +---------------------------+ | HEADER | | header id ('VST3') | 4 Bytes | version | 4 Bytes (int32) | ASCII-encoded class id | 32 Bytes +--| offset to chunk list | 8 Bytes (int64) | +---------------------------+ | | DATA AREA |<-+ | | data of chunks 1..n | | | ... ... | | | | | +->+---------------------------+ | | CHUNK LIST | | | list id ('List') | | 4 Bytes | entry count | | 4 Bytes (int32) +---------------------------+ | | 1..n | | | +----------------------+ | | | | chunk id | | | 4 Bytes | | offset to chunk data |----+ 8 Bytes (int64) | | size of chunk data | | 8 Bytes (int64) | +----------------------+ | EOF +---------------------------+ */ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ using ChunkID = char[4]; //------------------------------------------------------------------------ enum ChunkType { kHeader, kComponentState, kControllerState, kProgramData, kMetaInfo, kChunkList, kNumPresetChunks }; //------------------------------------------------------------------------ extern const ChunkID& getChunkID (ChunkType type); //------------------------------------------------------------------------ inline bool isEqualID (const ChunkID id1, const ChunkID id2) { return memcmp (id1, id2, sizeof (ChunkID)) == 0; } //------------------------------------------------------------------------ /** Handler for a VST 3 Preset File. \ingroup vstClasses \see \ref presetformat */ class PresetFile { public: //------------------------------------------------------------------------ PresetFile (IBStream* stream); ///< Constructor of Preset file based on a stream virtual ~PresetFile (); /** Internal structure used for chunk handling */ struct Entry { ChunkID id; TSize offset; TSize size; }; IBStream* getStream () const { return stream; } ///< Returns the associated stream. const FUID& getClassID () const { return classID; } ///< Returns the associated classID (component ID: Processor part (not the controller!)). void setClassID (const FUID& uid) { classID = uid; }///< Sets the associated classID (component ID: Processor part (not the controller!)). const Entry* getEntry (ChunkType which) const; ///< Returns an entry for a given chunk type. const Entry* getLastEntry () const; ///< Returns the last available entry. int32 getEntryCount () const { return entryCount; } ///< Returns the number of total entries in the current stream. const Entry& at (int32 index) const { return entries[index]; } ///< Returns the entry at a given position. bool contains (ChunkType which) const { return getEntry (which) != nullptr; } ///< Checks if a given chunk type exist in the stream. bool readChunkList (); ///< Reads and build the chunk list (including the header chunk). bool writeHeader (); ///< Writes into the stream the main header. bool writeChunkList (); ///< Writes into the stream the chunk list (should be at the end). /** Reads the meta XML info and its size, the size could be retrieved by passing zero as xmlBuffer. */ bool readMetaInfo (char* xmlBuffer, int32& size); /** Writes the meta XML info, -1 means null-terminated, forceWriting to true will force to rewrite the XML Info when the chunk already exists. */ bool writeMetaInfo (const char* xmlBuffer, int32 size = -1, bool forceWriting = false); bool prepareMetaInfoUpdate (); ///< checks if meta info chunk is the last one and jump to correct position. /** Writes a given data of a given size as "which" chunk type. */ bool writeChunk (const void* data, int32 size, ChunkType which = kComponentState); //------------------------------------------------------------- // for storing and restoring the whole plug-in state (component and controller states) bool seekToComponentState (); ///< Seeks to the begin of the Component State. bool storeComponentState (IComponent* component); ///< Stores the component state (only one time). bool storeComponentState (IBStream* componentStream); ///< Stores the component state from stream (only one time). bool restoreComponentState (IComponent* component); ///< Restores the component state. bool seekToControllerState (); ///< Seeks to the begin of the Controller State. bool storeControllerState (IEditController* editController);///< Stores the controller state (only one time). bool storeControllerState (IBStream* editStream); ///< Stores the controller state from stream (only one time). bool restoreControllerState (IEditController* editController);///< Restores the controller state. bool restoreComponentState (IEditController* editController);///< Restores the component state and apply it to the controller. //--- ---------------------------------------------------------- /** Store program data or unit data from stream (including the header chunk). \param inStream \param listID could be ProgramListID or UnitID. */ bool storeProgramData (IBStream* inStream, ProgramListID listID); //---when plug-in uses IProgramListData----------------------- /** Stores a IProgramListData with a given identifier and index (including the header chunk). */ bool storeProgramData (IProgramListData* programListData, ProgramListID programListID, int32 programIndex); /** Restores a IProgramListData with a given identifier and index. */ bool restoreProgramData (IProgramListData* programListData, ProgramListID* programListID = nullptr, int32 programIndex = 0); //---when plug-in uses IUnitData------------------------------ /** Stores a IUnitData with a given unitID (including the header chunk). */ bool storeProgramData (IUnitData* unitData, UnitID unitID); /** Restores a IUnitData with a given unitID (optional). */ bool restoreProgramData (IUnitData* unitData, UnitID* unitID = nullptr); //--- ---------------------------------------------------------- /** for keeping the controller part in sync concerning preset data stream, unitProgramListID * could be ProgramListID or UnitID. */ bool restoreProgramData (IUnitInfo* unitInfo, int32 unitProgramListID, int32 programIndex = -1); /** Gets the unitProgramListID saved in the kProgramData chunk (if available). */ bool getUnitProgramListID (int32& unitProgramListID); //--- --------------------------------------------------------------------- /** Shortcut helper to create preset from component/controller state. classID is the FUID of the * component (processor) part. */ static bool savePreset (IBStream* stream, const FUID& classID, IComponent* component, IEditController* editController = nullptr, const char* xmlBuffer = nullptr, int32 xmlSize = -1); static bool savePreset (IBStream* stream, const FUID& classID, IBStream* componentStream, IBStream* editStream = nullptr, const char* xmlBuffer = nullptr, int32 xmlSize = -1); /** Shortcut helper to load preset with component/controller state. classID is the FUID of the * component (processor) part. */ static bool loadPreset (IBStream* stream, const FUID& classID, IComponent* component, IEditController* editController = nullptr, std::vector* otherClassIDArray = nullptr); //------------------------------------------------------------------------ protected: bool readID (ChunkID id); bool writeID (const ChunkID id); bool readEqualID (const ChunkID id); bool readSize (TSize& size); bool writeSize (TSize size); bool readInt32 (int32& value); bool writeInt32 (int32 value); bool seekTo (TSize offset); bool beginChunk (Entry& e, ChunkType which); bool endChunk (Entry& e); IBStream* stream; FUID classID; ///< classID is the FUID of the component (processor) part enum { kMaxEntries = 128 }; Entry entries[kMaxEntries]; int32 entryCount {0}; }; //------------------------------------------------------------------------ /** Stream implementation for a file using stdio. */ class FileStream: public IBStream { public: //------------------------------------------------------------------------ static IBStream* open (const char* filename, const char* mode); ///< open a stream using stdio function //---from FUnknown------------------ DECLARE_FUNKNOWN_METHODS //---from IBStream------------------ tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = nullptr) SMTG_OVERRIDE; tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = nullptr) SMTG_OVERRIDE; tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = nullptr) SMTG_OVERRIDE; tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; //------------------------------------------------------------------------ protected: FileStream (FILE* file); virtual ~FileStream (); FILE* file; }; //------------------------------------------------------------------------ /** Stream representing a Read-Only subsection of its source stream. */ class ReadOnlyBStream: public IBStream { public: //------------------------------------------------------------------------ ReadOnlyBStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize); virtual ~ReadOnlyBStream (); //---from FUnknown------------------ DECLARE_FUNKNOWN_METHODS //---from IBStream------------------ tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = nullptr) SMTG_OVERRIDE; tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = nullptr) SMTG_OVERRIDE; tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = nullptr) SMTG_OVERRIDE; tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; //------------------------------------------------------------------------ protected: IBStream* sourceStream; TSize sourceOffset; TSize sectionSize; TSize seekPosition; }; //------------------------------------------------------------------------ /** Stream implementation for a memory buffer. */ class BufferStream : public IBStream { public: BufferStream (); virtual ~BufferStream (); //---from FUnknown------------------ DECLARE_FUNKNOWN_METHODS //---from IBStream------------------ tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = nullptr) SMTG_OVERRIDE; tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = nullptr) SMTG_OVERRIDE; tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = nullptr) SMTG_OVERRIDE; tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; protected: Buffer mBuffer; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstbus.h0000644000000000000000000000013215124701711022052 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstbus.h0000644000175000001440000001135115124701711022043 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstbus.h // Created by : Steinberg, 03/2008 // Description : VST Bus Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "base/source/fobject.h" #include "pluginterfaces/vst/ivstcomponent.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include #if defined(SMTG_CPP_17) && SMTG_CPP_17 #include #endif #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Basic Bus object. \ingroup vstClasses */ class Bus: public FObject { public: //------------------------------------------------------------------------ /** Constructor. */ Bus (const TChar* name, BusType busType, int32 flags); /** Returns true if the bus is active. */ TBool isActive () const { return active; } /** Activates the bus. */ void setActive (TBool state) { active = state; } #if defined(SMTG_CPP_17) && SMTG_CPP_17 /** Sets a new name for this bus. */ void setName (std::u16string_view newName) { name = newName; } #else /** Sets a new name for this bus. */ void setName (const std::u16string& newName) { name = newName; } #endif /** Sets a new busType for this bus. */ void setBusType (BusType newBusType) { busType = newBusType; } /** Sets a new flags for this bus. */ void setFlags (uint32 newFlags) { flags = newFlags; } /** Gets the BusInfo of this bus. */ virtual bool getInfo (BusInfo&); OBJ_METHODS (Vst::Bus, FObject) //------------------------------------------------------------------------ protected: std::u16string name; ///< name BusType busType; ///< kMain or kAux, see \ref BusTypes int32 flags; ///< flags, see \ref BusInfo::BusFlags TBool active; ///< activation state }; //------------------------------------------------------------------------ /** Description of an Event Bus. \ingroup vstClasses */ class EventBus: public Bus { public: //------------------------------------------------------------------------ /** Constructor of an EventBus. */ EventBus (const TChar* name, BusType busType, int32 flags, int32 channelCount); //---from Bus------- /** Gets the BusInfo associated to this Event bus. */ bool getInfo (BusInfo& info) SMTG_OVERRIDE; OBJ_METHODS (Vst::EventBus, Vst::Bus); //------------------------------------------------------------------------ protected: int32 channelCount; }; //------------------------------------------------------------------------ /** Description of an Audio Bus. \ingroup vstClasses */ class AudioBus: public Bus { public: //------------------------------------------------------------------------ AudioBus (const TChar* name, BusType busType, int32 flags, SpeakerArrangement arr); /** Gets the speaker arrangement defining this Audio bus. */ SpeakerArrangement getArrangement () const { return speakerArr; } /** Sets the speaker arrangement defining this Audio bus. */ void setArrangement (const SpeakerArrangement& arr) { speakerArr = arr; } //---from Bus--------------------- /** Gets the BusInfo associated to this Audio bus. */ bool getInfo (BusInfo& info) SMTG_OVERRIDE; OBJ_METHODS (Vst::AudioBus, Vst::Bus) //------------------------------------------------------------------------ protected: SpeakerArrangement speakerArr; }; //------------------------------------------------------------------------ /** List of Busses. \ingroup vstClasses */ class BusList: public FObject, public std::vector > { public: //------------------------------------------------------------------------ /** Constructor. */ BusList (MediaType type, BusDirection dir); /** Returns the BusList Type. See \ref MediaType */ MediaType getType () const { return type; } /** Returns the BusList direction. See \ref BusDirection */ BusDirection getDirection () const { return direction; } OBJ_METHODS (Vst::BusList, FObject); //------------------------------------------------------------------------ protected: MediaType type; BusDirection direction; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstpresetfile.cpp0000644000000000000000000000012715124701711023762 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstpresetfile.cpp0000644000175000001440000006212515124701711023754 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstpresetfile.cpp // Created by : Steinberg, 03/2006 // Description : VST 3 Preset File Format // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstpresetfile.h" #include namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // Preset Chunk IDs //------------------------------------------------------------------------ static const ChunkID commonChunks[kNumPresetChunks] = { {'V', 'S', 'T', '3'}, // kHeader {'C', 'o', 'm', 'p'}, // kComponentState {'C', 'o', 'n', 't'}, // kControllerState {'P', 'r', 'o', 'g'}, // kProgramData {'I', 'n', 'f', 'o'}, // kMetaInfo {'L', 'i', 's', 't'} // kChunkList }; //------------------------------------------------------------------------ // Preset Header: header id + version + class id + list offset static const int32 kFormatVersion = 1; static const int32 kClassIDSize = 32; // ASCII-encoded FUID static const int32 kHeaderSize = sizeof (ChunkID) + sizeof (int32) + kClassIDSize + sizeof (TSize); static const int32 kListOffsetPos = kHeaderSize - sizeof (TSize); //------------------------------------------------------------------------ const ChunkID& getChunkID (ChunkType type) { return commonChunks[type]; } #ifdef verify #undef verify #endif //------------------------------------------------------------------------ inline bool verify (tresult result) { return result == kResultOk || result == kNotImplemented; } //------------------------------------------------------------------------ bool copyStream (IBStream* inStream, IBStream* outStream) { if (!inStream || !outStream) return false; int8 buffer[8192]; int32 read = 0; int32 written = 0; while (inStream->read (buffer, 8192, &read) == kResultTrue && read > 0) { if (outStream->write (buffer, read, &written) != kResultTrue) { return false; } } return true; } //------------------------------------------------------------------------ // PresetFile //------------------------------------------------------------------------ bool PresetFile::savePreset (IBStream* stream, const FUID& classID, IComponent* component, IEditController* editController, const char* xmlBuffer, int32 xmlSize) { PresetFile pf (stream); pf.setClassID (classID); if (!pf.writeHeader ()) return false; if (!pf.storeComponentState (component)) return false; if (editController && !pf.storeControllerState (editController)) return false; if (xmlBuffer && !pf.writeMetaInfo (xmlBuffer, xmlSize)) return false; return pf.writeChunkList (); } //------------------------------------------------------------------------ bool PresetFile::savePreset (IBStream* stream, const FUID& classID, IBStream* componentStream, IBStream* editStream, const char* xmlBuffer, int32 xmlSize) { PresetFile pf (stream); pf.setClassID (classID); if (!pf.writeHeader ()) return false; if (!pf.storeComponentState (componentStream)) return false; if (editStream && !pf.storeControllerState (editStream)) return false; if (xmlBuffer && !pf.writeMetaInfo (xmlBuffer, xmlSize)) return false; return pf.writeChunkList (); } //------------------------------------------------------------------------ bool PresetFile::loadPreset (IBStream* stream, const FUID& classID, IComponent* component, IEditController* editController, std::vector* otherClassIDArray) { PresetFile pf (stream); if (!pf.readChunkList ()) return false; if (pf.getClassID () != classID) { if (otherClassIDArray) { // continue to load only if found in supported ID else abort load if (std::find (otherClassIDArray->begin (), otherClassIDArray->end (), pf.getClassID ()) == otherClassIDArray->end ()) return false; } else return false; } if (!pf.restoreComponentState (component)) return false; if (editController) { // assign component state to controller if (!pf.restoreComponentState (editController)) return false; // restore controller-only state (if present) if (pf.contains (kControllerState) && !pf.restoreControllerState (editController)) return false; } return true; } //------------------------------------------------------------------------ PresetFile::PresetFile (IBStream* stream) : stream (stream) { memset (entries, 0, sizeof (entries)); if (stream) stream->addRef (); } //------------------------------------------------------------------------ PresetFile::~PresetFile () { if (stream) stream->release (); } //------------------------------------------------------------------------ const PresetFile::Entry* PresetFile::getEntry (ChunkType which) const { const ChunkID& id = getChunkID (which); for (int32 i = 0; i < entryCount; i++) if (isEqualID (entries[i].id, id)) return &entries[i]; return nullptr; } //------------------------------------------------------------------------ const PresetFile::Entry* PresetFile::getLastEntry () const { return entryCount > 0 ? &entries[entryCount - 1] : nullptr; } //------------------------------------------------------------------------ bool PresetFile::readID (ChunkID id) { int32 numBytesRead = 0; stream->read (id, sizeof (ChunkID), &numBytesRead); return numBytesRead == sizeof (ChunkID); } //------------------------------------------------------------------------ bool PresetFile::writeID (const ChunkID id) { int32 numBytesWritten = 0; stream->write ((void*)id, sizeof (ChunkID), &numBytesWritten); return numBytesWritten == sizeof (ChunkID); } //------------------------------------------------------------------------ bool PresetFile::readEqualID (const ChunkID id) { ChunkID temp = {0}; return readID (temp) && isEqualID (temp, id); } //------------------------------------------------------------------------ bool PresetFile::readSize (TSize& size) { int32 numBytesRead = 0; stream->read (&size, sizeof (TSize), &numBytesRead); #if BYTEORDER == kBigEndian SWAP_64 (size) #endif return numBytesRead == sizeof (TSize); } //------------------------------------------------------------------------ bool PresetFile::writeSize (TSize size) { #if BYTEORDER == kBigEndian SWAP_64 (size) #endif int32 numBytesWritten = 0; stream->write (&size, sizeof (TSize), &numBytesWritten); return numBytesWritten == sizeof (TSize); } //------------------------------------------------------------------------ bool PresetFile::readInt32 (int32& value) { int32 numBytesRead = 0; stream->read (&value, sizeof (int32), &numBytesRead); #if BYTEORDER == kBigEndian SWAP_32 (value) #endif return numBytesRead == sizeof (int32); } //------------------------------------------------------------------------ bool PresetFile::writeInt32 (int32 value) { #if BYTEORDER == kBigEndian SWAP_32 (value) #endif int32 numBytesWritten = 0; stream->write (&value, sizeof (int32), &numBytesWritten); return numBytesWritten == sizeof (int32); } //------------------------------------------------------------------------ bool PresetFile::seekTo (TSize offset) { int64 result = -1; stream->seek (offset, IBStream::kIBSeekSet, &result); return result == offset; } //------------------------------------------------------------------------ bool PresetFile::readChunkList () { seekTo (0); entryCount = 0; char8 classString[kClassIDSize + 1] = {0}; // Read header int32 version = 0; TSize listOffset = 0; if (!(readEqualID (getChunkID (kHeader)) && readInt32 (version) && verify (stream->read (classString, kClassIDSize)) && readSize (listOffset) && listOffset > 0 && seekTo (listOffset))) return false; classID.fromString (classString); // Read list int32 count = 0; if (!readEqualID (getChunkID (kChunkList))) return false; if (!readInt32 (count)) return false; if (count > kMaxEntries) count = kMaxEntries; for (int32 i = 0; i < count; i++) { Entry& e = entries[i]; if (!(readID (e.id) && readSize (e.offset) && readSize (e.size))) break; entryCount++; } return entryCount > 0; } //------------------------------------------------------------------------ bool PresetFile::writeHeader () { // header id + version + class id + list offset (unknown yet) char8 classString[kClassIDSize + 1] = {0}; classID.toString (classString); return seekTo (0) && writeID (getChunkID (kHeader)) && writeInt32 (kFormatVersion) && verify (stream->write (classString, kClassIDSize)) && writeSize (0); } //------------------------------------------------------------------------ bool PresetFile::writeChunkList () { // Update list offset TSize pos = 0; stream->tell (&pos); if (!(seekTo (kListOffsetPos) && writeSize (pos) && seekTo (pos))) return false; // Write list if (!writeID (getChunkID (kChunkList))) return false; if (!writeInt32 (entryCount)) return false; for (int32 i = 0; i < entryCount; i++) { Entry& e = entries[i]; if (!(writeID (e.id) && writeSize (e.offset) && writeSize (e.size))) return false; } return true; } //------------------------------------------------------------------------ bool PresetFile::beginChunk (Entry& e, ChunkType which) { if (entryCount >= kMaxEntries) return false; const ChunkID& id = getChunkID (which); memcpy (e.id, &id, sizeof (ChunkID)); stream->tell (&e.offset); e.size = 0; return true; } //------------------------------------------------------------------------ bool PresetFile::endChunk (Entry& e) { if (entryCount >= kMaxEntries) return false; TSize pos = 0; stream->tell (&pos); e.size = pos - e.offset; entries[entryCount++] = e; return true; } //------------------------------------------------------------------------ bool PresetFile::readMetaInfo (char* xmlBuffer, int32& size) { bool result = false; const Entry* e = getEntry (kMetaInfo); if (e) { if (xmlBuffer) { result = seekTo (e->offset) && verify (stream->read (xmlBuffer, size, &size)); } else { size = (int32)e->size; result = size > 0; } } return result; } //------------------------------------------------------------------------ bool PresetFile::writeMetaInfo (const char* xmlBuffer, int32 size, bool forceWriting) { if (contains (kMetaInfo)) // already exists! { if (!forceWriting) return false; } if (!prepareMetaInfoUpdate ()) return false; if (size == -1) size = (int32)strlen (xmlBuffer); Entry e = {}; return beginChunk (e, kMetaInfo) && verify (stream->write ((void*)xmlBuffer, size)) && endChunk (e); } //------------------------------------------------------------------------ bool PresetFile::prepareMetaInfoUpdate () { TSize writePos = 0; const Entry* e = getEntry (kMetaInfo); if (e) { // meta info must be the last entry! if (e != getLastEntry ()) return false; writePos = e->offset; entryCount--; } else { // entries must be sorted ascending by offset! e = getLastEntry (); writePos = e ? e->offset + e->size : kHeaderSize; } return seekTo (writePos); } //------------------------------------------------------------------------ bool PresetFile::writeChunk (const void* data, int32 size, ChunkType which) { if (contains (which)) // already exists! return false; Entry e = {}; return beginChunk (e, which) && verify (stream->write ((void*)data, size)) && endChunk (e); } //------------------------------------------------------------------------ bool PresetFile::seekToComponentState () { const Entry* e = getEntry (kComponentState); return e && seekTo (e->offset); } //------------------------------------------------------------------------ bool PresetFile::storeComponentState (IComponent* component) { if (contains (kComponentState)) // already exists! return false; Entry e = {}; return beginChunk (e, kComponentState) && verify (component->getState (stream)) && endChunk (e); } //------------------------------------------------------------------------ bool PresetFile::storeComponentState (IBStream* componentStream) { if (contains (kComponentState)) // already exists! return false; Entry e = {}; return beginChunk (e, kComponentState) && copyStream (componentStream, stream) && endChunk (e); } //------------------------------------------------------------------------ bool PresetFile::restoreComponentState (IComponent* component) { const Entry* e = getEntry (kComponentState); if (!e) return false; auto readOnlyBStream = owned (new ReadOnlyBStream (stream, e->offset, e->size)); return verify (component->setState (readOnlyBStream)); } //------------------------------------------------------------------------ bool PresetFile::restoreComponentState (IEditController* editController) { const Entry* e = getEntry (kComponentState); if (e) { auto readOnlyBStream = owned (new ReadOnlyBStream (stream, e->offset, e->size)); return verify (editController->setComponentState (readOnlyBStream)); } return false; } //------------------------------------------------------------------------ bool PresetFile::seekToControllerState () { const Entry* e = getEntry (kControllerState); return e && seekTo (e->offset); } //------------------------------------------------------------------------ bool PresetFile::storeControllerState (IEditController* editController) { if (contains (kControllerState)) // already exists! return false; Entry e = {}; return beginChunk (e, kControllerState) && verify (editController->getState (stream)) && endChunk (e); } //------------------------------------------------------------------------ bool PresetFile::storeControllerState (IBStream* editStream) { if (contains (kControllerState)) // already exists! return false; Entry e = {}; return beginChunk (e, kControllerState) && copyStream (editStream, stream) && endChunk (e); } //------------------------------------------------------------------------ bool PresetFile::restoreControllerState (IEditController* editController) { const Entry* e = getEntry (kControllerState); if (e) { auto readOnlyBStream = owned (new ReadOnlyBStream (stream, e->offset, e->size)); return verify (editController->setState (readOnlyBStream)) || e->size == 0; } return false; } //------------------------------------------------------------------------ bool PresetFile::storeProgramData (IBStream* inStream, ProgramListID listID) { if (contains (kProgramData)) // already exists! return false; writeHeader (); Entry e = {}; if (beginChunk (e, kProgramData)) { if (writeInt32 (listID)) { if (!copyStream (inStream, stream)) return false; return endChunk (e); } } return false; } //------------------------------------------------------------------------ bool PresetFile::storeProgramData (IProgramListData* programListData, ProgramListID listID, int32 programIndex) { if (contains (kProgramData)) // already exists! return false; writeHeader (); Entry e = {}; return beginChunk (e, kProgramData) && writeInt32 (listID) && verify (programListData->getProgramData (listID, programIndex, stream)) && endChunk (e); } //------------------------------------------------------------------------ bool PresetFile::restoreProgramData (IProgramListData* programListData, ProgramListID* programListID, int32 programIndex) { const Entry* e = getEntry (kProgramData); ProgramListID savedProgramListID = -1; if (e && seekTo (e->offset)) { if (readInt32 (savedProgramListID)) { if (programListID && *programListID != savedProgramListID) return false; int32 alreadyRead = sizeof (int32); auto readOnlyBStream = owned ( new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead)); return programListData && verify (programListData->setProgramData ( savedProgramListID, programIndex, readOnlyBStream)); } } return false; } //------------------------------------------------------------------------ bool PresetFile::storeProgramData (IUnitData* unitData, UnitID unitID) { if (contains (kProgramData)) // already exists! return false; writeHeader (); Entry e = {}; return beginChunk (e, kProgramData) && writeInt32 (unitID) && verify (unitData->getUnitData (unitID, stream)) && endChunk (e); } //------------------------------------------------------------------------ bool PresetFile::restoreProgramData (IUnitData* unitData, UnitID* unitId) { const Entry* e = getEntry (kProgramData); UnitID savedUnitID = -1; if (e && seekTo (e->offset)) { if (readInt32 (savedUnitID)) { if (unitId && *unitId != savedUnitID) return false; int32 alreadyRead = sizeof (int32); auto readOnlyBStream = owned ( new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead)); return (unitData && verify (unitData->setUnitData (savedUnitID, readOnlyBStream))); } } return false; } //------------------------------------------------------------------------ bool PresetFile::restoreProgramData (IUnitInfo* unitInfo, int32 unitProgramListID, int32 programIndex) { const Entry* e = getEntry (kProgramData); int32 savedProgramListID = -1; if (e && seekTo (e->offset)) { if (readInt32 (savedProgramListID)) { if (unitProgramListID != savedProgramListID) return false; int32 alreadyRead = sizeof (int32); auto readOnlyBStream = owned ( new ReadOnlyBStream (stream, e->offset + alreadyRead, e->size - alreadyRead)); return (unitInfo && unitInfo->setUnitProgramData (unitProgramListID, programIndex, readOnlyBStream)); } } return false; } //------------------------------------------------------------------------ bool PresetFile::getUnitProgramListID (int32& unitProgramListID) { const Entry* e = getEntry (kProgramData); if (e && seekTo (e->offset)) { if (readInt32 (unitProgramListID)) { return true; } } return false; } //------------------------------------------------------------------------ // FileStream implementation //------------------------------------------------------------------------ IBStream* FileStream::open (const char* filename, const char* mode) { FILE* file = fopen (filename, mode); return file ? new FileStream (file) : nullptr; } //------------------------------------------------------------------------ FileStream::FileStream (FILE* file) : file (file) {FUNKNOWN_CTOR} //------------------------------------------------------------------------ FileStream::~FileStream () { fclose (file); FUNKNOWN_DTOR } //------------------------------------------------------------------------ IMPLEMENT_FUNKNOWN_METHODS (FileStream, IBStream, IBStream::iid) //------------------------------------------------------------------------ tresult PLUGIN_API FileStream::read (void* buffer, int32 numBytes, int32* numBytesRead) { size_t result = fread (buffer, 1, static_cast (numBytes), file); if (numBytesRead) *numBytesRead = (int32)result; return static_cast (result) == numBytes ? kResultOk : kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API FileStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) { size_t result = fwrite (buffer, 1, static_cast (numBytes), file); if (numBytesWritten) *numBytesWritten = (int32)result; return static_cast (result) == numBytes ? kResultOk : kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API FileStream::seek (int64 pos, int32 mode, int64* result) { if (fseek (file, (int32)pos, mode) == 0) { if (result) *result = ftell (file); return kResultOk; } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API FileStream::tell (int64* pos) { if (pos) *pos = ftell (file); return kResultOk; } //------------------------------------------------------------------------ // ReadOnlyBStream implementation //------------------------------------------------------------------------ IMPLEMENT_REFCOUNT (ReadOnlyBStream) //------------------------------------------------------------------------ ReadOnlyBStream::ReadOnlyBStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize) : sourceStream (sourceStream) , sourceOffset (sourceOffset) , sectionSize (sectionSize) , seekPosition (0) { FUNKNOWN_CTOR if (sourceStream) sourceStream->addRef (); } //------------------------------------------------------------------------ ReadOnlyBStream::~ReadOnlyBStream () { if (sourceStream) sourceStream->release (); FUNKNOWN_DTOR } //------------------------------------------------------------------------ tresult PLUGIN_API ReadOnlyBStream::queryInterface (const TUID _iid, void** obj) { return sourceStream ? sourceStream->queryInterface (_iid, obj) : kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API ReadOnlyBStream::read (void* buffer, int32 numBytes, int32* numBytesRead) { if (numBytesRead) *numBytesRead = 0; if (!sourceStream) return kNotInitialized; int32 maxBytesToRead = static_cast (sectionSize - seekPosition); if (numBytes > maxBytesToRead) numBytes = maxBytesToRead; if (numBytes <= 0) return kResultOk; tresult result = sourceStream->seek (sourceOffset + seekPosition, kIBSeekSet); if (result != kResultOk) return result; int32 numRead = 0; result = sourceStream->read (buffer, numBytes, &numRead); if (numRead > 0) seekPosition += numRead; if (numBytesRead) *numBytesRead = numRead; return result; } //------------------------------------------------------------------------ tresult PLUGIN_API ReadOnlyBStream::write (void* /*buffer*/, int32 /*numBytes*/, int32* numBytesWritten) { if (numBytesWritten) *numBytesWritten = 0; return kNotImplemented; } //------------------------------------------------------------------------ tresult PLUGIN_API ReadOnlyBStream::seek (int64 pos, int32 mode, int64* result) { switch (mode) { case kIBSeekSet: seekPosition = pos; break; case kIBSeekCur: seekPosition += pos; break; case kIBSeekEnd: seekPosition = sectionSize + pos; break; } if (seekPosition < 0) seekPosition = 0; if (seekPosition > sectionSize) seekPosition = sectionSize; if (result) *result = seekPosition; return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API ReadOnlyBStream::tell (int64* pos) { if (pos) *pos = seekPosition; return kResultOk; } //------------------------------------------------------------------------ // BufferStream implementation //------------------------------------------------------------------------ IMPLEMENT_FUNKNOWN_METHODS (BufferStream, IBStream, IBStream::iid) //------------------------------------------------------------------------ BufferStream::BufferStream () {FUNKNOWN_CTOR} //------------------------------------------------------------------------ BufferStream::~BufferStream () {FUNKNOWN_DTOR} //------------------------------------------------------------------------ tresult PLUGIN_API BufferStream::read (void* buffer, int32 numBytes, int32* numBytesRead) { uint32 size = mBuffer.get (buffer, static_cast (numBytes)); if (numBytesRead) *numBytesRead = static_cast (size); return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API BufferStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) { bool res = mBuffer.put (buffer, static_cast (numBytes)); if (numBytesWritten) *numBytesWritten = res ? numBytes : 0; return res ? kResultTrue : kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API BufferStream::seek (int64 pos, int32 mode, int64* result) { bool res = false; switch (mode) { //--- ----------------- case IBStream::kIBSeekSet: { int64 tmp = pos; if (tmp < 0) tmp = 0; res = mBuffer.setFillSize (static_cast (tmp)); } break; //--- ----------------- case IBStream::kIBSeekCur: { int64 tmp = mBuffer.getFillSize () + pos; if (tmp < 0) tmp = 0; res = mBuffer.setFillSize (static_cast (tmp)); } break; //--- ----------------- case IBStream::kIBSeekEnd: { int64 tmp = mBuffer.getSize () - pos; if (tmp < 0) tmp = 0; res = mBuffer.setFillSize (static_cast (tmp)); } break; } if (res && result) *result = mBuffer.getFillSize (); return res ? kResultTrue : kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API BufferStream::tell (int64* pos) { if (pos) *pos = mBuffer.getFillSize (); return pos ? kResultTrue : kResultFalse; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstrepresentation.h0000644000000000000000000000012715124701711024327 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstrepresentation.h0000644000175000001440000001313115124701711024312 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstrepresentation.h // Created by : Steinberg, 08/2010 // Description : VST Representation Helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstrepresentation.h" //------------------------------------------------------------------------ namespace Steinberg { class IBStreamer; namespace Vst { //------------------------------------------------------------------------ /** Helper for XML Representation creation. \ingroup vstClasses Here an example of how to use this helper: \n \code{.cpp} //------------------------------------------------------------------------ // here the parameter ids used by this example enum { kGain = 129, kSize, kCutoff, kResonance, kMaster, kEnable1, kEnable2, kFrequency1, kFrequency2, kGain1, kGain2, }; //------------------------------------------------------------------------ tresult PLUGIN_API MyPlugInController::getXmlRepresentationStream (Vst::RepresentationInfo& info, IBStream* stream) { String name (info.name); if (name == GENERIC_8_CELLS) { Vst::XmlRepresentationHelper helper (info, "My Company Name", "My Product Name", gPlugProcessorClassID, stream); helper.startPage ("Main Page"); helper.startEndCellOneLayer (Vst::LayerType::kKnob, kGain); helper.startEndCellOneLayer (Vst::LayerType::kKnob, kSize); helper.startEndCellOneLayer (Vst::LayerType::kKnob, kMaster); helper.startEndCell (); // empty cell helper.startEndCellOneLayer (Vst::LayerType::kKnob, kCutoff); helper.startEndCellOneLayer (Vst::LayerType::kKnob, kResonance); helper.startEndCell (); // empty cell helper.startEndCell (); // empty cell helper.endPage (); helper.startPage ("Page 2"); helper.startEndCellOneLayer (Vst::LayerType::kSwitch, kEnable1); helper.startEndCellOneLayer (Vst::LayerType::kKnob, kFrequency1); helper.startEndCellOneLayer (Vst::LayerType::kKnob, kGain1); helper.startEndCell (); // empty helper.startEndCellOneLayer (Vst::LayerType::kSwitch, kEnable2); helper.startEndCellOneLayer (Vst::LayerType::kKnob, kFrequency2); helper.startEndCellOneLayer (Vst::LayerType::kKnob, kGain2); helper.startEndCell (); // empty helper.endPage (); return kResultTrue; } return kResultFalse; } \endcode */ class XmlRepresentationHelper { public: XmlRepresentationHelper (const RepresentationInfo& info, const FIDString companyName, const FIDString pluginName, const TUID& pluginUID, IBStream* stream); virtual ~XmlRepresentationHelper (); bool startPage (FIDString name, int32 unitID = -1); ///< Starts a Page before adding a Cell. bool endPage (); ///< Ends a Page before opening a new one. bool startCell (); ///< Starts a Cell before adding a Layer. bool endCell (); ///< Ends a Cell when no more layer needed. bool startEndCell (); ///< Creates an empty cell (alignment for example). /** Starts a layer for a given type (Vst::LayerType), a parameter id, optionally a function (Vst::AttributesFunction) and a style (Vst::AttributesStyle). */ bool startLayer (int32 type, int32 id, FIDString _function = nullptr, FIDString style = nullptr); /** Ends a layer before adding new one */ bool endLayer (); /** Same than startLayer except that the layer will be ended automatically (no need to call * endLayer). */ bool startEndLayer (int32 type, int32 id, FIDString _function = nullptr, FIDString style = nullptr); /** Creates a Cell with 1 Layer and end it, could be only call after a call to startPage */ bool startEndCellOneLayer (int32 type, int32 id, FIDString _function = nullptr, FIDString style = nullptr); /** Starts a layer for a given parameter info and an optional function * (Vst::AttributesFunction). */ bool startLayer (Vst::ParameterInfo& info, FIDString _function = nullptr); /** Same than startLayer with end created automatically. */ bool startEndLayer (Vst::ParameterInfo& info, FIDString _function = nullptr); /** Creates a Cell with 1 Layer and end it, could be only call after a call to startPage. */ bool startEndCellOneLayer (Vst::ParameterInfo& info, FIDString _function = nullptr); /** Creates a Cell with 1 Layer (with name) and end it, could be only call after a call to * startPage. */ bool startEndCellOneLayerWithParamName (Vst::ParameterInfo& info, FIDString _function = nullptr); protected: enum { kInRepresentation = 0, kInPage, kInCell, kInLayer, kInTitleDisplay, kInName }; bool startLayer (int32 type, int32 id, FIDString _function, FIDString style, bool ended); bool startLayer (Vst::ParameterInfo& info, FIDString _function, bool ended); bool startEndTitleDisplay (Vst::ParameterInfo& info); bool checkState (int32 newState); IPtr stream; int32 state; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstgui_win32_bundle_support.h0000644000000000000000000000013215124701711026214 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstgui_win32_bundle_support.h0000644000175000001440000000221315124701711026202 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstgui_win32_bundle_support.h // Created by : Steinberg, 10/2018 // Description : VSTGUI Win32 Bundle Support // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once //----------------------------------------------------------------------------- namespace Steinberg { namespace Vst { //----------------------------------------------------------------------------- void setupVSTGUIBundleSupport (void* hInstance); //----------------------------------------------------------------------------- } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstgui_linux_runloop_support.h0000644000000000000000000000013215124701711026636 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstgui_linux_runloop_support.h0000644000175000001440000000213515124701711026627 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstgui_linux_runloop_support.h // Created by : Steinberg, 10/2025 // Description : VSTGUI Linux Runloop Support // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" //----------------------------------------------------------------------------- namespace Steinberg::Linux { bool setupVSTGUIRunloop (FUnknown* hostContext); //----------------------------------------------------------------------------- } // Steinberg::Linux qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstcomponentbase.h0000644000000000000000000000013215124701711024116 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstcomponentbase.h0000644000175000001440000000651015124701711024110 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstcomponentbase.h // Created by : Steinberg, 05/2005 // Description : Base class for Component and Edit Controller // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ipluginbase.h" #include "pluginterfaces/vst/ivstmessage.h" #include "pluginterfaces/vst/ivsthostapplication.h" #include "base/source/fobject.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Base class for VST 3 Component and Edit Controller. \ingroup vstClasses */ class ComponentBase: public FObject, public IPluginBase, public IConnectionPoint { public: //------------------------------------------------------------------------ ComponentBase (); ~ComponentBase () override; //--- Internal Methods------ /** Returns the hostContext (set by the host during initialize call). */ FUnknown* getHostContext () const { return hostContext; } /** Returns the peer for the messaging communication (you can only use IConnectionPoint::notify * for communicate between peers, do not try to cast peerConnection. */ IConnectionPoint* getPeer () const { return peerConnection; } /** Allocates a message instance (do not forget to release it). */ IMessage* allocateMessage () const; /** Sends the given message to the peer. */ tresult sendMessage (IMessage* message) const; /** Sends a simple text message to the peer (max 255 characters). Text is interpreted as UTF-8. */ tresult sendTextMessage (const char8* text) const; /** Sends a message with a given ID without any other payload. */ tresult sendMessageID (const char8* messageID) const; /** Receives a simple text message from the peer (max 255 characters). Text is UTF-8 encoded. */ virtual tresult receiveText (const char8* text); //---from IPluginBase------ tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE; tresult PLUGIN_API terminate () SMTG_OVERRIDE; //---from IConnectionPoint----------- tresult PLUGIN_API connect (IConnectionPoint* other) SMTG_OVERRIDE; tresult PLUGIN_API disconnect (IConnectionPoint* other) SMTG_OVERRIDE; tresult PLUGIN_API notify (IMessage* message) SMTG_OVERRIDE; //---Interface------ OBJ_METHODS (ComponentBase, FObject) DEFINE_INTERFACES DEF_INTERFACE (IPluginBase) DEF_INTERFACE (IConnectionPoint) END_DEFINE_INTERFACES (FObject) REFCOUNT_METHODS (FObject) //------------------------------------------------------------------------ protected: IPtr hostContext; IPtr peerConnection; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstbus.cpp0000644000000000000000000000013215124701711022405 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstbus.cpp0000644000175000001440000000540215124701711022376 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstbus.cpp // Created by : Steinberg, 03/2008 // Description : VST Bus Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstbus.h" namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // Bus Implementation //------------------------------------------------------------------------ Bus::Bus (const TChar* _name, BusType _busType, int32 _flags) : name (_name), busType (_busType), flags (_flags), active (false) { } //------------------------------------------------------------------------ bool Bus::getInfo (BusInfo& info) { memset (info.name, 0, sizeof (String128)); name.copy (info.name, 128); info.busType = busType; info.flags = flags; return true; } //------------------------------------------------------------------------ // EventBus Implementation //------------------------------------------------------------------------ EventBus::EventBus (const TChar* name, BusType busType, int32 flags, int32 channelCount) : Bus (name, busType, flags), channelCount (channelCount) { } //------------------------------------------------------------------------ bool EventBus::getInfo (BusInfo& info) { info.channelCount = channelCount; return Bus::getInfo (info); } //------------------------------------------------------------------------ // AudioBus Implementation //------------------------------------------------------------------------ AudioBus::AudioBus (const TChar* name, BusType busType, int32 flags, SpeakerArrangement arr) : Bus (name, busType, flags), speakerArr (arr) { } //------------------------------------------------------------------------ bool AudioBus::getInfo (BusInfo& info) { info.channelCount = SpeakerArr::getChannelCount (speakerArr); return Bus::getInfo (info); } //------------------------------------------------------------------------ // BusList Implementation //------------------------------------------------------------------------ BusList::BusList (MediaType type, BusDirection dir) : type (type), direction (dir) { } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/auwrapper0000644000000000000000000000013215124701711022304 xustar0030 mtime=1767080905.280506322 30 atime=1767080905.279210874 30 ctime=1767080905.280506322 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/0000755000175000001440000000000015124701711022351 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/ausdk.mm0000644000000000000000000000013215124701711024023 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/ausdk.mm0000644000175000001440000000504015124701711024012 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/ausdk.mm // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma clang diagnostic ignored "-Wdeprecated-declarations" #pragma clang diagnostic ignored "-Wunused-value" #pragma clang diagnostic ignored "-Wparentheses" #pragma clang diagnostic ignored "-Woverloaded-virtual" #ifndef MAC_OS_X_VERSION_10_7 #define MAC_OS_X_VERSION_10_7 1070 #endif #import "PublicUtility/CAAudioChannelLayout.cpp" #import "PublicUtility/CABundleLocker.cpp" #import "PublicUtility/CAHostTimeBase.cpp" #import "PublicUtility/CAStreamBasicDescription.cpp" #import "PublicUtility/CAVectorUnit.cpp" #import "PublicUtility/CAAUParameter.cpp" #import "AUPublic/AUBase/ComponentBase.cpp" #import "AUPublic/AUBase/AUScopeElement.cpp" #import "AUPublic/AUBase/AUOutputElement.cpp" #import "AUPublic/AUBase/AUInputElement.cpp" #import "AUPublic/AUBase/AUBase.cpp" #if !__LP64__ #ifndef verify_noerr #define verify_noerr(x) x #endif #ifndef verify #define verify(x) #endif #import "AUPublic/AUCarbonViewBase/AUCarbonViewBase.cpp" #import "AUPublic/AUCarbonViewBase/AUCarbonViewControl.cpp" #import "AUPublic/AUCarbonViewBase/AUCarbonViewDispatch.cpp" #import "AUPublic/AUCarbonViewBase/AUControlGroup.cpp" #import "AUPublic/AUCarbonViewBase/CarbonEventHandler.cpp" #endif #import "AUPublic/Utility/AUTimestampGenerator.cpp" #import "AUPublic/Utility/AUBuffer.cpp" #import "AUPublic/Utility/AUBaseHelper.cpp" #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 #import "AUPublic/OtherBases/AUMIDIEffectBase.cpp" #import "AUPublic/Utility/AUDebugDispatcher.cpp" #else #import "AUPublic/AUBase/AUPlugInDispatch.cpp" #endif #if !CA_USE_AUDIO_PLUGIN_ONLY #import "AUPublic/AUBase/AUDispatch.cpp" #import "AUPublic/OtherBases/MusicDeviceBase.cpp" #import "AUPublic/OtherBases/AUMIDIBase.cpp" #import "AUPublic/OtherBases/AUEffectBase.cpp" #endif /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/auresource.r0000644000000000000000000000013215124701711024721 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/auresource.r0000644000175000001440000000513715124701711024717 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/auresource.r // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include #include #include "audiounitconfig.h" /* ---------------------------------------------------------------------------------------------------------------------------------------- // audiounitconfig.h needs the following definitions: #define kAudioUnitVersion 0xFFFFFFFF // Version Number, needs to be in hex #define kAudioUnitName "Steinberg: MyVST3 as AudioUnit" // Company Name + Effect Name #define kAudioUnitDescription "My VST3 as AudioUnit" // Effect Description #define kAudioUnitType kAudioUnitType_Effect // can be kAudioUnitType_Effect or kAudioUnitType_MusicDevice #define kAudioUnitComponentSubType 'test' // unique id #define kAudioUnitComponentManuf 'SMTG' // registered company id #define kAudioUnitCarbonView 1 // if 0 no Carbon view support will be added */ #define kAudioUnitResID_Processor 1000 #define kAudioUnitResID_CarbonView 9000 //----------------------Processor---------------------------------------------- #define RES_ID kAudioUnitResID_Processor #define COMP_TYPE kAudioUnitType #define COMP_SUBTYPE kAudioUnitComponentSubType #define COMP_MANUF kAudioUnitComponentManuf #define VERSION kAudioUnitVersion #define NAME kAudioUnitName #define DESCRIPTION kAudioUnitDescription #define ENTRY_POINT "AUWrapperEntry" #include "AUResources.r" #if kAudioUnitCarbonView //----------------------View---------------------------------------------- #define RES_ID kAudioUnitResID_CarbonView #define COMP_TYPE kAudioUnitCarbonViewComponentType #define COMP_SUBTYPE kAudioUnitComponentSubType #define COMP_MANUF kAudioUnitComponentManuf #define VERSION kAudioUnitVersion #define NAME "CarbonView" #define DESCRIPTION "CarbonView" #define ENTRY_POINT "AUCarbonViewEntry" #include "AUResources.r" #endif qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/auwrapper.mm0000644000000000000000000000013215124701711024722 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/auwrapper.mm0000644000175000001440000027301315124701711024720 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/auwrapper.mm // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma GCC diagnostic ignored "-Wdeprecated-declarations" /* Things to do : - Parameter Mapping could be better (indexed stuff), but needs work if we want to have this - Speaker Arrangement -> Channel Layout (partially done, just doesn't work always in Logic 8) - dynamic Bus management */ #include "auwrapper.h" #include "NSDataIBStream.h" #include "aucocoaview.h" #include "public.sdk/source/vst/hosting/eventlist.h" #include "public.sdk/source/vst/hosting/hostclasses.h" #include "public.sdk/source/vst/hosting/parameterchanges.h" #include "public.sdk/source/vst/hosting/processdata.h" #include "public.sdk/source/vst/utility/ump.h" #include "public.sdk/source/vst/vsteditcontroller.h" #include "base/source/fdynlib.h" #include "base/source/fstring.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/base/ustring.h" #include "pluginterfaces/gui/iplugview.h" #include "pluginterfaces/vst/ivstmidicontrollers.h" #include "pluginterfaces/vst/vstpresetkeys.h" #include "pluginterfaces/vst/vsttypes.h" #include #if !SMTG_PLATFORM_64 #include #endif #include #include #include #if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK) #include "CAXException.h" #endif #define SMTG_MAKE_STRING_PRIVATE_DONT_USE(x) #x #define SMTG_MAKE_STRING(x) SMTG_MAKE_STRING_PRIVATE_DONT_USE (x) typedef bool (*bundleEntryPtr) (CFBundleRef); typedef bool (*bundleExitPtr) (void); #include // we ignore for the moment that the NSAddImage functions are deprecated #pragma GCC diagnostic ignored "-Wdeprecated-declarations" static bool CopyProcessPath (Steinberg::String& name) { Dl_info info; if (dladdr ((const void*)CopyProcessPath, &info)) { if (info.dli_fname) { name.assign (info.dli_fname); #ifdef UNICODE name.toWideString (); #endif return true; } } return false; } namespace Steinberg { //------------------------------------------------------------------------ class VST3DynLibrary : public FDynLibrary { public: VST3DynLibrary () : bundleEntryCalled (false) { gInstance = this; } ~VST3DynLibrary () { if (isLoaded ()) { if (bundleEntryCalled) { if (bundleExitPtr bundleExit = (bundleExitPtr)getProcAddress ("bundleExit")) bundleExit (); } #if defined(MAC_OS_X_VERSION_10_11) // workaround, because CFBundleCreate returns refcount == 2. if (CFBundleIsExecutableLoaded ((CFBundleRef)instance)) { CFBundleUnloadExecutable ((CFBundleRef)instance); CFRelease ((CFBundleRef)instance); } #else CFRelease ((CFBundleRef)instance); #endif instance = nullptr; isloaded = false; } gInstance = nullptr; } bool init (const tchar* path) { if (isLoaded ()) return true; isBundle = false; Steinberg::String name (path); if (name.getChar16 (0) != STR ('/')) // no absoltue path { Steinberg::String p; if (CopyProcessPath (p)) { Steinberg::int32 index = p.findLast (STR ('/')); p.remove (index + 1); name = p + name; } } CFStringRef fsString = (CFStringRef)name.toCFStringRef (); CFURLRef url = CFURLCreateWithFileSystemPath (NULL, fsString, kCFURLPOSIXPathStyle, true); if (url) { CFBundleRef bundle = CFBundleCreate (NULL, url); if (bundle) { bundleEntryPtr bundleEntry = (bundleEntryPtr)CFBundleGetFunctionPointerForName ( bundle, CFSTR ("bundleEntry")); if (bundleEntry) bundleEntryCalled = bundleEntry (bundle); if (bundleEntryCalled) { isBundle = true; isloaded = true; instance = (void*)bundle; } else CFRelease (bundle); } CFRelease (url); } CFRelease (fsString); return isLoaded (); } static VST3DynLibrary* gInstance; protected: bool bundleEntryCalled; }; VST3DynLibrary* VST3DynLibrary::gInstance = nullptr; namespace Vst { //------------------------------------------------------------------------ struct AUWrapper::MIDIOutputCallbackHelper { MIDIOutputCallbackHelper (); ~MIDIOutputCallbackHelper (); void setCallbackInfo (AUMIDIOutputCallback callback, void* userData); void addEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame); void fireAtTimeStamp (const AudioTimeStamp& inTimeStamp); private: struct MIDIMessageInfoStruct { UInt8 status; UInt8 channel; UInt8 data1; UInt8 data2; UInt32 startFrame; }; using MIDIMessageList = std::vector ; MIDIPacketList* PacketList () { return (MIDIPacketList*)mBuffersAllocated.data (); } std::array mBuffersAllocated; AUMIDIOutputCallbackStruct mMIDICallbackStruct; MIDIMessageList mMIDIMessageList; }; #ifdef SMTG_AUWRAPPER_USES_AUSDK using namespace ausdk; #endif //-------------------------------------------------------------------------------------------- class SpeakerArrangementBase { public: SpeakerArrangementBase () { channelLayout.mChannelBitmap = 0; channelLayout.mChannelLayoutTag = 0; channelLayout.mNumberChannelDescriptions = 0; } OSStatus setNumChannels (int32 numChannels) { switch (numChannels) { //--- ----------------------- case 1: channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono; break; //--- ----------------------- case 2: channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; break; //--- ----------------------- case 6: channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1; break; //--- ----------------------- default: return kAudioUnitErr_InvalidProperty; } return noErr; } protected: AudioChannelLayout channelLayout; }; //-------------------------------------------------------------------------------------------- class MultiChannelOutputElement : public AUOutputElement, public SpeakerArrangementBase { public: MultiChannelOutputElement (AUBase* audioUnit) : AUOutputElement (audioUnit) {} UInt32 GetChannelLayoutTags (AudioChannelLayoutTag* outLayoutTagsPtr) { if (AudioChannelLayoutTag_GetNumberOfChannels (channelLayout.mChannelLayoutTag) <= 2) return 0; if (outLayoutTagsPtr) *outLayoutTagsPtr = channelLayout.mChannelLayoutTag; return 1; } UInt32 GetAudioChannelLayout (AudioChannelLayout* outMapPtr, Boolean& outWritable) { if (AudioChannelLayoutTag_GetNumberOfChannels (channelLayout.mChannelLayoutTag) <= 2) return 0; if (outMapPtr) *outMapPtr = channelLayout; outWritable = false; return sizeof (AudioChannelLayout); } OSStatus SetAudioChannelLayout (const AudioChannelLayout& layout) { return kAudioUnitErr_InvalidProperty; } OSStatus SetStreamFormat (const AudioStreamBasicDescription& desc) { OSStatus err = setNumChannels (desc.mChannelsPerFrame); if (err != noErr) return err; return AUOutputElement::SetStreamFormat (desc); } }; //-------------------------------------------------------------------------------------------- class MultiChannelInputElement : public AUInputElement, public SpeakerArrangementBase { public: MultiChannelInputElement (AUBase* audioUnit) : AUInputElement (audioUnit) {} UInt32 GetChannelLayoutTags (AudioChannelLayoutTag* outLayoutTagsPtr) { if (AudioChannelLayoutTag_GetNumberOfChannels (channelLayout.mChannelLayoutTag) <= 2) return 0; if (outLayoutTagsPtr) *outLayoutTagsPtr = channelLayout.mChannelLayoutTag; return 1; } UInt32 GetAudioChannelLayout (AudioChannelLayout* outMapPtr, Boolean& outWritable) { if (AudioChannelLayoutTag_GetNumberOfChannels (channelLayout.mChannelLayoutTag) <= 2) return 0; if (outMapPtr) memcpy (outMapPtr, &channelLayout, sizeof (AudioChannelLayout)); outWritable = false; return sizeof (AudioChannelLayout); } OSStatus SetAudioChannelLayout (const AudioChannelLayout& layout) { return kAudioUnitErr_InvalidProperty; } OSStatus SetStreamFormat (const AudioStreamBasicDescription& desc) { OSStatus err = setNumChannels (desc.mChannelsPerFrame); if (err != noErr) return err; return AUInputElement::SetStreamFormat (desc); } }; //------------------------------------------------------------------------ static CFStringRef createCFStringFromString128 (const String128& string) { UString128 str (string); return CFStringCreateWithCharacters (nullptr, (const UniChar*)string, str.getLength ()); } //------------------------------------------------------------------------ static void createString128FromCFString (CFStringRef inString, String128& outString) { CFStringGetCharacters (inString, CFRangeMake (0, std::max (128, CFStringGetLength (inString))), (UniChar*)outString); } //------------------------------------------------------------------------ static CFBundleRef GetBundleFromExecutable (const char* filepath) { AutoreleasePool ap; NSString* execStr = [NSString stringWithCString:filepath encoding:NSUTF8StringEncoding]; NSString* macOSStr = [execStr stringByDeletingLastPathComponent]; NSString* contentsStr = [macOSStr stringByDeletingLastPathComponent]; NSString* bundleStr = [contentsStr stringByDeletingLastPathComponent]; return CFBundleCreate (nullptr, (CFURLRef)[NSURL fileURLWithPath:bundleStr isDirectory:YES]); } //------------------------------------------------------------------------ static CFBundleRef GetCurrentBundle () { Dl_info info; if (dladdr ((const void*)GetCurrentBundle, &info)) { if (info.dli_fname) { return GetBundleFromExecutable (info.dli_fname); } } return nullptr; } //------------------------------------------------------------------------ CFBundleRef AUWrapper::gBundleRef = nullptr; static CFIndex gBundleRefCount = 0; static AUChannelInfo* channelInfos = nullptr; //------------------------------------------------------------------------ static void initBundleRef () { if (AUWrapper::gBundleRef == nullptr) { AUWrapper::gBundleRef = GetCurrentBundle (); gBundleRefCount = CFGetRetainCount (AUWrapper::gBundleRef); } else CFRetain (AUWrapper::gBundleRef); } //------------------------------------------------------------------------ static void releaseBundleRef () { CFIndex currentCount = CFGetRetainCount (AUWrapper::gBundleRef); CFRelease (AUWrapper::gBundleRef); if (currentCount == gBundleRefCount) AUWrapper::gBundleRef = nullptr; } //------------------------------------------------------------------------ class AUHostApplication : public HostApplication, public IVst3ToAUWrapper { public: virtual tresult PLUGIN_API getName (String128 name) SMTG_OVERRIDE { String str ("VST3-AU Wrapper"); str.copyTo (name, 0, 127); return kResultTrue; } DEFINE_INTERFACES DEF_INTERFACE (Vst::IVst3ToAUWrapper) END_DEFINE_INTERFACES (HostApplication) REFCOUNT_METHODS (HostApplication) }; static AUHostApplication gHostApp; //------------------------------------------------------------------------ IMPLEMENT_FUNKNOWN_METHODS (AUWrapper, IComponentHandler, IComponentHandler::iid) //------------------------------------------------------------------------ AUWrapper::AUWrapper (ComponentInstanceRecord* ci) #ifdef SMTG_AUWRAPPER_USES_AUSDK : AUWRAPPER_BASE_CLASS (ci, 128, 128) #else : AUWRAPPER_BASE_CLASS (ci, 0, 0) #endif , audioProcessor (nullptr) , editController (nullptr) , timer (nullptr) , noteCounter (0) , sampleRate (44100) , bypassParamID (-1) , presets (nullptr) , numPresets (0) , factoryProgramChangedID (-1) , isInstrument (false) , isBypassed (false) , paramListenerRef (nullptr) , midiOutCount (0) , isOfflineRender (false) { FUNKNOWN_CTOR AutoreleasePool ap; initBundleRef (); for (int32 i = 0; i < kMaxProgramChangeParameters; i++) programChangeInfos[i] = {}; processData.processContext = &processContext; processData.inputParameterChanges = &processParamChanges; processData.outputParameterChanges = &outputParamChanges; eventList.setMaxSize (128); outputEvents.setMaxSize (128); processData.inputEvents = &eventList; processData.outputEvents = &outputEvents; loadVST3Module (); auto factory = U::cast (owned (getFactory ())); if (factory) { // find first audio processor class for (int32 i = 0; i < factory->countClasses (); i++) { PClassInfo2 ci; if (factory->getClassInfo2 (i, &ci) == kResultTrue) { if (strcmp (ci.category, kVstAudioEffectClass) == 0) { if (factory->createInstance (ci.cid, IAudioProcessor::iid, (void**)&audioProcessor) == kResultTrue) { ConstString plugCategory (ci.subCategories); if (plugCategory.findFirst ("Instrument", -1, ConstString::kCaseInsensitive) >= 0) isInstrument = true; break; } } } } } if (audioProcessor) { if (FUnknownPtr (audioProcessor)->initialize ((HostApplication*)&gHostApp) != kResultTrue) return; auto component = U::cast (audioProcessor); if (audioProcessor->queryInterface (IEditController::iid, (void**)&editController) != kResultTrue) { if (component) { TUID ccid; if (component->getControllerClassId (ccid) == kResultTrue) { if (factory->createInstance (ccid, IEditController::iid, (void**)&editController) == kResultTrue) { if (editController->initialize ((HostApplication*)&gHostApp) != kResultTrue) { editController->release (); editController = nullptr; return; } auto controllerConnectionPoint = U::cast (editController); auto processorConnectionPoint = U::cast (audioProcessor); if (controllerConnectionPoint && processorConnectionPoint) { controllerConnectionPoint->connect (processorConnectionPoint); processorConnectionPoint->connect (controllerConnectionPoint); } NSMutableData* processorData = [[[NSMutableData alloc] init] autorelease]; NSMutableDataIBStream stream (processorData); if (FUnknownPtr (audioProcessor)->getState (&stream) == kResultTrue) { stream.seek (0, IBStream::kIBSeekSet); editController->setComponentState (&stream); } } } } } if (editController && component) { editController->setComponentHandler (this); // initialize busses int32 inputBusCount = component->getBusCount (kAudio, kInput); int32 outputBusCount = component->getBusCount (kAudio, kOutput); CreateElements (); Inputs ().SetNumberOfElements (inputBusCount); Outputs ().SetNumberOfElements (outputBusCount); SpeakerArrangement sa; for (int32 inputNo = 0; inputNo < inputBusCount; inputNo++) { MultiChannelInputElement* element = dynamic_cast (Inputs ().GetIOElement (inputNo)); if (element == nullptr) continue; if (audioProcessor->getBusArrangement (kInput, inputNo, sa) == kResultTrue) { AudioStreamBasicDescription streamDesc (element->GetStreamFormat ()); streamDesc.mChannelsPerFrame = SpeakerArr::getChannelCount (sa); element->SetStreamFormat (streamDesc); } BusInfo info = {}; if (component->getBusInfo (kAudio, kInput, inputNo, info) == kResultTrue) { String busName (info.name); CFStringRef busNameString = (CFStringRef)busName.toCFStringRef (); element->SetName (busNameString); CFRelease (busNameString); } #ifdef SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES if (info.flags & BusInfo::BusFlags::kDefaultActive) #endif { component->activateBus (kAudio, kInput, inputNo, true); } } for (int32 outputNo = 0; outputNo < outputBusCount; outputNo++) { MultiChannelOutputElement* element = dynamic_cast (Outputs ().GetIOElement (outputNo)); if (element == nullptr) continue; if (audioProcessor->getBusArrangement (kOutput, outputNo, sa) == kResultTrue) { AudioStreamBasicDescription streamDesc (element->GetStreamFormat ()); streamDesc.mChannelsPerFrame = SpeakerArr::getChannelCount (sa); element->SetStreamFormat (streamDesc); } BusInfo info = {}; if (component->getBusInfo (kAudio, kOutput, outputNo, info) == kResultTrue) { String busName (info.name); CFStringRef busNameString = (CFStringRef)busName.toCFStringRef (); element->SetName (busNameString); CFRelease (busNameString); } #ifdef SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES if (info.flags & BusInfo::BusFlags::kDefaultActive) #endif { component->activateBus (kAudio, kOutput, outputNo, true); } } processData.prepare (*component, 0, kSample32); // initialize parameters syncParameterValues (); cacheParameterValues (); midiOutCount = component->getBusCount (kEvent, kOutput); if (midiOutCount > 0) mCallbackHelper = std::make_unique (); transferParamChanges.setMaxParameters (500); outputParamTransfer.setMaxParameters (500); timer = Timer::create (this, 20); auto editController2 = U::cast (editController); if (editController2) editController2->setKnobMode (kLinearMode); midiLearn = U::cast (editController); if (midiLearn) midiLearnRingBuffer.resize (8); } } } //------------------------------------------------------------------------ AUWrapper::~AUWrapper () { AutoreleasePool ap; if (timer) timer->release (); if (paramListenerRef) AUListenerDispose (paramListenerRef); if (audioProcessor) { auto combined = U::cast (audioProcessor); if (!combined) { auto controllerConnectionPoint = U::cast (editController); auto processorConnectionPoint = U::cast (audioProcessor); if (controllerConnectionPoint && processorConnectionPoint) { controllerConnectionPoint->disconnect (processorConnectionPoint); processorConnectionPoint->disconnect (controllerConnectionPoint); } } } midiLearn.reset (); if (editController) { editController->setComponentHandler (nullptr); uint32 refCount = editController->addRef (); if (refCount == 2) editController->terminate (); editController->release (); editController->release (); editController = nullptr; } clearParameterValueCache (); if (audioProcessor) { FUnknownPtr (audioProcessor)->terminate (); audioProcessor->release (); audioProcessor = nullptr; } unloadVST3Module (); releaseBundleRef (); if (presets) delete presets; FUNKNOWN_DTOR } //------------------------------------------------------------------------ void AUWrapper::loadVST3Module () { if (VST3DynLibrary::gInstance == nullptr) { if (gBundleRef) { String pluginPath; CFStringRef pluginNameStr = (CFStringRef)CFBundleGetValueForInfoDictionaryKey ( gBundleRef, CFSTR ("VST3 Plug-in")); if (!pluginNameStr) { NSURL* pluginUrl = (NSURL*)CFBundleCopyResourceURL (gBundleRef, CFSTR ("plugin"), CFSTR ("vst3"), NULL); if (!pluginUrl) { NSLog ( @"VST3 Plug-in not defined in Info Dictionary and not included in bundle"); return; } pluginPath.fromCFStringRef ((CFStringRef)[pluginUrl path]); [pluginUrl release]; } else { pluginPath.fromCFStringRef (pluginNameStr); if (!pluginPath.getChar (0) != STR ('/')) { FSRef fsRef; if (FSFindFolder (kLocalDomain, kAudioPlugInsFolderType, false, &fsRef) == noErr) { NSURL* url = (NSURL*)CFURLCreateFromFSRef (nullptr, &fsRef); if (url) { String basePath ([[url path] UTF8String]); basePath.toWideString (kCP_Utf8); pluginPath.insertAt (0, STR ("/VST3/")); pluginPath.insertAt (0, basePath); [url release]; } } } } dynLib = owned (new VST3DynLibrary ()); if (pluginPath.isEmpty () || !dynLib->init (pluginPath)) { dynLib.reset (); return; } } } else { dynLib = VST3DynLibrary::gInstance; } } //------------------------------------------------------------------------ void AUWrapper::unloadVST3Module () { dynLib.reset (); } //------------------------------------------------------------------------ IPluginFactory* AUWrapper::getFactory () { if (VST3DynLibrary::gInstance) { GetFactoryProc getPlugFactory = (GetFactoryProc)VST3DynLibrary::gInstance->getProcAddress ("GetPluginFactory"); if (getPlugFactory) return getPlugFactory (); } return nullptr; } //------------------------------------------------------------------------ // MARK: ComponentBase #if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK) ComponentResult AUWrapper::Version () { AutoreleasePool ap; NSString* versionString = (NSString*)CFBundleGetValueForInfoDictionaryKey (gBundleRef, CFSTR ("AudioUnit Version")); if (versionString) { int version = 0; if (sscanf ([versionString UTF8String], "%x", &version) == 1) return version; } return 0xFFFFFFFF; } #endif //------------------------------------------------------------------------ void AUWrapper::PostConstructor () { AUWRAPPER_BASE_CLASS::PostConstructor (); } //------------------------------------------------------------------------ static SpeakerArrangement numChannelsToSpeakerArrangement (UInt32 numChannels) { switch (numChannels) { //--- ----------------------- case 1: return SpeakerArr::kMono; //--- ----------------------- case 2: return SpeakerArr::kStereo; //--- ----------------------- case 6: return SpeakerArr::k51; } return 0; } //------------------------------------------------------------------------ ComponentResult AUWrapper::Initialize () { if (audioProcessor && editController) { // match speaker arrangement with AU stream format auto component = U::cast (audioProcessor); int32 inputBusCount = component->getBusCount (kAudio, kInput); int32 outputBusCount = component->getBusCount (kAudio, kOutput); SupportedNumChannels (0); // initialize channelInfos if (channelInfos) { int32 maxBusCount = std::max (inputBusCount, outputBusCount); for (int32 i = 0; i < maxBusCount; i++) { int32 inChannelCount = 0; int32 outChannelCount = 0; if (inputBusCount > i) inChannelCount = Inputs ().GetIOElement (i)->GetStreamFormat ().mChannelsPerFrame; if (outputBusCount > i) outChannelCount = Outputs ().GetIOElement (i)->GetStreamFormat ().mChannelsPerFrame; if (!validateChannelPair (inChannelCount, outChannelCount, channelInfos, SupportedNumChannels (0))) return kAudioUnitErr_FailedInitialization; } } SpeakerArrangement inputs[inputBusCount]; SpeakerArrangement outputs[outputBusCount]; for (int32 element = 0; element < inputBusCount; element++) { const AudioStreamBasicDescription desc = Inputs ().GetIOElement (element)->GetStreamFormat (); inputs[element] = numChannelsToSpeakerArrangement (desc.mChannelsPerFrame); } for (int32 element = 0; element < outputBusCount; element++) { const AudioStreamBasicDescription desc = Outputs ().GetIOElement (element)->GetStreamFormat (); outputs[element] = numChannelsToSpeakerArrangement (desc.mChannelsPerFrame); } if (audioProcessor->setBusArrangements (inputs, inputBusCount, outputs, outputBusCount) != kResultTrue) { return kAudioUnitErr_FailedInitialization; } // After set Bus Arrangement, the channelbuffers may need to be reallocated -> hence the // second prepare! processData.prepare (*component, 0, kSample32); ProcessSetup ps; ps.sampleRate = getSampleRate (); ps.maxSamplesPerBlock = GetMaxFramesPerSlice (); ps.symbolicSampleSize = kSample32; ps.processMode = kRealtime; audioProcessor->setupProcessing (ps); updateMidiMappingCache (); component->setActive (true); audioProcessor->setProcessing (true); if (paramListenerRef == nullptr) { static constexpr auto interval = 1. / 60.; // 60 times a second OSStatus status = AUListenerCreateWithDispatchQueue ( ¶mListenerRef, interval, dispatch_get_main_queue (), ^(void* _Nullable inObject, const AudioUnitParameter* _Nonnull inParameter, AudioUnitParameterValue inValue) { setControllerParameter (inParameter->mParameterID, inValue); }); SMTG_ASSERT (status == noErr) if (paramListenerRef) { AudioUnitParameter sPar; sPar.mAudioUnit = GetComponentInstance (); sPar.mParameterID = 0; sPar.mElement = 0; sPar.mScope = kAudioUnitScope_Global; programParameters.clear (); // for each VST3 parameter for (int32 i = 0; i < editController->getParameterCount (); i++) { ParameterInfo pi = {}; editController->getParameterInfo (i, pi); // do not register bypass if ((pi.flags & ParameterInfo::kIsBypass) != 0) { continue; } sPar.mParameterID = pi.id; status = AUListenerAddParameter (paramListenerRef, nullptr, &sPar); SMTG_ASSERT (status == noErr) if ((pi.flags & ParameterInfo::kIsProgramChange) != 0) { programParameters.push_back (pi); } } updateProgramChangesCache (); IUnitInfo* unitInfoController = NULL; // let's see if there's a preset list (take the first one) if (editController->queryInterface (IUnitInfo::iid, (void**)&unitInfoController) == kResultTrue && unitInfoController) { ProgramListInfo programListInfo; if (unitInfoController->getProgramListInfo (0, programListInfo) == kResultTrue) { factoryProgramChangedID = -1; numPresets = programListInfo.programCount; if (programListInfo.programCount > 0) { UnitID unitId = -1; // find the unit supporting this ProgramList IUnitInfo* unitInfo = NULL; if (editController->queryInterface (IUnitInfo::iid, (void**)&unitInfo) == kResultTrue && unitInfo) { int32 unitCount = unitInfo->getUnitCount (); for (int32 i = 0; i < unitCount; i++) { UnitInfo unitInfoStruct = {}; if (unitInfo->getUnitInfo (i, unitInfoStruct) == kResultTrue) { if (programListInfo.id == unitInfoStruct.programListId) { unitId = unitInfoStruct.id; break; } } } unitInfo->release (); } if (unitId != -1) { // find the associated ProgramChange parameter ID for (int32 i = 0; i < (int32)programParameters.size (); i++) { const ParameterInfo& paramInfo = programParameters.at (i); if (paramInfo.unitId == unitId) { factoryProgramChangedID = paramInfo.id; break; } } if (factoryProgramChangedID != -1) { presets = new AUPreset[programListInfo.programCount]; for (int32 i = 0; i < programListInfo.programCount; i++) { String128 name; unitInfoController->getProgramName (programListInfo.id, i, name); presets[i].presetNumber = i; presets[i].presetName = createCFStringFromString128 (name); } } } } } unitInfoController->release (); } } } return AUWRAPPER_BASE_CLASS::Initialize (); } return -1; } //------------------------------------------------------------------------ void AUWrapper::Cleanup () { if (audioProcessor) { audioProcessor->setProcessing (false); auto component = U::cast (audioProcessor); component->setActive (false); } } //------------------------------------------------------------------------ void AUWrapper::updateProgramChangesCache () { // assign programChanges auto pci = std::make_unique (); for (size_t midiChannel = 0u; midiChannel < pci->size (); ++midiChannel) { auto& programChangeInfo = pci->at (midiChannel); programChangeInfo = {}; UnitID unitId; ProgramListID programListId; if (getProgramListAndUnit (static_cast (midiChannel), unitId, programListId)) { for (int32 i = 0; i < (int32)programParameters.size (); i++) { const ParameterInfo& paramInfo = programParameters.at (i); if (paramInfo.unitId == unitId) { programChangeInfo.pid = paramInfo.id; ParameterInfo paramInfo = {}; if (editController->getParameterInfo (paramInfo.id, paramInfo) == kResultTrue) { programChangeInfo.numPrograms = paramInfo.stepCount + 1; } break; } } } } programChangeInfoTransfer.transferObject_ui (std::move (pci)); } //------------------------------------------------------------------------ void AUWrapper::updateMidiMappingCache () { if (!editController || !audioProcessor) return; auto midiMapping = U::cast (editController); if (!midiMapping) return; auto comp = U::cast (audioProcessor); if (!comp) return; auto numInputs = comp->getBusCount (MediaTypes::kEvent, BusDirections::kInput); if (numInputs <= 0) return; MidiMapping cache; cache.busList.resize (numInputs); for (auto busIndex = 0; busIndex < numInputs; ++busIndex) { BusInfo busInfo {}; if (comp->getBusInfo (MediaTypes::kEvent, BusDirections::kInput, 0, busInfo) != kResultTrue) continue; if (busInfo.channelCount <= 0) continue; cache.busList[busIndex].resize (busInfo.channelCount); for (auto channelIndex = 0; channelIndex < busInfo.channelCount; ++channelIndex) { for (auto cc = 0; cc < ControllerNumbers::kCountCtrlNumber; ++cc) { ParamID pid; if (midiMapping->getMidiControllerAssignment (busIndex, channelIndex, cc, pid) == kResultTrue) { cache.busList[busIndex][channelIndex][cc] = pid; } } } } if (!cache.empty ()) { midiMappingTransfer.transferObject_ui (std::make_unique (std::move (cache))); } } //------------------------------------------------------------------------ // MARK: AUBase //-------------------------------------------------------------------------------------------- #ifdef SMTG_AUWRAPPER_USES_AUSDK std::unique_ptr AUWrapper::CreateElement (AudioUnitScope scope, AudioUnitElement element) { switch (scope) { //--- ----------------------- case kAudioUnitScope_Output: return std::make_unique(this); //--- ----------------------- case kAudioUnitScope_Input: return std::make_unique(this); } return AUWRAPPER_BASE_CLASS::CreateElement (scope, element); } #else AUElement* AUWrapper::CreateElement (AudioUnitScope scope, AudioUnitElement element) { switch (scope) { //--- ----------------------- case kAudioUnitScope_Output: return new MultiChannelOutputElement (this); //--- ----------------------- case kAudioUnitScope_Input: return new MultiChannelInputElement (this); } return AUWRAPPER_BASE_CLASS::CreateElement (scope, element); } #endif // SMTG_AUWRAPPER_USES_AUSDK //------------------------------------------------------------------------ UInt32 AUWrapper::SupportedNumChannels (const AUChannelInfo** outInfo) { /* This code expects that the Info.plist contains an array with key AudioUnit SupportedNumChannels that contains the supported AUChannelInfo informations like: Outputs 2 Inputs 2 Outputs 0 Inputs 1 Outputs 1 Inputs 1 */ static unsigned long numChannelInfos = 0; static bool once = true; if (once && gBundleRef) { once = false; char buffer[128]; CFStringRef processName = nullptr; ProcessSerialNumber psn; SMTG_VERIFY_IS (GetCurrentProcess (&psn), noErr); SMTG_VERIFY_IS (CopyProcessName (&psn, &processName), noErr); CFStringGetCString (processName, buffer, sizeof (buffer), kCFStringEncodingUTF8); CFRelease (processName); strncat (buffer, " SupportedNumChannels", sizeof (buffer) - strlen (buffer) - 1); CFStringRef dictName = CFStringCreateWithCString (kCFAllocatorDefault, buffer, kCFStringEncodingUTF8); NSArray* supportedNumChannelsArray = (NSArray*)CFBundleGetValueForInfoDictionaryKey (gBundleRef, dictName); if (!supportedNumChannelsArray) supportedNumChannelsArray = (NSArray*)CFBundleGetValueForInfoDictionaryKey ( gBundleRef, CFSTR ("AudioUnit SupportedNumChannels")); if (supportedNumChannelsArray) { numChannelInfos = [supportedNumChannelsArray count]; if (numChannelInfos > 0) { channelInfos = new AUChannelInfo[numChannelInfos]; int i = 0; for (NSDictionary* dict in supportedNumChannelsArray) { NSInteger inputs = [[dict objectForKey:@"Inputs"] integerValue]; NSInteger outputs = [[dict objectForKey:@"Outputs"] integerValue]; channelInfos[i].inChannels = inputs; channelInfos[i++].outChannels = outputs; } } } CFRelease (dictName); } if (outInfo) *outInfo = channelInfos; return static_cast (numChannelInfos); } //------------------------------------------------------------------------ bool AUWrapper::StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) { return IsInitialized () ? false : true; } //------------------------------------------------------------------------ ComponentResult AUWrapper::ChangeStreamFormat (AudioUnitScope inScope, AudioUnitElement inElement, const AudioStreamBasicDescription& inPrevFormat, const AudioStreamBasicDescription& inNewFormat) { if (inPrevFormat.mChannelsPerFrame == inNewFormat.mChannelsPerFrame) { // sample rate change ComponentResult res = AUWRAPPER_BASE_CLASS::ChangeStreamFormat (inScope, inElement, inPrevFormat, inNewFormat); if (res == noErr) sampleRate = inNewFormat.mSampleRate; return res; } else if (inPrevFormat.mSampleRate != inNewFormat.mSampleRate) sampleRate = inNewFormat.mSampleRate; else if (IsInitialized () || SupportedNumChannels (0) == 0) return kAudioUnitErr_Initialized; AUIOElement* element; switch (inScope) { //--- ----------------------- case kAudioUnitScope_Input: element = Inputs ().GetIOElement (inElement); break; //--- ----------------------- case kAudioUnitScope_Output: element = Outputs ().GetIOElement (inElement); break; //--- ----------------------- case kAudioUnitScope_Global: element = Outputs ().GetIOElement (0); break; //--- ----------------------- default: Throw (kAudioUnitErr_InvalidScope); } OSStatus err = element->SetStreamFormat (inNewFormat); if (err == noErr) PropertyChanged (kAudioUnitProperty_StreamFormat, inScope, inElement); return err; } //------------------------------------------------------------------------ ComponentResult AUWrapper::SetConnection (const AudioUnitConnection& inConnection) { ComponentResult result = AUWRAPPER_BASE_CLASS::SetConnection (inConnection); if (result == noErr) { int32 busIndex = inConnection.destInputNumber; bool active = GetInput (busIndex)->IsActive (); auto component = U::cast (audioProcessor); component->activateBus (kAudio, kInput, busIndex, active); } return result; } //------------------------------------------------------------------------ void AUWrapper::buildUnitInfos (IUnitInfo* unitInfoController, UnitInfoMap& units) const { units.clear (); if (unitInfoController) { int32 numUnits = unitInfoController->getUnitCount (); for (int32 i = 0; i < numUnits; i++) { UnitInfo ui; if (unitInfoController->getUnitInfo (i, ui) == kResultTrue) units[ui.id] = ui; } } } //------------------------------------------------------------------------ ComponentResult AUWrapper::GetParameterInfo (AudioUnitScope inScope, AudioUnitParameterID inParameterID, AudioUnitParameterInfo& outParameterInfo) { if (inScope != kAudioUnitScope_Global) return kAudioUnitErr_InvalidScope; Steinberg::Base::Thread::FGuard guard (parameterCacheChanging); CachedParameterInfoMap::const_iterator it = cachedParameterInfos.find (inParameterID); if (it == cachedParameterInfos.end ()) return kAudioUnitErr_InvalidParameter; memcpy (&outParameterInfo, &it->second, sizeof (AudioUnitParameterInfo)); if (outParameterInfo.cfNameString) CFRetain (outParameterInfo.cfNameString); if (outParameterInfo.unitName) CFRetain (outParameterInfo.unitName); return noErr; } //------------------------------------------------------------------------ ComponentResult AUWrapper::SetParameter (AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames) { if (inScope == kAudioUnitScope_Global) { if (inID == factoryProgramChangedID) { int presetIdx = (int)((float)inValue * (float)numPresets); if (presetIdx >= 0 && presetIdx < numPresets) { SetAFactoryPresetAsCurrent (presets[presetIdx]); PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0); PropertyChanged (kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global, 0); } } transferParamChanges.addChange (inID, inValue, inBufferOffsetInFrames); } return AUWRAPPER_BASE_CLASS::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames); } //------------------------------------------------------------------------ void AUWrapper::setControllerParameter (ParamID pid, ParamValue value) { if (editController && pid != factoryProgramChangedID) editController->setParamNormalized (pid, value); } //------------------------------------------------------------------------ ComponentResult AUWrapper::SaveState (CFPropertyListRef* outData) { ComponentResult result = AUWRAPPER_BASE_CLASS::SaveState (outData); if (result != noErr) return result; NSMutableDictionary* dict = (NSMutableDictionary*)*outData; AutoreleasePool ap; NSMutableData* processorData = [[[NSMutableData alloc] init] autorelease]; NSMutableData* controllerData = [[[NSMutableData alloc] init] autorelease]; if (audioProcessor) { NSMutableDataIBStream stream (processorData); if (FUnknownPtr (audioProcessor)->getState (&stream) != kResultTrue) { [processorData setLength:0]; } } if (editController) { NSMutableDataIBStream stream (controllerData); if (editController->getState (&stream) != kResultTrue) { [controllerData setLength:0]; } } [dict setValue:processorData forKey:@"Processor State"]; [dict setValue:controllerData forKey:@"Controller State"]; *outData = dict; return result; } //------------------------------------------------------------------------ ComponentResult AUWrapper::RestoreState (CFPropertyListRef inData) { return restoreState (inData, false); } //------------------------------------------------------------------------ ComponentResult AUWrapper::restoreState (CFPropertyListRef inData, bool fromProject) { AutoreleasePool ap; if (CFGetTypeID (inData) != CFDictionaryGetTypeID ()) return kAudioUnitErr_InvalidPropertyValue; AudioComponentDescription desc = GetComponentDescription (); CFDictionaryRef dict = static_cast (inData); if (CFDictionaryContainsKey ( dict, [NSString stringWithCString:kAUPresetPartKey encoding:NSASCIIStringEncoding])) return kAudioUnitErr_InvalidPropertyValue; #define kCurrentSavedStateVersion 0 CFNumberRef cfnum = reinterpret_cast (CFDictionaryGetValue ( dict, [NSString stringWithCString:kAUPresetVersionKey encoding:NSASCIIStringEncoding])); if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue; SInt32 value; CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value); if (value != kCurrentSavedStateVersion) return kAudioUnitErr_InvalidPropertyValue; cfnum = reinterpret_cast (CFDictionaryGetValue ( dict, [NSString stringWithCString:kAUPresetSubtypeKey encoding:NSASCIIStringEncoding])); if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue; CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value); if (UInt32 (value) != desc.componentSubType) return kAudioUnitErr_InvalidPropertyValue; cfnum = reinterpret_cast ( CFDictionaryGetValue (dict, [NSString stringWithCString:kAUPresetManufacturerKey encoding:NSASCIIStringEncoding])); if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue; CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value); if (UInt32 (value) != desc.componentManufacturer) return kAudioUnitErr_InvalidPropertyValue; ComponentResult result = noErr; if (result == noErr && audioProcessor && editController && CFGetTypeID (inData) == CFDictionaryGetTypeID ()) { // Note: the bypass state is not part of the AU state bool wasBypassed = false; if (bypassParamID != -1) { wasBypassed = editController->getParamNormalized (bypassParamID) >= 0.5 ? true : false; } NSDictionary* dict = (NSDictionary*)inData; NSData* processorData = [dict valueForKey:@"Processor State"]; auto processLen = [processorData length]; if (processLen == 0) return kAudioUnitErr_InvalidPropertyValue; if (processorData) { NSDataIBStream stream (processorData); if (fromProject) stream.getAttributes ()->setString (Vst::PresetAttributes::kStateType, String (Vst::StateType::kProject)); FUnknownPtr (audioProcessor)->setState (&stream); } NSData* controllerData = [dict valueForKey:@"Controller State"]; if (controllerData) { NSDataIBStream processorStream (processorData); editController->setComponentState (&processorStream); NSDataIBStream stream (controllerData); if (fromProject) stream.getAttributes ()->setString (Vst::PresetAttributes::kStateType, String (Vst::StateType::kProject)); editController->setState (&stream); syncParameterValues (); cacheParameterValues (); } if (bypassParamID != -1) { transferParamChanges.addChange (bypassParamID, wasBypassed ? 1 : 0, 0); editController->setParamNormalized (bypassParamID, wasBypassed ? 1 : 0); } } return result; } //------------------------------------------------------------------------ OSStatus AUWrapper::GetPresets (CFArrayRef* outData) const { if (presets && numPresets > 0) { if (outData != nullptr) { CFMutableArrayRef presetsArray = CFArrayCreateMutable (NULL, numPresets, NULL); for (int32 i = 0; i < numPresets; i++) { CFArrayAppendValue (presetsArray, &presets[i]); } *outData = (CFArrayRef)presetsArray; } return noErr; } return kAudioUnitErr_InvalidProperty; } //------------------------------------------------------------------------ OSStatus AUWrapper::NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) { if (numPresets > 0) { float normalizedValue = (float)inNewFactoryPreset.presetNumber / (float)numPresets; transferParamChanges.addChange (factoryProgramChangedID, normalizedValue, 0); editController->setParamNormalized (factoryProgramChangedID, normalizedValue); if (inNewFactoryPreset.presetNumber < numPresets) SetAFactoryPresetAsCurrent (presets[inNewFactoryPreset.presetNumber]); return noErr; } return kAudioUnitErr_InvalidProperty; } //------------------------------------------------------------------------ ComponentResult AUWrapper::Render (AudioUnitRenderActionFlags& ioActionFlags, const AudioTimeStamp& inTimeStamp, UInt32 inNumberFrames) { #if !AUSDK_MIDI2_AVAILABLE midiMappingTransfer.accessTransferObject_rt ( [&] (auto& obj) { midiMappingCache = std::move (obj); }); #endif updateProcessContext (); processContext.systemTime = inTimeStamp.mHostTime; processParamChanges.clearQueue (); transferParamChanges.transferChangesTo (processParamChanges); processData.numSamples = inNumberFrames; processData.processMode = isOfflineRender? kOffline : kRealtime; for (int32 i = 0; i < Inputs ().GetNumberOfElements (); i++) { AUInputElement* input = GetInput (i); if (input->IsActive ()) input->PullInput (ioActionFlags, inTimeStamp, i, inNumberFrames); processData.inputs[i].numChannels = input->GetStreamFormat ().mChannelsPerFrame; for (int32 channel = 0; channel < input->GetStreamFormat ().mChannelsPerFrame; channel++) { processData.inputs[i].channelBuffers32[channel] = input->IsActive () ? (Sample32*)input->GetBufferList ().mBuffers[channel].mData : nullptr; } } for (int32 i = 0; i < Outputs ().GetNumberOfElements (); i++) { AUOutputElement* output = GetOutput (i); output->PrepareBuffer (inNumberFrames); processData.outputs[i].numChannels = output->GetStreamFormat ().mChannelsPerFrame; for (int32 channel = 0; channel < output->GetStreamFormat ().mChannelsPerFrame; channel++) { processData.outputs[i].channelBuffers32[channel] = (Sample32*)output->GetBufferList ().mBuffers[channel].mData; } } audioProcessor->process (processData); outputParamTransfer.transferChangesFrom (outputParamChanges); outputParamChanges.clearQueue (); eventList.clear (); ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence; processOutputEvents (inTimeStamp); return noErr; } static constexpr uint8 kNoteOff = 0x80; ///< note, off velocity static constexpr uint8 kNoteOn = 0x90; ///< note, on velocity static constexpr uint8 kPolyPressure = 0xA0; ///< note, pressure static constexpr uint8 kController = 0xB0; ///< controller, value static constexpr uint8 kProgramChangeStatus = 0xC0; ///< program change static constexpr uint8 kAfterTouchStatus = 0xD0; ///< channel pressure static constexpr uint8 kPitchBendStatus = 0xE0; ///< lsb, msb //const float kMidiScaler = 1.f / 127.f; static constexpr uint8 kChannelMask = 0x0F; //static const uint8 kStatusMask = 0xF0; static constexpr uint32 kDataMask = 0x7F; //------------------------------------------------------------------------ inline void AUWrapper::processOutputEvents (const AudioTimeStamp& inTimeStamp) { if (midiOutCount > 0 && outputEvents.getEventCount () > 0) { int32 total = outputEvents.getEventCount (); Event e = {}; for (int32 i = 0; i < total; i++) { if (outputEvents.getEvent (i, e) != kResultOk) break; //--- SYSEX ---------------- if (e.type == Event::kDataEvent && e.data.type == DataEvent::kMidiSysEx) { } else { switch (e.type) { //--- ----------------------- case Event::kNoteOnEvent: { UInt8 status = (UInt8) (kNoteOn | (e.noteOn.channel & kChannelMask)); UInt8 data1 = (UInt8) (e.noteOn.pitch & kDataMask); UInt8 data2 = (UInt8) ((int32) (e.noteOn.velocity * 127.f + 0.4999999f) & kDataMask); UInt8 channel = e.noteOn.channel; mCallbackHelper->addEvent (status, channel, data1, data2, e.sampleOffset); } break; //--- ----------------------- case Event::kNoteOffEvent: { UInt8 status = (UInt8) (kNoteOff | (e.noteOff.channel & kChannelMask)); UInt8 data1 = e.noteOff.pitch; UInt8 data2 = (UInt8) ((int32) (e.noteOff.velocity * 127.f + 0.4999999f) & kDataMask); UInt8 channel = e.noteOff.channel; mCallbackHelper->addEvent (status, channel, data1, data2, e.sampleOffset); } break; } } } outputEvents.clear (); mCallbackHelper->fireAtTimeStamp (inTimeStamp); } } //-------------------------------------------------------------------------------------------- #ifdef SMTG_AUWRAPPER_USES_AUSDK OSStatus AUWrapper::GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32& outDataSize, bool& outWritable) #else OSStatus AUWrapper::GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32& outDataSize, Boolean& outWritable) #endif { switch (inID) { //--- ----------------------- case kAudioUnitProperty_BypassEffect: { if (inScope == kAudioUnitScope_Global && bypassParamID != -1) { outWritable = true; outDataSize = sizeof (UInt32); return noErr; } return kAudioUnitErr_InvalidProperty; } //--- ----------------------- case kAudioUnitProperty_ParameterClumpName: { if (bypassParamID != -1) { outWritable = false; outDataSize = sizeof (AudioUnitParameterNameInfo); return noErr; } break; } //--- ----------------------- case kAudioUnitProperty_ParameterStringFromValue: { if (inScope == kAudioUnitScope_Global) { outDataSize = sizeof (AudioUnitParameterStringFromValue); outWritable = false; return noErr; } return kAudioUnitErr_InvalidProperty; } //--- ----------------------- case kAudioUnitProperty_ParameterValueFromString: { if (inScope == kAudioUnitScope_Global) { outDataSize = sizeof (AudioUnitParameterValueFromString); outWritable = false; return noErr; } return kAudioUnitErr_InvalidProperty; } //--- ----------------------- case kAudioUnitProperty_ElementName: { if (inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output) { outDataSize = sizeof (CFStringRef); return noErr; } break; } //--- ----------------------- case kAudioUnitProperty_CocoaUI: { outWritable = false; outDataSize = sizeof (AudioUnitCocoaViewInfo); return noErr; } //--- ----------------------- case kAudioUnitProperty_MIDIOutputCallbackInfo: { if (inScope == kAudioUnitScope_Global && midiOutCount > 0) { outDataSize = sizeof (CFArrayRef); outWritable = false; return noErr; } break; } //--- ----------------------- case kAudioUnitProperty_MIDIOutputCallback: { if (inScope == kAudioUnitScope_Global && midiOutCount > 0) { outDataSize = sizeof (AUMIDIOutputCallbackStruct); outWritable = true; return noErr; } break; } #if AUSDK_MIDI2_AVAILABLE //--- ----------------------- case kAudioUnitProperty_AudioUnitMIDIProtocol: { if (inScope == kAudioUnitScope_Global) { outDataSize = sizeof (SInt32); outWritable = false; return noErr; } return kAudioUnitErr_InvalidProperty; } #endif //--- ----------------------- case 64000: { if (editController) { outDataSize = sizeof (TPtrInt); outWritable = false; return noErr; } return kAudioUnitErr_InvalidProperty; } //--- ----------------------- case 64001: { if (VST3DynLibrary::gInstance) { outDataSize = sizeof (TPtrInt); outWritable = false; return noErr; } return kAudioUnitErr_InvalidProperty; } //--- ----------------------- case kAudioUnitProperty_OfflineRender: { if (inScope == kAudioUnitScope_Global) { outWritable = true; outDataSize = sizeof (UInt32); return noErr; } return kAudioUnitErr_InvalidProperty; } } return AUWRAPPER_BASE_CLASS::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable); } //-------------------------------------------------------------------------------------------- ComponentResult AUWrapper::SetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize) { switch (inID) { //--- ----------------------- case kAudioUnitProperty_BypassEffect: { if (inScope == kAudioUnitScope_Global && bypassParamID != -1) { bool tempNewSetting = *((UInt32*)inData) != 0; transferParamChanges.addChange (bypassParamID, tempNewSetting ? 1 : 0, 0); editController->setParamNormalized (bypassParamID, tempNewSetting ? 1 : 0); isBypassed = tempNewSetting; return noErr; } break; } //--- ----------------------- case kAudioUnitProperty_MIDIOutputCallback: { if (inScope == kAudioUnitScope_Global && midiOutCount > 0) { if (inDataSize < sizeof (AUMIDIOutputCallbackStruct)) return kAudioUnitErr_InvalidPropertyValue; AUMIDIOutputCallbackStruct* callbackStruct = (AUMIDIOutputCallbackStruct*)inData; mCallbackHelper->setCallbackInfo (callbackStruct->midiOutputCallback, callbackStruct->userData); return noErr; } break; } //--- ----------------------- case kAudioUnitProperty_OfflineRender: { if (inScope == kAudioUnitScope_Global) { bool offline = *((UInt32*)inData) != 0; if(offline != isOfflineRender && audioProcessor != nullptr) { isOfflineRender = offline; auto component = U::cast (audioProcessor); if(IsInitialized()) { audioProcessor->setProcessing (false); component->setActive (false); } ProcessSetup ps; ps.sampleRate = getSampleRate (); ps.maxSamplesPerBlock = GetMaxFramesPerSlice (); ps.symbolicSampleSize = kSample32; ps.processMode = isOfflineRender? kOffline : kRealtime; audioProcessor->setupProcessing (ps); if(IsInitialized()) { component->setActive (true); audioProcessor->setProcessing (true); } } return noErr; } break; } } return AUWRAPPER_BASE_CLASS::SetProperty (inID, inScope, inElement, inData, inDataSize); } //-------------------------------------------------------------------------------------------- bool AUWrapper::CanScheduleParameters () const { return false; } //-------------------------------------------------------------------------------------------- ComponentResult AUWrapper::GetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData) { switch (inID) { //--- ----------------------- case kAudioUnitProperty_BypassEffect: { if (inScope == kAudioUnitScope_Global && bypassParamID != -1) { UInt32 bypass = editController->getParamNormalized (bypassParamID) >= 0.5 ? 1 : 0; *((UInt32*)outData) = bypass; return noErr; } break; } //--- ----------------------- case kAudioUnitProperty_ParameterClumpName: { AudioUnitParameterNameInfo* parameterNameInfo = (AudioUnitParameterNameInfo*)outData; int32 clumpId = parameterNameInfo->inID; if (clumpId < 1 || clumpId > clumpGroups.size ()) return kAudioUnitErr_PropertyNotInUse; const String& clumpGroup = clumpGroups.at (clumpId - 1); parameterNameInfo->outName = (CFStringRef)clumpGroup.toCFStringRef (); return noErr; } //--- ----------------------- case kAudioUnitProperty_ParameterStringFromValue: { if (inScope == kAudioUnitScope_Global) { AudioUnitParameterStringFromValue* ps = (AudioUnitParameterStringFromValue*)outData; String128 paramString; if (editController->getParamStringByValue ( ps->inParamID, ps->inValue ? *ps->inValue : editController->getParamNormalized (ps->inParamID), paramString) == kResultTrue) ps->outString = createCFStringFromString128 (paramString); else ps->outString = CFStringCreateWithCString (nullptr, "", kCFStringEncodingUTF8); return noErr; } return kAudioUnitErr_InvalidProperty; } //--- ----------------------- case kAudioUnitProperty_ParameterValueFromString: { if (inScope == kAudioUnitScope_Global) { AudioUnitParameterValueFromString* ps = (AudioUnitParameterValueFromString*)outData; String128 paramString; createString128FromCFString (ps->inString, paramString); ParamValue value; if (editController->getParamValueByString (ps->inParamID, paramString, value) == kResultTrue) { ps->outValue = value; return noErr; } return -1; } return kAudioUnitErr_InvalidProperty; } //--- ----------------------- case kAudioUnitProperty_ElementName: { if (inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output) { auto component = U::cast (audioProcessor); BusInfo busInfo; if (component->getBusInfo (kAudio, inScope == kAudioUnitScope_Input ? kInput : kOutput, inElement, busInfo) == kResultTrue) { outData = (void*)createCFStringFromString128 (busInfo.name); return noErr; } } return kAudioUnitErr_InvalidProperty; } //--- ----------------------- case kAudioUnitProperty_CocoaUI: { AutoreleasePool ap; CFStringRef className = (CFStringRef)[[NSString alloc] initWithCString:SMTG_MAKE_STRING (SMTG_AUCocoaUIBase_CLASS_NAME) encoding:NSUTF8StringEncoding]; const char* image = class_getImageName ( objc_getClass (SMTG_MAKE_STRING (SMTG_AUCocoaUIBase_CLASS_NAME))); CFBundleRef bundle = GetBundleFromExecutable (image); CFURLRef url = CFBundleCopyBundleURL (bundle); CFRelease (bundle); AudioUnitCocoaViewInfo cocoaInfo = {url, {className}}; *((AudioUnitCocoaViewInfo*)outData) = cocoaInfo; return noErr; } //--- ----------------------- case kAudioUnitProperty_MIDIOutputCallbackInfo: { if (inScope == kAudioUnitScope_Global && midiOutCount > 0) { CFStringRef strs[1]; strs[0] = CFSTR ("MIDI Callback"); CFArrayRef callbackArray = CFArrayCreate (NULL, (const void**)strs, 1, &kCFTypeArrayCallBacks); *(CFArrayRef*)outData = callbackArray; return noErr; } return kAudioUnitErr_InvalidProperty; } #if AUSDK_MIDI2_AVAILABLE //--- ----------------------- case kAudioUnitProperty_HostMIDIProtocol: { if (inScope == kAudioUnitScope_Global) return noErr; return kAudioUnitErr_InvalidProperty; } //--- ----------------------- case kAudioUnitProperty_AudioUnitMIDIProtocol: { if (inScope == kAudioUnitScope_Global) { *reinterpret_cast(outData) = MIDIProtocolID::kMIDIProtocol_2_0; return noErr; } return kAudioUnitErr_InvalidProperty; } #endif // AUSDK_MIDI2_AVAILABLE //--- ----------------------- case 64000: { if (editController) { TPtrInt ptr = (TPtrInt)editController; *((TPtrInt*)outData) = ptr; return noErr; } else *((TPtrInt*)outData) = 0; return kAudioUnitErr_InvalidProperty; } //--- ----------------------- case 64001: { if (VST3DynLibrary::gInstance) { TPtrInt ptr = (TPtrInt)VST3DynLibrary::gInstance; *((TPtrInt*)outData) = ptr; return noErr; } else *((TPtrInt*)outData) = 0; return kAudioUnitErr_InvalidProperty; } } return AUWRAPPER_BASE_CLASS::GetProperty (inID, inScope, inElement, outData); } #if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK) //------------------------------------------------------------------------ int AUWrapper::GetNumCustomUIComponents () { #if !__LP64__ if (editController) { static int numUIComponents = 0; static bool once = true; if (once) { AutoreleasePool ap; once = false; IPlugView* plugView = editController->createView (ViewType::kEditor); if (plugView) { if (plugView->isPlatformTypeSupported (kPlatformTypeHIView) == kResultTrue) numUIComponents = 1; plugView->release (); } } return numUIComponents; } #endif // !__LP64__ return 0; } //------------------------------------------------------------------------ void AUWrapper::GetUIComponentDescs (ComponentDescription* inDescArray) { #if !__LP64__ if (editController && GetNumCustomUIComponents () > 0) { static OSType subType = 0; static OSType manuf = 0; static bool once = true; if (once) { once = false; short prevResFile = CurResFile (); CFBundleRef bundle = GetCurrentBundle (); if (!bundle) return; short resourceFileID = CFBundleOpenBundleResourceMap (bundle); UseResFile (resourceFileID); Handle h = Get1Resource ('thng', 9000); if (h) { HLock (h); OSType* hPtr = (OSType*)*h; subType = hPtr[1]; manuf = hPtr[2]; HUnlock (h); } UseResFile (prevResFile); CFRelease (bundle); } inDescArray[0].componentType = kAudioUnitCarbonViewComponentType; inDescArray[0].componentSubType = subType; inDescArray[0].componentManufacturer = manuf; inDescArray[0].componentFlags = 0; inDescArray[0].componentFlagsMask = 0; } #endif // !__LP64__ } #endif // !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK) //------------------------------------------------------------------------ Float64 AUWrapper::GetLatency () { Float64 latencyInSeconds = 0.0; if (audioProcessor) latencyInSeconds = audioProcessor->getLatencySamples () / getSampleRate (); return latencyInSeconds; } //------------------------------------------------------------------------ Float64 AUWrapper::GetTailTime () { Float64 tailTimeInSeconds = 0.0; if (audioProcessor) tailTimeInSeconds = audioProcessor->getTailSamples () / getSampleRate (); return tailTimeInSeconds; } #if !CA_USE_AUDIO_PLUGIN_ONLY //------------------------------------------------------------------------ // MARK: MusicDeviceBase //------------------------------------------------------------------------ OSStatus AUWrapper::HandleNoteOn (UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) { Event e = {}; e.type = Event::kNoteOnEvent; e.noteOn.channel = inChannel; e.noteOn.pitch = inNoteNumber; e.noteOn.velocity = inVelocity / 127.; e.noteOn.noteId = inNoteNumber; e.sampleOffset = inStartFrame; eventList.addEvent (e); return noErr; } //------------------------------------------------------------------------ OSStatus AUWrapper::HandleNoteOff (UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) { Event e = {}; e.type = Event::kNoteOffEvent; e.noteOff.channel = inChannel; e.noteOff.pitch = inNoteNumber; e.noteOff.velocity = inVelocity / 127.; e.noteOff.noteId = inNoteNumber; e.sampleOffset = inStartFrame; eventList.addEvent (e); return noErr; } //------------------------------------------------------------------------ ComponentResult AUWrapper::StartNote (MusicDeviceInstrumentID inInstrument, MusicDeviceGroupID inGroupID, NoteInstanceID* outNoteInstanceID, UInt32 inOffsetSampleFrame, const MusicDeviceNoteParams& inParams) { NoteInstanceID noteID = ((UInt8)inParams.mPitch) | ((noteCounter++) << 8); Event e = {}; e.type = Event::kNoteOnEvent; e.noteOn.pitch = inParams.mPitch; e.noteOn.velocity = inParams.mVelocity / 127.; e.noteOn.noteId = noteID; e.sampleOffset = inOffsetSampleFrame; // The Group ID is the channel with a standard midi device e.noteOn.channel = inGroupID; eventList.addEvent (e); if (outNoteInstanceID) *outNoteInstanceID = noteID; return noErr; } //------------------------------------------------------------------------ ComponentResult AUWrapper::StopNote (MusicDeviceGroupID inGroupID, NoteInstanceID inNoteInstanceID, UInt32 inOffsetSampleFrame) { UInt32 pitch = inNoteInstanceID & 0xFF; Event e = {}; e.type = Event::kNoteOffEvent; e.noteOff.pitch = pitch; e.noteOff.velocity = 0; e.noteOff.noteId = inNoteInstanceID; e.sampleOffset = inOffsetSampleFrame; // The Group ID is the channel with a standard midi device e.noteOff.channel = inGroupID; eventList.addEvent (e); return noErr; } //-------------------------------------------------------------------------------------------- OSStatus AUWrapper::GetInstrumentCount (UInt32& outInstCount) const { outInstCount = 1; return noErr; } //------------------------------------------------------------------------ // MARK: AUMIDIBase //------------------------------------------------------------------------ OSStatus AUWrapper::HandleNonNoteEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame) { OSStatus result = noErr; if (status == kPolyPressure) // kMidiMessage_PolyPressure { Event e = {}; e.type = Event::kPolyPressureEvent; e.polyPressure.channel = channel; e.polyPressure.pitch = data1; e.polyPressure.pressure = data2 / 127.; e.sampleOffset = inStartFrame; eventList.addEvent (e); return result; } if (midiMappingCache.empty ()) return result; ParamID pid = kNoParamId; ParamValue value = 0; CtrlNumber cn = -1; bool prgChange = false; switch (status) { //--- ----------------------- case kPitchBendStatus: // kMidiMessage_PitchWheel { cn = kPitchBend; unsigned short _14bit; _14bit = (unsigned short)data2; _14bit <<= 7; _14bit |= (unsigned short)data1; value = (double)_14bit / (double)0x3FFF; break; } //--- ----------------------- case kAfterTouchStatus: // kMidiMessage_ChannelPressure { cn = kAfterTouch; value = data1 / 127.f; break; } //--- ----------------------- case kController: // kMidiMessage_ControlChange { cn = data1; value = data2 / 127.f; break; } //--- ----------------------- case kProgramChangeStatus: // kMidiMessage_ProgramChange { programChangeInfoTransfer.accessTransferObject_rt ([this] (const auto& pci) { programChangeInfos = std::move (pci); }); pid = programChangeInfos[channel].pid; if (pid != kNoParamId && programChangeInfos[channel].numPrograms > 0 && data1 < programChangeInfos[channel].numPrograms) { value = static_cast (data1) / static_cast (programChangeInfos[channel].numPrograms - 1); prgChange = true; } } } if (prgChange || cn >= 0) { if (pid == kNoParamId && channel < midiMappingCache.busList[0].size ()) { auto it = midiMappingCache.busList[0][channel].find (cn); if (it != midiMappingCache.busList[0][channel].end ()) pid = it->second; } if (pid != kNoParamId) { beginEdit (pid); performEdit (pid, value); endEdit (pid); // make sure that our edit controller get the changes int32 index; IParamValueQueue* vq = outputParamChanges.addParameterData (pid, index); if (vq) vq->addPoint (inStartFrame, value, index); } } if (midiLearn && cn >= 0) midiLearnRingBuffer.push ({0, channel, cn}); return result; } #endif // !CA_USE_AUDIO_PLUGIN_ONLY #if AUSDK_MIDI2_AVAILABLE //------------------------------------------------------------------------ bool AUWrapper::handleMIDIEventPacket (UInt32 inOffsetSampleFrame, const MIDIEventPacket* packet) { struct UMPHandler final : UMP::UniversalMidiPacketHandlerAdapter { AUWrapper& w; UInt32 sampleOffset; UMPHandler (AUWrapper& wrapper, UInt32 sampleOffset) : w (wrapper), sampleOffset (sampleOffset) { } void onNoteOn (Group group, Channel channel, NoteNumber note, Velocity16 velocity, AttributeType attr, AttributeValue attrValue) const override { Event e = {}; e.type = Event::kNoteOnEvent; e.noteOn.channel = channel; e.noteOn.pitch = note; e.noteOn.velocity = static_cast (velocity) / static_cast (std::numeric_limits::max ()); e.noteOn.noteId = note; e.sampleOffset = sampleOffset; w.eventList.addEvent (e); } void onNoteOff (Group group, Channel channel, NoteNumber note, Velocity16 velocity, AttributeType attr, AttributeValue attrValue) const override { Event e = {}; e.type = Event::kNoteOffEvent; e.noteOff.channel = channel; e.noteOff.pitch = note; e.noteOff.velocity = static_cast (velocity) / static_cast (std::numeric_limits::max ()); e.noteOff.noteId = note; e.sampleOffset = sampleOffset; w.eventList.addEvent (e); } void onPolyPressure (Group group, Channel channel, NoteNumber note, Data32 data) const override { Event e = {}; e.type = Event::kPolyPressureEvent; e.polyPressure.channel = channel; e.polyPressure.pitch = note; e.polyPressure.pressure = data / static_cast (std::numeric_limits::max ()); e.sampleOffset = sampleOffset; w.eventList.addEvent (e); } void onProgramChange (Group group, Channel channel, OptionFlags options, Program program, BankMSB bankMSB, BankLSB bankLSB) const override { w.programChangeInfoTransfer.accessTransferObject_rt ([&] (const auto& pci) { w.programChangeInfos = std::move (pci); }); auto pid = w.programChangeInfos[channel].pid; if (pid != kNoParamId && w.programChangeInfos[channel].numPrograms > 0 && program < w.programChangeInfos[channel].numPrograms) { auto value = static_cast (program) / static_cast (w.programChangeInfos[channel].numPrograms - 1); addParameterChange (pid, value); } } void onControlChange (Group group, Channel channel, ControllerNumber controller, Data32 data) const override { if (!w.midiMappingCache.empty () && controller < ControllerNumbers::kCountCtrlNumber) { ParamID pid {}; constexpr std::array ignored = { ControllerNumbers::kCtrlBankSelectMSB, ControllerNumbers::kCtrlDataEntryMSB, ControllerNumbers::kCtrlBankSelectLSB, ControllerNumbers::kCtrlDataEntryLSB, ControllerNumbers::kCtrlNRPNSelectLSB, ControllerNumbers::kCtrlNRPNSelectMSB, ControllerNumbers::kCtrlRPNSelectLSB, ControllerNumbers::kCtrlRPNSelectMSB}; if (std::find (ignored.begin (), ignored.end (), controller) != ignored.end ()) return; addControllerChange (group, channel, controller, data / static_cast (std::numeric_limits::max ())); } } void onPitchBend (Group group, Channel channel, Data32 data) const override { if (!w.midiMappingCache.empty ()) { addControllerChange (group, channel, ControllerNumbers::kPitchBend, data / static_cast (std::numeric_limits::max ())); } } void onChannelPressure (Group group, Channel channel, Data32 data) const override { if (!w.midiMappingCache.empty ()) { addControllerChange (group, channel, ControllerNumbers::kAfterTouch, data / static_cast (std::numeric_limits::max ())); } } void addControllerChange (Group group, Channel channel, ControllerNumber ctrler, ParamValue value) const { assert (!w.midiMappingCache.empty ()); if (channel < w.midiMappingCache.busList[0].size ()) { auto it = w.midiMappingCache.busList[0][channel].find (ctrler); if (it != w.midiMappingCache.busList[0][channel].end ()) addParameterChange (it->second, value); } if (w.midiLearn) w.midiLearnRingBuffer.push ({0, channel, ctrler}); } void addParameterChange (ParamID pid, ParamValue value) const { w.beginEdit (pid); w.performEdit (pid, value); w.endEdit (pid); int32 index; IParamValueQueue* vq = w.outputParamChanges.addParameterData (pid, index); if (vq) vq->addPoint (sampleOffset, value, index); } }; UMPHandler handler (*this, inOffsetSampleFrame); return UMP::parsePackets (packet->wordCount, packet->words, handler) == packet->wordCount; } //------------------------------------------------------------------------ OSStatus AUWrapper::MIDIEventList (UInt32 inOffsetSampleFrame, const struct MIDIEventList* eventList) { if (eventList->protocol != MIDIProtocolID::kMIDIProtocol_2_0) return -1; if (eventList->numPackets == 0) return noErr; midiMappingTransfer.accessTransferObject_rt ( [&] (auto& obj) { midiMappingCache = std::move (obj); }); auto eventPacket = &eventList->packet[0]; if (!handleMIDIEventPacket (inOffsetSampleFrame, eventPacket)) return -1; for (auto i = 1u; i < eventList->numPackets; ++i) { eventPacket = MIDIEventPacketNext (eventPacket); if (!handleMIDIEventPacket (inOffsetSampleFrame, eventPacket)) return -1; } return noErr; } #endif // AUSDK_MIDI2_AVAILABLE //------------------------------------------------------------------------ // MARK: IComponentHandler //------------------------------------------------------------------------ tresult PLUGIN_API AUWrapper::beginEdit (ParamID tag) { AudioUnitEvent auEvent; auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance (); auEvent.mArgument.mParameter.mParameterID = tag; auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global; auEvent.mArgument.mParameter.mElement = 0; auEvent.mEventType = kAudioUnitEvent_BeginParameterChangeGesture; AUEventListenerNotify (paramListenerRef, NULL, &auEvent); return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API AUWrapper::performEdit (ParamID tag, ParamValue valueNormalized) { AudioUnitParameter sPar = {GetComponentInstance (), tag, kAudioUnitScope_Global, 0}; AUParameterSet (paramListenerRef, NULL, &sPar, valueNormalized, 0); AudioUnitEvent auEvent; auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance (); auEvent.mArgument.mParameter.mParameterID = tag; auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global; auEvent.mArgument.mParameter.mElement = 0; auEvent.mEventType = kAudioUnitEvent_ParameterValueChange; AUEventListenerNotify (paramListenerRef, NULL, &auEvent); return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API AUWrapper::endEdit (ParamID tag) { AudioUnitEvent auEvent; auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance (); auEvent.mArgument.mParameter.mParameterID = tag; auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global; auEvent.mArgument.mParameter.mElement = 0; auEvent.mEventType = kAudioUnitEvent_EndParameterChangeGesture; AUEventListenerNotify (paramListenerRef, NULL, &auEvent); return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API AUWrapper::restartComponent (int32 flags) { tresult result = kNotImplemented; if (flags & kParamValuesChanged) { syncParameterValues (); updateProgramChangesCache (); result = kResultTrue; } if (flags & kMidiCCAssignmentChanged) { updateMidiMappingCache (); result = kResultTrue; } if (flags & kParamTitlesChanged) { cacheParameterValues (); result = kResultTrue; PropertyChanged (kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0); } if (flags & kLatencyChanged) { AudioUnitEvent auEvent; auEvent.mArgument.mProperty.mAudioUnit = GetComponentInstance (); auEvent.mArgument.mProperty.mPropertyID = kAudioUnitProperty_Latency; auEvent.mArgument.mProperty.mScope = kAudioUnitScope_Global; auEvent.mArgument.mProperty.mElement = 0; auEvent.mEventType = kAudioUnitEvent_PropertyChange; AUEventListenerNotify (paramListenerRef, NULL, &auEvent); result = kResultTrue; } // TODO: finish restartComponent implementation return result; } //------------------------------------------------------------------------ // MARK: Helpers //------------------------------------------------------------------------ void AUWrapper::syncParameterValues () { if (editController) { for (int32 i = 0; i < editController->getParameterCount (); i++) { ParameterInfo pi; editController->getParameterInfo (i, pi); if (pi.flags & ParameterInfo::kIsBypass) { bypassParamID = pi.id; } else { ParamValue value = editController->getParamNormalized (pi.id); GlobalScope ().GetElement (0)->SetParameter (pi.id, value); } } } } //------------------------------------------------------------------------ void AUWrapper::cacheParameterValues () { clearParameterValueCache (); if (editController) { Steinberg::Base::Thread::FGuard guard (parameterCacheChanging); auto unitInfoController = U::cast (editController); if (unitInfoController) { if (unitInfos.empty ()) buildUnitInfos (unitInfoController, unitInfos); } // This must be cached! for (int32 i = 0; i < editController->getParameterCount (); i++) { AudioUnitParameterInfo auInfo; ParameterInfo pi; editController->getParameterInfo (i, pi); int32 clumpIdx = 0; // Zero should be reserved for values that are not clumped // Build clump group if possible if (unitInfoController) { String fullUnitName; ConstString separator (STR (".")); bool insertSeparator = false; UnitInfoMap::const_iterator it = unitInfos.find (pi.unitId); while (it != unitInfos.end () && it->second.id != kRootUnitId) { ConstString unitName (it->second.name); if (unitName.length () > 0) { if (insertSeparator) fullUnitName.insertAt (0, separator); insertSeparator = true; fullUnitName.insertAt (0, unitName); } it = unitInfos.find (it->second.parentUnitId); } if (!fullUnitName.isEmpty ()) { bool insertClump = true; clumpIdx = 1; for (int32 i = 0; i < clumpGroups.size (); i++) { const String& str = clumpGroups.at (i); if (str.compare (fullUnitName) == 0) { insertClump = false; break; } ++clumpIdx; } if (insertClump) clumpGroups.push_back (fullUnitName); } } auInfo.name[0] = 0; auInfo.cfNameString = createCFStringFromString128 (pi.title); auInfo.defaultValue = pi.defaultNormalizedValue; auInfo.minValue = 0; auInfo.maxValue = 1; auInfo.unit = kAudioUnitParameterUnit_CustomUnit; auInfo.unitName = createCFStringFromString128 (pi.units); // We release this ourselved when emptying the cache auInfo.flags = /*kAudioUnitParameterFlag_CFNameRelease |*/ kAudioUnitParameterFlag_HasCFNameString | kAudioUnitParameterFlag_IsReadable | kAudioUnitParameterFlag_HasName; if (clumpIdx > 0) AUBase::HasClump (auInfo, clumpIdx); if (!(pi.flags & ParameterInfo::kIsReadOnly)) auInfo.flags |= kAudioUnitParameterFlag_IsWritable; if (!(pi.flags & ParameterInfo::kCanAutomate)) auInfo.flags |= kAudioUnitParameterFlag_NonRealTime; if (pi.stepCount == 1) auInfo.unit = kAudioUnitParameterUnit_Boolean; cachedParameterInfos[pi.id] = auInfo; } } } //------------------------------------------------------------------------ void AUWrapper::clearParameterValueCache () { Steinberg::Base::Thread::FGuard guard (parameterCacheChanging); for (CachedParameterInfoMap::const_iterator it = cachedParameterInfos.begin (), end = cachedParameterInfos.end (); it != end; ++it) { CFRelease (it->second.cfNameString); CFRelease (it->second.unitName); } cachedParameterInfos.clear (); } //------------------------------------------------------------------------ void AUWrapper::updateProcessContext () { memset (&processContext, 0, sizeof (ProcessContext)); processContext.sampleRate = getSampleRate (); Float64 beat = 0; Float64 tempo = 0; if (CallHostBeatAndTempo (&beat, &tempo) == noErr) { processContext.state |= ProcessContext::kTempoValid | ProcessContext::kProjectTimeMusicValid; processContext.tempo = tempo; processContext.projectTimeMusic = beat; } UInt32 deltaSampleOffsetToNextBeat = 0; Float32 timeSigNumerator = 0; UInt32 timeSigDenominator = 0; Float64 currentMeasureDownBeat = 0; if (CallHostMusicalTimeLocation (&deltaSampleOffsetToNextBeat, &timeSigNumerator, &timeSigDenominator, ¤tMeasureDownBeat) == noErr) { processContext.state |= ProcessContext::kTimeSigValid | ProcessContext::kBarPositionValid | ProcessContext::kClockValid; processContext.timeSigNumerator = timeSigNumerator; processContext.timeSigDenominator = timeSigDenominator; processContext.samplesToNextClock = deltaSampleOffsetToNextBeat; processContext.barPositionMusic = currentMeasureDownBeat; } Boolean isPlaying; Boolean transportStateChanged; Float64 currentSampleInTimeLine; Boolean isCycling; Float64 cycleStartBeat; Float64 cycleEndBeat; if (CallHostTransportState (&isPlaying, &transportStateChanged, ¤tSampleInTimeLine, &isCycling, &cycleStartBeat, &cycleEndBeat) == noErr) { processContext.state |= ProcessContext::kCycleValid; processContext.cycleStartMusic = cycleStartBeat; processContext.cycleEndMusic = cycleEndBeat; processContext.projectTimeSamples = currentSampleInTimeLine; if (isPlaying) processContext.state |= ProcessContext::kPlaying; if (isCycling) processContext.state |= ProcessContext::kCycleActive; } } //------------------------------------------------------------------------ void AUWrapper::onTimer (Timer* timer) { ParamID pid; ParamValue value; int32 sampleOffset; while (outputParamTransfer.getNextChange (pid, value, sampleOffset)) { GlobalScope ().GetElement (0)->SetParameter (pid, value); AudioUnitParameter auParam = {}; auParam.mAudioUnit = GetComponentInstance (); auParam.mElement = 0; auParam.mScope = kAudioUnitScope_Global; auParam.mParameterID = pid; AUParameterListenerNotify (paramListenerRef, nullptr, &auParam); editController->setParamNormalized (pid, value); } if (isBypassed) { ProcessData bypassData = processData; bypassData.numSamples = 0; processParamChanges.clearQueue (); transferParamChanges.transferChangesTo (processParamChanges); if (processParamChanges.getParameterCount () > 0) { audioProcessor->process (bypassData); outputParamTransfer.transferChangesFrom (outputParamChanges); outputParamChanges.clearQueue (); } } if (midiLearn) { MidiLearnEvent event; while (midiLearnRingBuffer.pop (event)) midiLearn->onLiveMIDIControllerInput (event.busIndex, event.channel, event.midiCC); } } //------------------------------------------------------------------------ bool AUWrapper::validateChannelPair (int inChannelsIn, int inChannelsOut, const AUChannelInfo* info, UInt32 numChanInfo) const { // we've the following cases (some combinations) to test here: /* >0 An explicit number of channels on either side 0 that side (generally input!) has no elements -1 wild card: -1,-1 any num channels as long as same channels on in and out -1,-2 any num channels channels on in and out - special meaning -2+ indicates total num channs AU can handle - elements configurable to any num channels, - element count in scope must be writable */ // now chan layout can contain -1 for either scope (ie. doesn't care) for (unsigned int i = 0; i < numChanInfo; ++i) { // less than zero on both sides - check for special attributes if ((info[i].inChannels < 0) && (info[i].outChannels < 0)) { // these are our wild card matches if (info[i].inChannels == -1 && info[i].outChannels == -1) { if (inChannelsIn && inChannelsOut) { if (inChannelsOut == inChannelsIn) return true; } else return true; // if one of these is zero, then a -1 means any } else if ((info[i].inChannels == -1 && info[i].outChannels == -2) || (info[i].inChannels == -2 && info[i].outChannels == -1)) { return true; } // these are our total num channels matches // element count MUST be writable else { bool outWrite = false; bool inWrite = false; // IsElementCountWritable (kAudioUnitScope_Output, outWrite); // IsElementCountWritable (kAudioUnitScope_Input, inWrite); if (inWrite && outWrite) { if ((inChannelsOut <= abs (info[i].outChannels)) && (inChannelsIn <= abs (info[i].inChannels))) { return true; } } } } // special meaning on input, specific num on output else if (info[i].inChannels < 0) { if (info[i].outChannels == inChannelsOut) { // can do any in channels if (info[i].inChannels == -1) { return true; } // total chans on input else { bool inWrite = false; // IsElementCountWritable (kAudioUnitScope_Input, inWrite); if (inWrite && (inChannelsIn <= abs (info[i].inChannels))) { return true; } } } } // special meaning on output, specific num on input else if (info[i].outChannels < 0) { if (info[i].inChannels == inChannelsIn) { // can do any out channels if (info[i].outChannels == -1) { return true; } // total chans on output else { bool outWrite = false; // IsElementCountWritable (kAudioUnitScope_Output, outWrite); if (outWrite && (inChannelsOut <= abs (info[i].outChannels))) { return true; } } } } // both chans in struct >= 0 - thus has to explicitly match else if ((info[i].inChannels == inChannelsIn) && (info[i].outChannels == inChannelsOut)) { return true; } // now check to see if a wild card on the args (inChannelsIn or inChannelsOut chans is zero) // is found // tells us to match just one side of the scopes else if (inChannelsIn == 0) { if (info[i].outChannels == inChannelsOut) { return true; } } else if (inChannelsOut == 0) { if (info[i].inChannels == inChannelsIn) { return true; } } } return false; } //------------------------------------------------------------------------ bool AUWrapper::getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId) { programListId = kNoProgramListId; unitId = -1; IUnitInfo* unitInfo = NULL; if (editController && editController->queryInterface (IUnitInfo::iid, (void**)&unitInfo) == kResultTrue && unitInfo) { if (unitInfo->getUnitByBus (kEvent, kInput, 0, midiChannel, unitId) == kResultTrue) { int32 unitCount = unitInfo->getUnitCount (); for (int32 i = 0; i < unitCount; i++) { UnitInfo unitInfoStruct = {}; if (unitInfo->getUnitInfo (i, unitInfoStruct) == kResultTrue) { if (unitId == unitInfoStruct.id) { programListId = unitInfoStruct.programListId; unitInfo->release (); return programListId != kNoProgramListId; } } } } unitInfo->release (); } return false; } //------------------------------------------------------------------------ AUWrapper::MIDIOutputCallbackHelper::MIDIOutputCallbackHelper () { mMIDIMessageList.reserve (16); mMIDICallbackStruct.midiOutputCallback = NULL; } //------------------------------------------------------------------------ AUWrapper::MIDIOutputCallbackHelper::~MIDIOutputCallbackHelper () = default; //------------------------------------------------------------------------ void AUWrapper::MIDIOutputCallbackHelper::setCallbackInfo (AUMIDIOutputCallback callback, void* userData) { mMIDICallbackStruct.midiOutputCallback = callback; mMIDICallbackStruct.userData = userData; } //------------------------------------------------------------------------ void AUWrapper::MIDIOutputCallbackHelper::addEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame) { MIDIMessageInfoStruct info = {status, channel, data1, data2, inStartFrame}; mMIDIMessageList.push_back (info); } //------------------------------------------------------------------------ void AUWrapper::MIDIOutputCallbackHelper::fireAtTimeStamp (const AudioTimeStamp& inTimeStamp) { if (!mMIDIMessageList.empty () && mMIDICallbackStruct.midiOutputCallback != nullptr) { auto callMidiOutputCallback = [&] (MIDIPacketList* pktlist) { auto result = mMIDICallbackStruct.midiOutputCallback (mMIDICallbackStruct.userData, &inTimeStamp, 0, pktlist); if (result != noErr) NSLog (@"error calling midiOutputCallback: %d", result); }; // synthesize the packet list and call the MIDIOutputCallback // iterate through the vector and get each item MIDIPacketList* pktlist = PacketList (); MIDIPacket* pkt = MIDIPacketListInit (pktlist); for (auto it = mMIDIMessageList.begin (); it != mMIDIMessageList.end (); it++) { auto& item = *it; static_assert (sizeof (Byte) == 1, "the following code expects this"); std::array data = {item.status, item.data1, item.data2}; pkt = MIDIPacketListAdd (pktlist, mBuffersAllocated.size (), pkt, item.startFrame, data.size (), data.data ()); if (pkt == nullptr) { // send what we have and then clear the buffer and send again // issue the callback with what we got callMidiOutputCallback (pktlist); // clear stuff we've already processed, and fire again mMIDIMessageList.erase (mMIDIMessageList.begin (), it); fireAtTimeStamp (inTimeStamp); return; } } callMidiOutputCallback (pktlist); } mMIDIMessageList.clear (); } #if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK) // copied from AUDispatch.cpp #if __LP64__ // comp instance, parameters in forward order #define PARAM(_typ, _name, _index, _nparams) _typ _name = *(_typ*)¶ms->params[_index + 1]; #else // parameters in reverse order, then comp instance #define PARAM(_typ, _name, _index, _nparams) \ _typ _name = *(_typ*)¶ms->params[_nparams - 1 - _index]; #endif //------------------------------------------------------------------------ ComponentResult AUWrapper::ComponentEntryDispatch (ComponentParameters* params, AUWrapper* This) { OSStatus result = noErr; switch (params->what) { //--- ----------------------- case kAudioUnitSetPropertySelect: { PARAM (AudioUnitPropertyID, inID, 0, 5); if (inID == kAudioUnitProperty_ClassInfoFromDocument) { PARAM (AudioUnitScope, inScope, 1, 5); // PARAM (AudioUnitElement, inElement, 2, 5); PARAM (const void*, inData, 3, 5); PARAM (UInt32, inDataSize, 4, 5); ca_require (inDataSize == sizeof (CFPropertyListRef*), InvalidPropertyValue); ca_require (inScope == kAudioUnitScope_Global, InvalidScope); result = This->restoreState (*(CFPropertyListRef*)inData, true); return result; } break; } } return AUWRAPPER_BASE_CLASS::ComponentEntryDispatch (params, This); InvalidScope: return kAudioUnitErr_InvalidScope; InvalidPropertyValue: return kAudioUnitErr_InvalidPropertyValue; } #endif //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ class CleanupMemory { public: CleanupMemory () {} ~CleanupMemory () { if (channelInfos) { delete[] channelInfos; channelInfos = nullptr; } } }; CleanupMemory _gCleanupMemory; CleanupMemory* gCleanupMemory = &_gCleanupMemory; static OSStatus AUWrapperMethodSetProperty (void* self, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize) { OSStatus result = noErr; auto plugInstance = reinterpret_cast (self); auto auwrapper = reinterpret_cast (&plugInstance->mInstanceStorage); try { if (inData && inDataSize && inID == kAudioUnitProperty_ClassInfoFromDocument) { #ifdef SMTG_AUWRAPPER_USES_AUSDK ThrowExceptionIf (!inDataSize == sizeof (CFPropertyListRef*), kAudioUnitErr_InvalidPropertyValue); ThrowExceptionIf (!inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidScope); #else #endif // SMTG_AUWRAPPER_USES_AUSDK return auwrapper->restoreState (*(CFPropertyListRef*)inData, true); } return auwrapper->DispatchSetProperty (inID, inScope, inElement, inData, inDataSize); } #ifdef SMTG_AUWRAPPER_USES_AUSDK AUSDK_Catch (result) return result; #else catch (OSStatus err) { return err; } catch (...) { return -1; } InvalidScope: return kAudioUnitErr_InvalidScope; InvalidPropertyValue: return kAudioUnitErr_InvalidPropertyValue; #endif // SMTG_AUWRAPPER_USES_AUSDK } struct AUWrapperLookup { static AudioComponentMethod Lookup (SInt16 selector) { // ProcessBufferLists not supported, so don't try any of the process methods if (selector == kAudioUnitProcessSelect || selector == kAudioUnitProcessMultipleSelect) return NULL; if (selector == kAudioUnitSetPropertySelect) return (AudioComponentMethod)AUWrapperMethodSetProperty; AudioComponentMethod method = AUBaseProcessLookup::Lookup (selector); if (method) return method; method = AUMusicLookup::Lookup (selector); if (method) return method; method = AUBaseProcessMultipleLookup::Lookup (selector); if (method) return method; return NULL; } }; template class AUFactory : public APFactory { }; //------------------------------------------------------------------------ // new entry method extern "C" { void* AUWrapperFactory (const AudioComponentDescription* inDesc); __attribute__ ((visibility ("default"))) void* AUWrapperFactory ( const AudioComponentDescription* inDesc) { return AUFactory::Factory (inDesc); } } // extern "C" #if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK) //------------------------------------------------------------------------ // old entry method extern "C" { ComponentResult AUWrapperEntry (ComponentParameters* params, AUWrapper* obj); __attribute__ ((visibility ("default"))) ComponentResult AUWrapperEntry ( ComponentParameters* params, AUWrapper* obj) { return ComponentEntryPoint::Dispatch (params, obj); } } #endif //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/config0000644000000000000000000000013215124701711023551 xustar0030 mtime=1767080905.280506322 30 atime=1767080905.279700628 30 ctime=1767080905.280506322 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/config/0000755000175000001440000000000015124701711023616 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/config/PaxHeaders/ausdkpath.xcconfig0000644000000000000000000000013215124701711027334 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/config/ausdkpath.xcconfig0000644000175000001440000000200615124701711027322 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : ausdkpath.xcconfig // Created by : Steinberg, 5/24/12 // Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- // If you are building with Xcode >= 4.x please add the path to your downloaded Audio Tools for Xcode CUSTOM_AU_SDK_PATH=/Applications/Xcode.app/Contents/Developer/Extras/CoreAudio/ // AUWRAPPER_CHANGE qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/config/PaxHeaders/auwrapper.xcconfig0000644000000000000000000000013215124701711027356 xustar0030 mtime=1767080905.280506322 30 atime=1767080905.279700628 30 ctime=1767080905.280506322 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/config/auwrapper.xcconfig0000644000175000001440000000237515124701711027355 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : auwrapper.xcconfig // Created by : Steinberg, 5/24/12 // Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "../../../../../base/mac/config/libc++base" #include "ausdkpath" PRODUCT_NAME = auwrapper HEADER_SEARCH_PATHS = ../../../.. $(DEVELOPER_DIR)/Examples/CoreAudio/** $(DEVELOPER_DIR)/Extras/CoreAudio/** $(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUViewBase/** ../../../../external.apple.coreaudio/** GCC_PREFIX_HEADER = auwrapper_prefix.pch GCC_PRECOMPILE_PREFIX_HEADER = YES CLANG_CXX_LANGUAGE_STANDARD = c++17 CLANG_CXX_LIBRARY = libc++ qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/config/PaxHeaders/auwrapper_debug.xcconfig0000644000000000000000000000013215124701711030524 xustar0030 mtime=1767080905.280506322 30 atime=1767080905.280506322 30 ctime=1767080905.280506322 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/config/auwrapper_debug.xcconfig0000644000175000001440000000164315124701711030520 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : ausdkpath_debug.xcconfig // Created by : Steinberg, 5/24/12 // Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "../../../../../base/mac/config/debug" GCC_OPTIMIZATION_LEVEL = 0 DEPLOYMENT_POSTPROCESSING = NO qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/config/PaxHeaders/auwrapper_release.xcconfi0000644000000000000000000000013215124701711030707 xustar0030 mtime=1767080905.280506322 30 atime=1767080905.280506322 30 ctime=1767080905.280506322 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/config/auwrapper_release.xcconfig0000644000175000001440000000165015124701711031050 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : auwrapper_release.xcconfig // Created by : Steinberg, 5/24/12 // Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "../../../../../base/mac/config/release" GCC_OPTIMIZATION_LEVEL = 3 DEPLOYMENT_POSTPROCESSING = NO qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/aucocoaview.mm0000644000000000000000000000013215124701711025221 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/aucocoaview.mm0000644000175000001440000002064315124701711025216 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/aucocoaview.mm // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #import "aucocoaview.h" #import "auwrapper.h" #import "public.sdk/source/vst/utility/objcclassbuilder.h" #import "pluginterfaces/base/funknownimpl.h" #import "pluginterfaces/gui/iplugview.h" #import "pluginterfaces/vst/ivsteditcontroller.h" //------------------------------------------------------------------------ @interface NSObject (SMTG_AUView) - (id)initWithEditController:(Steinberg::Vst::IEditController*)editController audioUnit:(AudioUnit)au preferredSize:(NSSize)size; @end //------------------------------------------------------------------------ namespace Steinberg { namespace { //------------------------------------------------------------------------ struct AUPlugFrame : U::Implements> { AUPlugFrame (NSView* parent) : parent (parent) {} tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* vr) override { NSRect newSize = NSMakeRect ([parent frame].origin.x, [parent frame].origin.y, vr->right - vr->left, vr->bottom - vr->top); [parent setFrame:newSize]; return kResultTrue; } NSView* parent; }; //------------------------------------------------------------------------ struct AUView { static constexpr auto VarNamePlugView = "plugView"; static constexpr auto VarNameEditController = "editController"; static constexpr auto VarNameAudioUnit = "audioUnit"; static constexpr auto VarNameDynlib = "dynlib"; static constexpr auto VarNamePlugFrame = "plugFrame"; static constexpr auto VarNameIsAttached = "isAttached"; struct Instance : ObjCInstance { using PlugViewVar = std::optional>; using EditControllerVar = std::optional>; using AudioUnitVar = std::optional>; using DynlibVar = std::optional>; using PlugFrameVar = std::optional>; using IsAttachedVar = std::optional>; Instance (__unsafe_unretained id obj) : ObjCInstance (obj, [NSView class]) { plugView = getVariable (VarNamePlugView); editController = getVariable (VarNameEditController); audioUnit = getVariable (VarNamePlugView); dynlib = getVariable (VarNameDynlib); plugFrame = getVariable (VarNamePlugFrame); isAttached = getVariable (VarNameIsAttached); } PlugViewVar plugView; EditControllerVar editController; AudioUnitVar audioUnit; DynlibVar dynlib; PlugFrameVar plugFrame; IsAttachedVar isAttached; }; static id alloc () { static ObjCClass gInstance; return [gInstance.cl alloc]; } private: struct ObjCClass { Class cl; ObjCClass () { cl = ObjCClassBuilder () .init ("SMTG_AUView", [NSView class]) .addIvar (VarNamePlugView) .addIvar (VarNameEditController) .addIvar (VarNameAudioUnit) .addIvar (VarNameDynlib) .addIvar (VarNamePlugFrame) .addIvar (VarNameIsAttached) .addMethod (@selector (initWithEditController:audioUnit:preferredSize:), initWithEditController) .addMethod (@selector (setFrame:), setFrame) .addMethod (@selector (isFlipped), isFlipped) .addMethod (@selector (viewDidMoveToSuperview), viewDidMoveToSuperview) .addMethod (@selector (dealloc), dealloc) .finalize (); } static id initWithEditController (id self, SEL cmd, Vst::IEditController* editController, AudioUnit au, NSSize size) { ObjCInstance obj (self); self = obj.callSuper (@selector (initWithFrame:), NSMakeRect (0, 0, size.width, size.height)); if (self) { Instance inst (self); inst.editController->set (editController); editController->addRef (); inst.audioUnit->set (au); auto plugView = editController->createView (Vst::ViewType::kEditor); if (!plugView || plugView->isPlatformTypeSupported (kPlatformTypeNSView) != kResultTrue) { [self dealloc]; return nil; } inst.plugView->set (plugView); auto plugFrame = NEW AUPlugFrame (self); inst.plugFrame->set (plugFrame); plugView->setFrame (plugFrame); if (plugView->attached (self, kPlatformTypeNSView) != kResultTrue) { [self dealloc]; return nil; } ViewRect vr; if (plugView->getSize (&vr) == kResultTrue) { NSRect newSize = NSMakeRect (0, 0, vr.right - vr.left, vr.bottom - vr.top); [self setFrame:newSize]; } inst.isAttached->set (YES); FObject* fObject = nullptr; UInt32 size = sizeof (FObject*); if (AudioUnitGetProperty (au, 64001, kAudioUnitScope_Global, 0, &fObject, &size) == noErr) { fObject->addRef (); inst.dynlib->set (fObject); } } return self; } static void setFrame (id self, SEL cmd, NSRect newSize) { Instance inst (self); inst.callSuper (@selector (setFrame:), newSize); ViewRect viewRect (0, 0, newSize.size.width, newSize.size.height); if (inst.plugView->get ()) inst.plugView->get ()->onSize (&viewRect); } static BOOL isFlipped (id self, SEL cmd) { return YES; } static void viewDidMoveToSuperview (id self, SEL cmd) { Instance inst (self); if (inst.plugView->get ()) { if ([self superview]) { if (!inst.isAttached->get ()) { if (inst.plugView->get ()->attached (self, kPlatformTypeNSView) == kResultTrue) { inst.isAttached->set (YES); } } } else { if (inst.isAttached->get ()) { inst.plugView->get ()->removed (); inst.isAttached->set (NO); } } } } static void dealloc (id self, SEL cmd) { Instance inst (self); if (auto plugView = inst.plugView->get ()) { if (inst.isAttached->get ()) { plugView->setFrame (0); plugView->removed (); } plugView->release (); if (auto plugFrame = inst.plugFrame->get ()) plugFrame->release (); if (auto editController = inst.editController->get ()) { auto refCount = editController->addRef (); if (refCount == 2) editController->terminate (); editController->release (); editController->release (); inst.editController->set (nullptr); } } if (auto dynlib = inst.dynlib->get ()) dynlib->release (); inst.callSuper (@selector (dealloc)); } }; }; //------------------------------------------------------------------------ } // anonymous } // Steinberg //------------------------------------------------------------------------ @implementation SMTG_AUCocoaUIBase_CLASS_NAME //------------------------------------------------------------------------ - (unsigned)interfaceVersion { return 0; } //------------------------------------------------------------------------ - (NSString*)description { return @"Cocoa View"; } //------------------------------------------------------------------------ - (NSView*)uiViewForAudioUnit:(AudioUnit)inAU withSize:(NSSize)inPreferredSize { using namespace Steinberg; Vst::IEditController* editController = 0; UInt32 size = sizeof (Vst::IEditController*); if (AudioUnitGetProperty (inAU, 64000, kAudioUnitScope_Global, 0, &editController, &size) != noErr) return nil; return [[AUView::alloc () initWithEditController:editController audioUnit:inAU preferredSize:inPreferredSize] autorelease]; } @end /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/NSDataIBStream.h0000644000000000000000000000013215124701711025233 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279210874 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/NSDataIBStream.h0000644000175000001440000000506115124701711025225 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/NSDataIBStream.h // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma once #import #import "pluginterfaces/base/ibstream.h" #import "public.sdk/source/vst/hosting/hostclasses.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ class NSDataIBStream : public IBStream, Vst::IStreamAttributes { public: NSDataIBStream (NSData* data, bool hideAttributes = false); virtual ~NSDataIBStream (); //---from IBStream------------------- tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = 0) SMTG_OVERRIDE; tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0) SMTG_OVERRIDE; tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = 0) SMTG_OVERRIDE; tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; //---from Vst::IStreamAttributes----- tresult PLUGIN_API getFileName (String128 name) SMTG_OVERRIDE; IAttributeList* PLUGIN_API getAttributes () SMTG_OVERRIDE; //------------------------------------------------------------------------ DECLARE_FUNKNOWN_METHODS protected: NSData* data; int64 currentPos; IPtr attrList; bool hideAttributes; }; //------------------------------------------------------------------------ class NSMutableDataIBStream : public NSDataIBStream { public: NSMutableDataIBStream (NSMutableData* data); virtual ~NSMutableDataIBStream (); tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0) SMTG_OVERRIDE; //------------------------------------------------------------------------ protected: NSMutableData* mdata; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/NSDataIBStream.mm0000644000000000000000000000013215124701711025415 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/NSDataIBStream.mm0000644000175000001440000001162215124701711025407 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/NSDataIBStream.mm // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #include "NSDataIBStream.h" #include "pluginterfaces/vst/ivstattributes.h" #include #if __clang__ #if __has_feature(objc_arc) && __clang_major__ >= 3 #define ARC_ENABLED 1 #endif // __has_feature(objc_arc) #endif // __clang__ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ NSDataIBStream::NSDataIBStream (NSData* data, bool hideAttributes) : data (data) , currentPos (0) , hideAttributes (hideAttributes) { FUNKNOWN_CTOR if (!hideAttributes) attrList = HostAttributeList::make (); #if !ARC_ENABLED [data retain]; #endif } //------------------------------------------------------------------------ NSDataIBStream::~NSDataIBStream () { #if !ARC_ENABLED [data release]; #endif FUNKNOWN_DTOR } //------------------------------------------------------------------------ IMPLEMENT_REFCOUNT (NSDataIBStream) //------------------------------------------------------------------------ tresult PLUGIN_API NSDataIBStream::queryInterface (const TUID iid, void** obj) { QUERY_INTERFACE (iid, obj, FUnknown::iid, IBStream) QUERY_INTERFACE (iid, obj, IBStream::iid, IBStream) if (!hideAttributes) QUERY_INTERFACE (iid, obj, IStreamAttributes::iid, IStreamAttributes) *obj = 0; return kNoInterface; } //------------------------------------------------------------------------ tresult PLUGIN_API NSDataIBStream::read (void* buffer, int32 numBytes, int32* numBytesRead) { int32 useBytes = std::min (numBytes, (int32)([data length] - currentPos)); if (useBytes > 0) { [data getBytes: buffer range: NSMakeRange (currentPos, useBytes)]; if (numBytesRead) *numBytesRead = useBytes; currentPos += useBytes; return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API NSDataIBStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) { return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API NSDataIBStream::seek (int64 pos, int32 mode, int64* result) { switch (mode) { case kIBSeekSet: { if (pos <= [data length]) { currentPos = pos; if (result) tell (result); return kResultTrue; } break; } case kIBSeekCur: { if (currentPos + pos <= [data length]) { currentPos += pos; if (result) tell (result); return kResultTrue; } break; } case kIBSeekEnd: { if ([data length] + pos <= [data length]) { currentPos = [data length] + pos; if (result) tell (result); return kResultTrue; } break; } } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API NSDataIBStream::tell (int64* pos) { if (pos) { *pos = currentPos; return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API NSDataIBStream::getFileName (String128 name) { return kNotImplemented; } //------------------------------------------------------------------------ IAttributeList* PLUGIN_API NSDataIBStream::getAttributes () { return attrList; } //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ NSMutableDataIBStream::NSMutableDataIBStream (NSMutableData* data) : NSDataIBStream (data, true) , mdata (data) { } //------------------------------------------------------------------------ NSMutableDataIBStream::~NSMutableDataIBStream () { [mdata setLength:currentPos]; } //------------------------------------------------------------------------ tresult PLUGIN_API NSMutableDataIBStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) { [mdata replaceBytesInRange:NSMakeRange (currentPos, numBytes) withBytes:buffer]; if (numBytesWritten) *numBytesWritten = numBytes; currentPos += numBytes; return kResultTrue; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/auwrapper_prefix.pch0000644000000000000000000000013215124701711026440 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/auwrapper_prefix.pch0000644000175000001440000000154115124701711026431 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/auwrapper_prefix.pch // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include #include qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/aucarbonview.mm0000644000000000000000000000013215124701711025401 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/aucarbonview.mm0000644000175000001440000001066015124701711025374 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/aucarbonview.mm // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #include "aucarbonview.h" #if !SMTG_PLATFORM_64 namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ AUCarbonView::AUCarbonView (AudioUnitCarbonView auv) : AUCarbonViewBase (auv) , editController (0) , plugView (0) , hiPlugView (0) { } //------------------------------------------------------------------------ AUCarbonView::~AUCarbonView () { if (plugView) { plugView->setFrame (0); plugView->removed (); plugView->release (); } } //------------------------------------------------------------------------ OSStatus AUCarbonView::HIViewAdded (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) { UInt32 eventClass = GetEventClass (inEvent); UInt32 eventKind = GetEventKind (inEvent); if (eventClass == kEventClassControl && eventKind == kEventControlAddedSubControl) { HIViewRef newControl; if (GetEventParameter (inEvent, kEventParamControlSubControl, typeControlRef, NULL, sizeof (HIViewRef) , NULL , &newControl) == noErr) { AUCarbonView* wrapper = (AUCarbonView*)inUserData; wrapper->hiPlugView = newControl; RemoveEventHandler (wrapper->eventHandler); wrapper->eventHandler = 0; } } return eventNotHandledErr; } //------------------------------------------------------------------------ OSStatus AUCarbonView::CreateUI (Float32 xoffset, Float32 yoffset) { AudioUnit unit = GetEditAudioUnit (); if (unit) { if (!editController) { UInt32 size = sizeof (IEditController*); if (AudioUnitGetProperty (unit, 64000, kAudioUnitScope_Global, 0, &editController, &size) != noErr) return kAudioUnitErr_NoConnection; } if (editController) { plugView = editController->createView (ViewType::kEditor); if (!plugView) return kAudioUnitErr_NoConnection; HIViewRef contentView; const EventTypeSpec eventTypes[] = { { kEventClassControl, kEventControlAddedSubControl }, }; OSStatus err = HIViewFindByID (HIViewGetRoot (GetCarbonWindow ()), kHIViewWindowContentID, &contentView); err = InstallControlEventHandler (contentView, HIViewAdded, 1, eventTypes, this, &eventHandler); plugView->setFrame (this); if (plugView->attached (GetCarbonWindow (), kPlatformTypeHIView) == kResultTrue) { HIViewRemoveFromSuperview (hiPlugView); EmbedControl (hiPlugView); HIViewMoveBy (hiPlugView, xoffset, yoffset); return noErr; } else plugView->setFrame (0); } } return kAudioUnitErr_NoConnection; } //------------------------------------------------------------------------ tresult PLUGIN_API AUCarbonView::resizeView (IPlugView* view, ViewRect* vr) { if (vr == 0 || view != plugView) return kInvalidArgument; HIViewRef hiView = GetCarbonPane (); if (hiView) { HIRect r; if (HIViewGetFrame (hiView, &r) != noErr) return kResultFalse; r.size.width = vr->right - vr->left; r.size.height = vr->bottom - vr->top; if (HIViewSetFrame (hiView, &r) != noErr) return kResultFalse; if (plugView) plugView->onSize (vr); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ //COMPONENT_ENTRY(AUCarbonView) //------------------------------------------------------------------------ extern "C" { ComponentResult AUCarbonViewEntry(ComponentParameters *params, AUCarbonView *obj); __attribute__ ((visibility ("default"))) ComponentResult AUCarbonViewEntry(ComponentParameters *params, AUCarbonView *obj) { return ComponentEntryPoint::Dispatch(params, obj); } } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg #endif // !SMTG_PLATFORM_64 /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/aucocoaview.h0000644000000000000000000000013215124701711025037 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/aucocoaview.h0000644000175000001440000000214215124701711025026 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/aucocoaview.h // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma once #ifndef SMTG_AUCocoaUIBase_CLASS_NAME #import "aucocoaclassprefix.h" #endif #import #import //------------------------------------------------------------------------ @interface SMTG_AUCocoaUIBase_CLASS_NAME : NSObject @end /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/aucarbonview.h0000644000000000000000000000013215124701711025217 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/aucarbonview.h0000644000175000001440000000414215124701711025210 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/aucarbonview.h // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma once #include "pluginterfaces/base/fplatform.h" #if !SMTG_PLATFORM_64 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #include "AUPublic/AUCarbonViewBase/AUCarbonViewBase.h" #pragma clang diagnostic pop #include "pluginterfaces/vst/ivsteditcontroller.h" #include "base/source/fobject.h" #include "pluginterfaces/gui/iplugview.h" namespace Steinberg { namespace Vst { class AUCarbonPlugFrame; //------------------------------------------------------------------------ class AUCarbonView : public AUCarbonViewBase, public IPlugFrame, public FObject { public: AUCarbonView (AudioUnitCarbonView auv); ~AUCarbonView (); OSStatus CreateUI (Float32 xoffset, Float32 yoffset) override; OBJ_METHODS(AUCarbonView, FObject) DEF_INTERFACES_1(IPlugFrame, FObject) REFCOUNT_METHODS(FObject) protected: tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* vr) SMTG_OVERRIDE; static OSStatus HIViewAdded (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* inUserData); IEditController* editController; AUCarbonPlugFrame* plugFrame; IPlugView* plugView; HIViewRef hiPlugView; EventHandlerRef eventHandler; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg #endif // !SMTG_PLATFORM_64 /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215124701711025121 xustar0030 mtime=1767080905.279210874 30 atime=1767080905.279210874 30 ctime=1767080905.279210874 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/CMakeLists.txt0000644000175000001440000000570115124701711025114 0ustar00rncbcusers if(SMTG_MAC) if (XCODE AND SMTG_ENABLE_AUV2_BUILDS) option(SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES "Activate only the buses that have the kDefaultActive flag set in the AUWrapper. This may not work on some hosts because they never activate a bus later." OFF ) string(RANDOM LENGTH 20 CocoaId) file(CONFIGURE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/au/aucocoaclassprefix.h" CONTENT "#define SMTG_AUCocoaUIBase_CLASS_NAME SMTG_AUCocoaUIBase_${CocoaId}" ) set(target au_wrapper) set(${target}_sources aucarbonview.mm aucarbonview.h aucocoaview.mm aucocoaview.h auwrapper.mm auwrapper.h NSDataIBStream.mm NSDataIBStream.h ) add_library(${target} STATIC ${${target}_sources} ) smtg_target_setup_universal_binary(${target}) set_target_properties(${target} PROPERTIES ${SDK_IDE_LIBS_FOLDER} ) target_compile_features(${target} PUBLIC cxx_std_17 ) if(SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES) target_compile_definitions(${target} PRIVATE SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES ) endif() target_link_libraries(${target} PRIVATE sdk_hosting "-framework AudioUnit" "-framework CoreMIDI" "-framework AudioToolbox" "-framework CoreFoundation" "-framework Carbon" "-framework Cocoa" "-framework CoreAudio" ) target_include_directories(${target} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/au/" ) if(NOT ${SMTG_COREAUDIO_SDK_PATH} STREQUAL "") target_sources(${target} PRIVATE ausdk.mm ) target_include_directories(${target} PRIVATE "${SMTG_COREAUDIO_SDK_PATH}/**" ) elseif(NOT ${SMTG_AUDIOUNIT_SDK_PATH} STREQUAL "") target_compile_definitions(${target} PRIVATE SMTG_AUWRAPPER_USES_AUSDK ) ## Adding the xcodeproj will crash Xcode when closing and reopening the cmake generated project # target_sources(${target} PRIVATE # "${SMTG_AUDIOUNIT_SDK_PATH}/AudioUnitSDK.xcodeproj" # ) target_include_directories(${target} PRIVATE "${SMTG_AUDIOUNIT_SDK_PATH}/include/**" ) target_link_libraries(${target} PRIVATE AudioUnitSDK ) else() message(${SMTG_AUDIOUNIT_SDK_PATH}) message(FATAL_ERROR "The option SMTG_ENABLE_AUV2_BUILDS is set but the audio unit SDK paths are not set") endif() else() message("[SMTG] * To enable building the AudioUnit wrapper, you need to use the Xcode generator and set SMTG_COREAUDIO_SDK_PATH to the path of your installation of the CoreAudio SDK!") endif(XCODE AND SMTG_ENABLE_AUV2_BUILDS) endif(SMTG_MAC) qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/auwrapper.h0000644000000000000000000000013215124701711024540 xustar0030 mtime=1767080905.279700628 30 atime=1767080905.279700628 30 ctime=1767080905.279700628 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/auwrapper.h0000644000175000001440000002607015124701711024535 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/auwrapper/auwrapper.h // Created by : Steinberg, 12/2007 // Description : VST 3 -> AU Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma once #ifdef SMTG_AUWRAPPER_USES_AUSDK #if CA_USE_AUDIO_PLUGIN_ONLY #include "AudioUnitSDK/AUBase.h" #define AUWRAPPER_BASE_CLASS ausdk::AUBase #else #include "AudioUnitSDK/MusicDeviceBase.h" #define AUWRAPPER_BASE_CLASS ausdk::MusicDeviceBase #endif // CA_USE_AUDIO_PLUGIN_ONLY #else #if CA_USE_AUDIO_PLUGIN_ONLY #include "AudioUnits/AUPublic/AUBase/AUBase.h" #define AUWRAPPER_BASE_CLASS AUBase #else #include "AudioUnits/AUPublic/OtherBases/MusicDeviceBase.h" #define AUWRAPPER_BASE_CLASS MusicDeviceBase #endif // CA_USE_AUDIO_PLUGIN_ONLY #endif // SMTG_AUWRAPPER_USES_AUSDK #include "public.sdk/source/vst/hosting/eventlist.h" #include "public.sdk/source/vst/hosting/parameterchanges.h" #include "public.sdk/source/vst/hosting/processdata.h" #include "public.sdk/source/vst/utility/ringbuffer.h" #include "public.sdk/source/vst/utility/rttransfer.h" #include "base/source/fstring.h" #include "base/source/timer.h" #include "base/thread/include/flock.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstmidilearn.h" #include "pluginterfaces/vst/ivstprocesscontext.h" #include "pluginterfaces/vst/ivstunits.h" #include #include #include #include #include #include namespace Steinberg { class VST3DynLibrary; namespace Vst { //------------------------------------------------------------------------ //------------------------------------------------------------------------ class AUWrapper : public AUWRAPPER_BASE_CLASS, public IComponentHandler, public ITimerCallback { public: #ifdef SMTG_AUWRAPPER_USES_AUSDK using AUElement = ausdk::AUElement; #else using AudioStreamBasicDescription = CAStreamBasicDescription; #endif AUWrapper (ComponentInstanceRecord* ci); ~AUWrapper (); //---ComponentBase--------------------- #if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK) ComponentResult Version () SMTG_OVERRIDE; #endif void PostConstructor () SMTG_OVERRIDE; //---AUBase----------------------------- void Cleanup () SMTG_OVERRIDE; ComponentResult Initialize () SMTG_OVERRIDE; #ifdef SMTG_AUWRAPPER_USES_AUSDK std::unique_ptr CreateElement (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE; #else AUElement* CreateElement (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE; #endif UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) SMTG_OVERRIDE; bool StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE; ComponentResult ChangeStreamFormat (AudioUnitScope inScope, AudioUnitElement inElement, const AudioStreamBasicDescription& inPrevFormat, const AudioStreamBasicDescription& inNewFormat) SMTG_OVERRIDE; ComponentResult SetConnection (const AudioUnitConnection& inConnection) SMTG_OVERRIDE; ComponentResult GetParameterInfo (AudioUnitScope inScope, AudioUnitParameterID inParameterID, AudioUnitParameterInfo& outParameterInfo) SMTG_OVERRIDE; ComponentResult SetParameter (AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames) SMTG_OVERRIDE; ComponentResult SaveState (CFPropertyListRef* outData) SMTG_OVERRIDE; ComponentResult RestoreState (CFPropertyListRef inData) SMTG_OVERRIDE; ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags, const AudioTimeStamp &inTimeStamp, UInt32 inNumberFrames) SMTG_OVERRIDE; void processOutputEvents (const AudioTimeStamp &inTimeStamp); #if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK) int GetNumCustomUIComponents () SMTG_OVERRIDE; void GetUIComponentDescs (ComponentDescription* inDescArray) SMTG_OVERRIDE; #endif #ifdef SMTG_AUWRAPPER_USES_AUSDK OSStatus GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 &outDataSize, bool &outWritable) SMTG_OVERRIDE; #else OSStatus GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 &outDataSize, Boolean &outWritable) SMTG_OVERRIDE; #endif ComponentResult GetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData) SMTG_OVERRIDE; ComponentResult SetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize) SMTG_OVERRIDE; bool CanScheduleParameters() const SMTG_OVERRIDE; Float64 GetLatency () SMTG_OVERRIDE; Float64 GetTailTime () SMTG_OVERRIDE; //---Factory presets OSStatus GetPresets (CFArrayRef* outData) const SMTG_OVERRIDE; OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) SMTG_OVERRIDE; #if !CA_USE_AUDIO_PLUGIN_ONLY //---MusicDeviceBase------------------------- OSStatus HandleNoteOn (UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) SMTG_OVERRIDE; OSStatus HandleNoteOff (UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) SMTG_OVERRIDE; ComponentResult StartNote (MusicDeviceInstrumentID inInstrument, MusicDeviceGroupID inGroupID, NoteInstanceID* outNoteInstanceID, UInt32 inOffsetSampleFrame, const MusicDeviceNoteParams &inParams) SMTG_OVERRIDE; ComponentResult StopNote (MusicDeviceGroupID inGroupID, NoteInstanceID inNoteInstanceID, UInt32 inOffsetSampleFrame) SMTG_OVERRIDE; OSStatus GetInstrumentCount (UInt32 &outInstCount) const SMTG_OVERRIDE; //---AUMIDIBase------------------------------ OSStatus HandleNonNoteEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame) SMTG_OVERRIDE; #endif #if AUSDK_MIDI2_AVAILABLE OSStatus MIDIEventList (UInt32 inOffsetSampleFrame, const struct MIDIEventList* eventList) override; bool handleMIDIEventPacket (UInt32 inOffsetSampleFrame, const MIDIEventPacket* packet); #endif //---custom---------------------------------- void setControllerParameter (ParamID pid, ParamValue value); // return for a given midiChannel the unitID and the ProgramListID bool getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId); // restore preset state, add StateType "Project" to stream if loading from project ComponentResult restoreState (CFPropertyListRef inData, bool fromProject); //------------------------------------------------------------------------ #if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK) static ComponentResult ComponentEntryDispatch (ComponentParameters* params, AUWrapper* This); #endif //------------------------------------------------------------------------ static CFBundleRef gBundleRef; //------------------------------------------------------------------------ DECLARE_FUNKNOWN_METHODS protected: //---from IComponentHandler------------------- tresult PLUGIN_API beginEdit (ParamID tag) SMTG_OVERRIDE; tresult PLUGIN_API performEdit (ParamID tag, ParamValue valueNormalized) SMTG_OVERRIDE; tresult PLUGIN_API endEdit (ParamID tag) SMTG_OVERRIDE; tresult PLUGIN_API restartComponent (int32 flags) SMTG_OVERRIDE; //---from ITimerCallback---------------------- void onTimer (Timer* timer) SMTG_OVERRIDE; // internal helpers double getSampleRate () const { return sampleRate; } void updateProcessContext (); void syncParameterValues (); void cacheParameterValues (); void clearParameterValueCache (); void updateProgramChangesCache (); virtual IPluginFactory* getFactory (); void loadVST3Module (); void unloadVST3Module (); bool validateChannelPair (int inChannelsIn, int inChannelsOut, const AUChannelInfo* info, UInt32 numChanInfo) const; IAudioProcessor* audioProcessor; IEditController* editController; Timer* timer; HostProcessData processData; ParameterChanges processParamChanges; ParameterChanges outputParamChanges; ParameterChangeTransfer transferParamChanges; ParameterChangeTransfer outputParamTransfer; ProcessContext processContext; EventList eventList; typedef std::map CachedParameterInfoMap; typedef std::map UnitInfoMap; typedef std::vector ClumpGroupVector; UnitInfoMap unitInfos; ClumpGroupVector clumpGroups; CachedParameterInfoMap cachedParameterInfos; Steinberg::Base::Thread::FLock parameterCacheChanging; NoteInstanceID noteCounter; double sampleRate; ParamID bypassParamID; AUPreset* presets; int32 numPresets; ParamID factoryProgramChangedID; AUParameterListenerRef paramListenerRef; std::vector programParameters; static constexpr int32 kMaxProgramChangeParameters = 16; struct ProgramChangeInfo { ParamID pid {kNoParamId}; int32 numPrograms {0}; }; using ProgramChangeInfoList = std::array; using ProgramChangeInfoTransfer = RTTransferT; ProgramChangeInfoList programChangeInfos; ProgramChangeInfoTransfer programChangeInfoTransfer; // midi mapping struct MidiMapping { using CC2ParamMap = std::unordered_map; using ChannelList = std::vector; using BusList = std::vector; BusList busList; bool empty () const { return busList.empty () || busList[0].empty (); } }; using MidiMappingTransfer = RTTransferT; MidiMappingTransfer midiMappingTransfer; MidiMapping midiMappingCache; struct MidiLearnEvent { int32 busIndex; int16 channel; CtrlNumber midiCC; }; using MidiLearnRingBuffer = OneReaderOneWriter::RingBuffer; MidiLearnRingBuffer midiLearnRingBuffer; IPtr midiLearn; struct MIDIOutputCallbackHelper; int32 midiOutCount; // currently only 0 or 1 supported std::unique_ptr mCallbackHelper; EventList outputEvents; bool isInstrument; bool isBypassed; bool isOfflineRender; private: void buildUnitInfos (IUnitInfo* unitInfoController, UnitInfoMap& units) const; void updateMidiMappingCache (); IPtr dynLib; }; //------------------------------------------------------------------------ class AutoreleasePool { public: AutoreleasePool () { ap = [[NSAutoreleasePool alloc] init]; } ~AutoreleasePool () { [ap drain]; } //------------------------------------------------------------------------ protected: NSAutoreleasePool* ap; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/PaxHeaders/usediids.cpp0000644000000000000000000000013215124701711024676 xustar0030 mtime=1767080905.280506322 30 atime=1767080905.280506322 30 ctime=1767080905.280506322 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auwrapper/usediids.cpp0000644000175000001440000000433015124701711024666 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Validator // Filename : usediids.cpp // Created by : Steinberg 09.2008 // Description : Interface symbols file // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- //#define INIT_CLASS_IID // This macro definition modifies the behavior of DECLARE_CLASS_IID (funknown.h) // and produces the actual symbols for all interface identifiers. // It must be defined before including the interface headers and // in only one source file! //------------------------------------------------------------------------ //#define INIT_CLASS_IID #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivsthostapplication.h" #include "pluginterfaces/vst/ivstmidilearn.h" #include "pluginterfaces/vst/ivstparameterchanges.h" #include "pluginterfaces/vst/ivstpluginterfacesupport.h" #include "pluginterfaces/vst/ivstevents.h" #include "pluginterfaces/vst/ivstunits.h" namespace Steinberg { DEF_CLASS_IID (Vst::IAttributeList) DEF_CLASS_IID (Vst::IAudioProcessor) DEF_CLASS_IID (Vst::IEditController) DEF_CLASS_IID (Vst::IEditController2) DEF_CLASS_IID (Vst::IComponent) DEF_CLASS_IID (Vst::IComponentHandler) DEF_CLASS_IID (Vst::IConnectionPoint) DEF_CLASS_IID (Vst::IEventList) DEF_CLASS_IID (Vst::IHostApplication) DEF_CLASS_IID (Vst::IMessage) DEF_CLASS_IID (Vst::IMidiLearn) DEF_CLASS_IID (Vst::IMidiMapping) DEF_CLASS_IID (Vst::IParameterChanges) DEF_CLASS_IID (Vst::IParamValueQueue) DEF_CLASS_IID (Vst::IPlugInterfaceSupport) DEF_CLASS_IID (Vst::IProgramListData) DEF_CLASS_IID (Vst::IStreamAttributes) DEF_CLASS_IID (Vst::IVst3ToAUWrapper) DEF_CLASS_IID (Vst::IUnitData) DEF_CLASS_IID (Vst::IUnitInfo) } qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstaudioeffect.cpp0000644000000000000000000000013215124701711024072 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstaudioeffect.cpp0000644000175000001440000001474615124701711024076 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstaudioeffect.cpp // Created by : Steinberg, 04/2005 // Description : Basic Audio Effect Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstaudioeffect.h" namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // AudioEffect //------------------------------------------------------------------------ AudioEffect::AudioEffect () { processSetup.maxSamplesPerBlock = 1024; processSetup.processMode = Vst::kRealtime; processSetup.sampleRate = 44100.0; processSetup.symbolicSampleSize = Vst::kSample32; } //------------------------------------------------------------------------ AudioBus* AudioEffect::addAudioInput (const TChar* name, SpeakerArrangement arr, BusType busType, int32 flags) { auto* newBus = new AudioBus (name, busType, flags, arr); audioInputs.push_back (IPtr (newBus, false)); return newBus; } //------------------------------------------------------------------------ AudioBus* AudioEffect::addAudioOutput (const TChar* name, SpeakerArrangement arr, BusType busType, int32 flags) { auto* newBus = new AudioBus (name, busType, flags, arr); audioOutputs.push_back (IPtr (newBus, false)); return newBus; } //------------------------------------------------------------------------ AudioBus* AudioEffect::getAudioInput (int32 index) { AudioBus* bus = nullptr; if (index < static_cast (audioInputs.size ())) bus = FCast (audioInputs.at (index)); return bus; } //------------------------------------------------------------------------ AudioBus* AudioEffect::getAudioOutput (int32 index) { AudioBus* bus = nullptr; if (index < static_cast (audioOutputs.size ())) bus = FCast (audioOutputs.at (index)); return bus; } //------------------------------------------------------------------------ EventBus* AudioEffect::addEventInput (const TChar* name, int32 channels, BusType busType, int32 flags) { auto* newBus = new EventBus (name, busType, flags, channels); eventInputs.push_back (IPtr (newBus, false)); return newBus; } //------------------------------------------------------------------------ EventBus* AudioEffect::addEventOutput (const TChar* name, int32 channels, BusType busType, int32 flags) { auto* newBus = new EventBus (name, busType, flags, channels); eventOutputs.push_back (IPtr (newBus, false)); return newBus; } //------------------------------------------------------------------------ EventBus* AudioEffect::getEventInput (int32 index) { EventBus* bus = nullptr; if (index < static_cast (eventInputs.size ())) bus = FCast (eventInputs.at (index)); return bus; } //------------------------------------------------------------------------ EventBus* AudioEffect::getEventOutput (int32 index) { EventBus* bus = nullptr; if (index < static_cast (eventOutputs.size ())) bus = FCast (eventOutputs.at (index)); return bus; } //------------------------------------------------------------------------ tresult PLUGIN_API AudioEffect::setBusArrangements (SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts) { if (numIns < 0 || numOuts < 0) return kInvalidArgument; if (numIns > static_cast (audioInputs.size ()) || numOuts > static_cast (audioOutputs.size ())) return kResultFalse; for (int32 index = 0; index < static_cast (audioInputs.size ()); ++index) { if (index >= numIns) break; FCast (audioInputs[index].get ())->setArrangement (inputs[index]); } for (int32 index = 0; index < static_cast (audioOutputs.size ()); ++index) { if (index >= numOuts) break; FCast (audioOutputs[index].get ())->setArrangement (outputs[index]); } return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API AudioEffect::getBusArrangement (BusDirection dir, int32 busIndex, SpeakerArrangement& arr) { BusList* busList = getBusList (kAudio, dir); if (!busList || busIndex < 0 || static_cast (busList->size ()) <= busIndex) return kInvalidArgument; if (auto* audioBus = FCast (busList->at (busIndex))) { arr = audioBus->getArrangement (); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API AudioEffect::setupProcessing (ProcessSetup& newSetup) { processSetup.maxSamplesPerBlock = newSetup.maxSamplesPerBlock; processSetup.processMode = newSetup.processMode; processSetup.sampleRate = newSetup.sampleRate; if (canProcessSampleSize (newSetup.symbolicSampleSize) != kResultTrue) return kResultFalse; processSetup.symbolicSampleSize = newSetup.symbolicSampleSize; return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API AudioEffect::setProcessing (TBool /*state*/) { return kNotImplemented; } //------------------------------------------------------------------------ tresult PLUGIN_API AudioEffect::canProcessSampleSize (int32 symbolicSampleSize) { return symbolicSampleSize == kSample32 ? kResultTrue : kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API AudioEffect::process (ProcessData& /*data*/) { return kNotImplemented; } //------------------------------------------------------------------------ uint32 PLUGIN_API AudioEffect::getProcessContextRequirements () { return processContextRequirements.flags; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstcomponent.h0000644000000000000000000000013215124701711023263 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstcomponent.h0000644000175000001440000000762515124701711023265 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstcomponent.h // Created by : Steinberg, 04/2005 // Description : Basic VST Plug-in Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/vstcomponentbase.h" #include "public.sdk/source/vst/vstbus.h" #include "pluginterfaces/vst/ivstcomponent.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Default implementation for a VST 3 Component. \ingroup vstClasses Can be used as base class for a VST 3 component implementation. */ class Component : public ComponentBase, public IComponent { public: //------------------------------------------------------------------------ /** Constructor */ Component (); //---Internal Methods------- /** Sets the controller Class ID associated to its component. */ void setControllerClass (const FUID& cid) { controllerClass = cid; } void setControllerClass (const TUID& cid) { controllerClass = FUID::fromTUID (cid); } /** Removes all Audio Busses. */ tresult removeAudioBusses (); /** Removes all Event Busses. */ tresult removeEventBusses (); /** Renames a specific bus. Do not forget to inform the host about this (see \ref * IComponentHandler::restartComponent (kIoTitlesChanged)). */ tresult renameBus (MediaType type, BusDirection dir, int32 index, const String128 newName); //---from IComponent-------- tresult PLUGIN_API getControllerClassId (TUID classID) SMTG_OVERRIDE; tresult PLUGIN_API setIoMode (IoMode mode) SMTG_OVERRIDE; int32 PLUGIN_API getBusCount (MediaType type, BusDirection dir) SMTG_OVERRIDE; tresult PLUGIN_API getBusInfo (MediaType type, BusDirection dir, int32 index, BusInfo& info) SMTG_OVERRIDE; tresult PLUGIN_API getRoutingInfo (RoutingInfo& inInfo, RoutingInfo& outInfo) SMTG_OVERRIDE; tresult PLUGIN_API activateBus (MediaType type, BusDirection dir, int32 index, TBool state) SMTG_OVERRIDE; tresult PLUGIN_API setActive (TBool state) SMTG_OVERRIDE; tresult PLUGIN_API setState (IBStream* state) SMTG_OVERRIDE; tresult PLUGIN_API getState (IBStream* state) SMTG_OVERRIDE; //---from ComponentBase------ tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE; tresult PLUGIN_API terminate () SMTG_OVERRIDE; //---Interface--------- OBJ_METHODS (Component, ComponentBase) DEFINE_INTERFACES DEF_INTERFACE (IComponent) END_DEFINE_INTERFACES (ComponentBase) REFCOUNT_METHODS (ComponentBase) //------------------------------------------------------------------------ protected: FUID controllerClass; BusList audioInputs; BusList audioOutputs; BusList eventInputs; BusList eventOutputs; BusList* getBusList (MediaType type, BusDirection dir); tresult removeAllBusses (); }; //------------------------------------------------------------------------ // some Helper functions.... //------------------------------------------------------------------------ /** Gets the channel index of a given speaker in a arrangement, returns kResultFalse if speaker not * part of the arrangement else returns kResultTrue. */ tresult getSpeakerChannelIndex (SpeakerArrangement arrangement, uint64 speaker, int32& channel); //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/hosting0000644000000000000000000000013115124701711021750 xustar0030 mtime=1767080905.281210866 29 atime=1767080905.28072064 30 ctime=1767080905.281210866 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/0000755000175000001440000000000015124701711022016 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/hostclasses.h0000644000000000000000000000013215124701711024532 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/hostclasses.h0000644000175000001440000000654115124701711024530 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/hostclasses.h // Created by : Steinberg, 03/05/2008. // Description : VST 3 hostclasses, example impl. for IHostApplication, IAttributeList and IMessage // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/hosting/pluginterfacesupport.h" #include "pluginterfaces/vst/ivsthostapplication.h" #include #include #include namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Implementation's example of IHostApplication. \ingroup hostingBase */ class HostApplication : public IHostApplication { public: HostApplication (); virtual ~HostApplication () noexcept {FUNKNOWN_DTOR} //--- IHostApplication --------------- tresult PLUGIN_API getName (String128 name) override; tresult PLUGIN_API createInstance (TUID cid, TUID _iid, void** obj) override; DECLARE_FUNKNOWN_METHODS PlugInterfaceSupport* getPlugInterfaceSupport () const { return mPlugInterfaceSupport; } private: IPtr mPlugInterfaceSupport; }; //------------------------------------------------------------------------ /** Example, ready to use implementation of IAttributeList. \ingroup hostingBase */ class HostAttributeList final : public IAttributeList { public: /** make a new attribute list instance */ static IPtr make (); tresult PLUGIN_API setInt (AttrID aid, int64 value) override; tresult PLUGIN_API getInt (AttrID aid, int64& value) override; tresult PLUGIN_API setFloat (AttrID aid, double value) override; tresult PLUGIN_API getFloat (AttrID aid, double& value) override; tresult PLUGIN_API setString (AttrID aid, const TChar* string) override; tresult PLUGIN_API getString (AttrID aid, TChar* string, uint32 sizeInBytes) override; tresult PLUGIN_API setBinary (AttrID aid, const void* data, uint32 sizeInBytes) override; tresult PLUGIN_API getBinary (AttrID aid, const void*& data, uint32& sizeInBytes) override; virtual ~HostAttributeList () noexcept; DECLARE_FUNKNOWN_METHODS private: HostAttributeList (); struct Attribute; std::map list; }; //------------------------------------------------------------------------ /** Example implementation of IMessage. \ingroup hostingBase */ class HostMessage final : public IMessage { public: HostMessage (); virtual ~HostMessage () noexcept; const char* PLUGIN_API getMessageID () override; void PLUGIN_API setMessageID (const char* messageID) override; IAttributeList* PLUGIN_API getAttributes () override; DECLARE_FUNKNOWN_METHODS private: char* messageId {nullptr}; IPtr attributeList; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/pluginterfacesupport.cpp0000644000000000000000000000013215124701711027017 xustar0030 mtime=1767080905.281210866 30 atime=1767080905.281210866 30 ctime=1767080905.281210866 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/pluginterfacesupport.cpp0000644000175000001440000001050115124701711027004 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/pluginterfacesupport.cpp // Created by : Steinberg, 11/2018. // Description : VST 3 hostclasses, example implementations for IPlugInterfaceSupport // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginterfacesupport.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstunits.h" #include "pluginterfaces/vst/ivstmessage.h" #include //----------------------------------------------------------------------------- namespace Steinberg { namespace Vst { //----------------------------------------------------------------------------- PlugInterfaceSupport::PlugInterfaceSupport () { FUNKNOWN_CTOR // add minimum set //---VST 3.0.0-------------------------------- addPlugInterfaceSupported (IComponent::iid); addPlugInterfaceSupported (IAudioProcessor::iid); addPlugInterfaceSupported (IEditController::iid); addPlugInterfaceSupported (IConnectionPoint::iid); addPlugInterfaceSupported (IUnitInfo::iid); addPlugInterfaceSupported (IUnitData::iid); addPlugInterfaceSupported (IProgramListData::iid); //---VST 3.0.1-------------------------------- addPlugInterfaceSupported (IMidiMapping::iid); //---VST 3.1---------------------------------- addPlugInterfaceSupported (IEditController2::iid); /* //---VST 3.0.2-------------------------------- addPlugInterfaceSupported (IParameterFinder::iid); //---VST 3.1---------------------------------- addPlugInterfaceSupported (IAudioPresentationLatency::iid); //---VST 3.5---------------------------------- addPlugInterfaceSupported (IKeyswitchController::iid); addPlugInterfaceSupported (IContextMenuTarget::iid); addPlugInterfaceSupported (IEditControllerHostEditing::iid); addPlugInterfaceSupported (IXmlRepresentationController::iid); addPlugInterfaceSupported (INoteExpressionController::iid); //---VST 3.6.5-------------------------------- addPlugInterfaceSupported (ChannelContext::IInfoListener::iid); addPlugInterfaceSupported (IPrefetchableSupport::iid); addPlugInterfaceSupported (IAutomationState::iid); //---VST 3.6.11-------------------------------- addPlugInterfaceSupported (INoteExpressionPhysicalUIMapping::iid); //---VST 3.6.12-------------------------------- addPlugInterfaceSupported (IMidiLearn::iid); //---VST 3.7----------------------------------- addPlugInterfaceSupported (IProcessContextRequirements::iid); addPlugInterfaceSupported (IParameterFunctionName::iid); addPlugInterfaceSupported (IProgress::iid); //----VST 3.8------------------------------------ addPlugInterfaceSupported (IMidiMapping2::iid) addPlugInterfaceSupported (IMidiLearn2::iid) */ } //----------------------------------------------------------------------------- tresult PLUGIN_API PlugInterfaceSupport::isPlugInterfaceSupported (const TUID _iid) { auto uid = FUID::fromTUID (_iid); if (std::find (mFUIDArray.begin (), mFUIDArray.end (), uid) != mFUIDArray.end ()) return kResultTrue; return kResultFalse; } //----------------------------------------------------------------------------- void PlugInterfaceSupport::addPlugInterfaceSupported (const TUID _iid) { mFUIDArray.push_back (FUID::fromTUID (_iid)); } //----------------------------------------------------------------------------- bool PlugInterfaceSupport::removePlugInterfaceSupported (const TUID _iid) { auto uid = FUID::fromTUID (_iid); auto it = std::find (mFUIDArray.begin (), mFUIDArray.end (), uid); if (it == mFUIDArray.end ()) return false; mFUIDArray.erase (it); return true; } IMPLEMENT_FUNKNOWN_METHODS (PlugInterfaceSupport, IPlugInterfaceSupport, IPlugInterfaceSupport::iid) //----------------------------------------------------------------------------- } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/connectionproxy.h0000644000000000000000000000013115124701711025437 xustar0030 mtime=1767080905.280856296 29 atime=1767080905.28072064 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/connectionproxy.h0000644000175000001440000000345615124701711025440 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/connectionproxy.h // Created by : Steinberg, 04/2020 // Description : VST 3 Plug-in connection class // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstmessage.h" #include "public.sdk/source/common/threadchecker.h" namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Helper for creating and initializing component. \ingroup Helper */ //------------------------------------------------------------------------ class ConnectionProxy : public IConnectionPoint { public: ConnectionProxy (IConnectionPoint* srcConnection); virtual ~ConnectionProxy (); //--- from IConnectionPoint tresult PLUGIN_API connect (IConnectionPoint* other) override; tresult PLUGIN_API disconnect (IConnectionPoint* other) override; tresult PLUGIN_API notify (IMessage* message) override; bool disconnect (); //------------------------------------------------------------------------ DECLARE_FUNKNOWN_METHODS protected: std::unique_ptr threadChecker {ThreadChecker::create ()}; IPtr srcConnection; IPtr dstConnection; }; } } // namespaces qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/module_linux.cpp0000644000000000000000000000013215124701711025236 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/module_linux.cpp0000644000175000001440000002376515124701711025243 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/module_linux.cpp // Created by : Steinberg, 08/2016 // Description : hosting module classes (linux implementation) // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "module.h" #include "public.sdk/source/vst/utility/optional.h" #include "public.sdk/source/vst/utility/stringconvert.h" #include "pluginterfaces/base/funknownimpl.h" #include #include #include #include #include #if SMTG_CPP17 #if __has_include() #define USE_EXPERIMENTAL_FS 0 #elif __has_include() #define USE_EXPERIMENTAL_FS 1 #endif #else // !SMTG_CPP17 #define USE_EXPERIMENTAL_FS 1 #endif // SMTG_CPP17 #if USE_EXPERIMENTAL_FS == 1 #include namespace filesystem = std::experimental::filesystem; #else // USE_EXPERIMENTAL_FS == 0 #include namespace filesystem = std::filesystem; #endif // USE_EXPERIMENTAL_FS //------------------------------------------------------------------------ extern "C" { using ModuleEntryFunc = bool (PLUGIN_API*) (void*); using ModuleExitFunc = bool (PLUGIN_API*) (); } //------------------------------------------------------------------------ namespace VST3 { namespace Hosting { using Path = filesystem::path; //------------------------------------------------------------------------ namespace { //------------------------------------------------------------------------ Optional getCurrentMachineName () { struct utsname unameData; int res = uname (&unameData); if (res != 0) return {}; return {unameData.machine}; } //------------------------------------------------------------------------ Optional getApplicationPath () { std::string appPath = ""; pid_t pid = getpid (); char buf[10]; sprintf (buf, "%d", pid); std::string _link = "/proc/"; _link.append (buf); _link.append ("/exe"); char proc[1024]; int ch = readlink (_link.c_str (), proc, 1024); if (ch == -1) return {}; proc[ch] = 0; appPath = proc; std::string::size_type t = appPath.find_last_of ("/"); appPath = appPath.substr (0, t); return Path {appPath}; } //------------------------------------------------------------------------ class LinuxModule : public Module { public: template T getFunctionPointer (const char* name) { return reinterpret_cast (dlsym (mModule, name)); } ~LinuxModule () override { factory = PluginFactory (nullptr); if (mModule) { if (auto moduleExit = getFunctionPointer ("ModuleExit")) moduleExit (); dlclose (mModule); } } static Optional getSOPath (const std::string& inPath) { Path modulePath {inPath}; if (!filesystem::is_directory (modulePath)) return {}; auto stem = modulePath.stem (); modulePath /= "Contents"; if (!filesystem::is_directory (modulePath)) return {}; // use the Machine Hardware Name (from uname cmd-line) as prefix for "-linux" auto machine = getCurrentMachineName (); if (!machine) return {}; modulePath /= *machine + "-linux"; if (!filesystem::is_directory (modulePath)) return {}; modulePath /= stem; modulePath += ".so"; return Optional (std::move (modulePath)); } bool load (const std::string& inPath, std::string& errorDescription) override { auto modulePath = getSOPath (inPath); if (!modulePath) { errorDescription = inPath + " is not a module directory."; return false; } mModule = dlopen (reinterpret_cast (modulePath->generic_string ().data ()), RTLD_LAZY); if (!mModule) { errorDescription = "dlopen failed.\n"; errorDescription += dlerror (); return false; } // ModuleEntry is mandatory auto moduleEntry = getFunctionPointer ("ModuleEntry"); if (!moduleEntry) { errorDescription = "The shared library does not export the required 'ModuleEntry' function"; return false; } // ModuleExit is mandatory auto moduleExit = getFunctionPointer ("ModuleExit"); if (!moduleExit) { errorDescription = "The shared library does not export the required 'ModuleExit' function"; return false; } auto factoryProc = getFunctionPointer ("GetPluginFactory"); if (!factoryProc) { errorDescription = "The shared library does not export the required 'GetPluginFactory' function"; return false; } if (!moduleEntry (mModule)) { errorDescription = "Calling 'ModuleEntry' failed"; return false; } auto f = Steinberg::U::cast (owned (factoryProc ())); if (!f) { errorDescription = "Calling 'GetPluginFactory' returned nullptr"; return false; } factory = PluginFactory (f); return true; } void* mModule {nullptr}; }; //------------------------------------------------------------------------ void findFilesWithExt (const std::string& path, const std::string& ext, Module::PathList& pathList, bool recursive = true) { try { for (auto& p : filesystem::directory_iterator (path)) { if (p.path ().extension () == ext) { pathList.push_back (p.path ().generic_string ()); } else if (recursive && p.status ().type () == filesystem::file_type::directory) { findFilesWithExt (p.path (), ext, pathList); } } } catch (...) { } } //------------------------------------------------------------------------ void findModules (const std::string& path, Module::PathList& pathList) { findFilesWithExt (path, ".vst3", pathList); } //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ Module::Ptr Module::create (const std::string& path, std::string& errorDescription) { auto _module = std::make_shared (); if (_module->load (path, errorDescription)) { _module->path = path; auto it = std::find_if (path.rbegin (), path.rend (), [] (const std::string::value_type& c) { return c == '/'; }); if (it != path.rend ()) _module->name = {it.base (), path.end ()}; return _module; } return nullptr; } //------------------------------------------------------------------------ Module::PathList Module::getModulePaths () { /* VST3 component locations on linux : * User privately installed : $HOME/.vst3/ * Distribution installed : /usr/lib/vst3/ * Locally installed : /usr/local/lib/vst3/ * Application : /$APPFOLDER/vst3/ */ const auto systemPaths = {"/usr/lib/vst3/", "/usr/local/lib/vst3/"}; PathList list; if (auto homeDir = getenv ("HOME")) { filesystem::path homePath (homeDir); homePath /= ".vst3"; findModules (homePath.generic_string (), list); } for (auto path : systemPaths) findModules (path, list); // application level auto appPath = getApplicationPath (); if (appPath) { *appPath /= "vst3"; findModules (appPath->generic_string (), list); } return list; } //------------------------------------------------------------------------ Module::SnapshotList Module::getSnapshots (const std::string& modulePath) { SnapshotList result; filesystem::path path (modulePath); path /= "Contents"; path /= "Resources"; path /= "Snapshots"; PathList pngList; findFilesWithExt (path, ".png", pngList, false); for (auto& png : pngList) { filesystem::path p (png); auto filename = p.filename ().generic_string (); auto uid = Snapshot::decodeUID (filename); if (!uid) continue; auto scaleFactor = 1.; if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename)) scaleFactor = *decodedScaleFactor; Module::Snapshot::ImageDesc desc; desc.scaleFactor = scaleFactor; desc.path = std::move (png); bool found = false; for (auto& entry : result) { if (entry.uid != *uid) continue; found = true; entry.images.emplace_back (std::move (desc)); break; } if (found) continue; Module::Snapshot snapshot; snapshot.uid = *uid; snapshot.images.emplace_back (std::move (desc)); result.emplace_back (std::move (snapshot)); } return result; } //------------------------------------------------------------------------ Optional Module::getModuleInfoPath (const std::string& modulePath) { filesystem::path path (modulePath); path /= "Contents"; path /= "Resources"; path /= "moduleinfo.json"; if (filesystem::exists (path)) return {path.generic_string ()}; return {}; } //------------------------------------------------------------------------ bool Module::validateBundleStructure (const std::string& modulePath, std::string& errorDescription) { filesystem::path path (modulePath); auto moduleName = path.filename (); path /= "Contents"; if (filesystem::exists (path) == false) { errorDescription = "Expecting 'Contents' as first subfolder."; return false; } auto machine = getCurrentMachineName (); if (!machine) { errorDescription = "Could not get the current machine name."; return false; } path /= *machine + "-linux"; if (filesystem::exists (path) == false) { errorDescription = "Expecting '" + *machine + "-linux' as architecture subfolder."; return false; } moduleName.replace_extension (".so"); path /= moduleName; if (filesystem::exists (path) == false) { errorDescription = "Shared library name is not equal to bundle folder name. Must be '" + moduleName.string () + "'."; return false; } return true; } //------------------------------------------------------------------------ } // Hosting } // VST3 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/module_mac.mm0000644000000000000000000000013215124701711024466 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/module_mac.mm0000644000175000001440000002666515124701711024475 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/module_mac.mm // Created by : Steinberg, 08/2016 // Description : hosting module classes (macOS implementation) // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "module.h" #import #import #if !__has_feature(objc_arc) #error this file needs to be compiled with automatic reference counting enabled #endif //------------------------------------------------------------------------ extern "C" { typedef bool (*BundleEntryFunc) (CFBundleRef); typedef bool (*BundleExitFunc) (); } //------------------------------------------------------------------------ namespace VST3 { namespace Hosting { //------------------------------------------------------------------------ namespace { //------------------------------------------------------------------------ template class CFPtr { public: inline CFPtr (const T& obj = nullptr) : obj (obj) {} inline CFPtr (CFPtr&& other) { *this = other; } inline ~CFPtr () { if (obj) CFRelease (obj); } inline CFPtr& operator= (CFPtr&& other) { obj = other.obj; other.obj = nullptr; return *this; } inline CFPtr& operator= (const T& o) { if (obj) CFRelease (obj); obj = o; return *this; } inline operator T () const { return obj; } // act as T private: CFPtr (const CFPtr& other) = delete; CFPtr& operator= (const CFPtr& other) = delete; T obj = nullptr; }; //------------------------------------------------------------------------ class MacModule : public Module { public: template T getFunctionPointer (const char* name) { assert (bundle); CFPtr functionName ( CFStringCreateWithCString (kCFAllocatorDefault, name, kCFStringEncodingASCII)); return reinterpret_cast (CFBundleGetFunctionPointerForName (bundle, functionName)); } bool loadInternal (const std::string& path, std::string& errorDescription) { CFPtr url (CFURLCreateFromFileSystemRepresentation ( kCFAllocatorDefault, reinterpret_cast (path.data ()), path.length (), true)); if (!url) return false; bundle = CFBundleCreate (kCFAllocatorDefault, url); CFErrorRef error = nullptr; if (!bundle || !CFBundleLoadExecutableAndReturnError (bundle, &error)) { if (error) { CFPtr errorString (CFErrorCopyDescription (error)); if (errorString) { auto stringLength = CFStringGetLength (errorString); auto maxSize = CFStringGetMaximumSizeForEncoding (stringLength, kCFStringEncodingUTF8); auto buffer = std::make_unique (maxSize); if (CFStringGetCString (errorString, buffer.get (), maxSize, kCFStringEncodingUTF8)) errorDescription = buffer.get (); CFRelease (error); } } else { errorDescription = "Could not create Bundle for path: " + path; } return false; } // bundleEntry is mandatory auto bundleEntry = getFunctionPointer ("bundleEntry"); if (!bundleEntry) { errorDescription = "Bundle does not export the required 'bundleEntry' function"; return false; } // bundleExit is mandatory auto bundleExit = getFunctionPointer ("bundleExit"); if (!bundleExit) { errorDescription = "Bundle does not export the required 'bundleExit' function"; return false; } auto factoryProc = getFunctionPointer ("GetPluginFactory"); if (!factoryProc) { errorDescription = "Bundle does not export the required 'GetPluginFactory' function"; return false; } if (!bundleEntry (bundle)) { errorDescription = "Calling 'bundleEntry' failed"; return false; } auto f = owned (factoryProc ()); if (!f) { errorDescription = "Calling 'GetPluginFactory' returned nullptr"; return false; } factory = PluginFactory (f); return true; } bool load (const std::string& path, std::string& errorDescription) override { if (!path.empty () && path[0] != '/') { auto buffer = std::make_unique (PATH_MAX); auto workDir = getcwd (buffer.get (), PATH_MAX); if (workDir) { std::string wd (workDir); wd += "/"; if (loadInternal (wd + path, errorDescription)) { name = path; return true; } return false; } } return loadInternal (path, errorDescription); } ~MacModule () override { factory = PluginFactory (nullptr); if (bundle) { if (auto bundleExit = getFunctionPointer ("bundleExit")) bundleExit (); } } CFPtr bundle; }; //------------------------------------------------------------------------ void findModulesInDirectory (NSURL* dirUrl, Module::PathList& result) { dirUrl = [dirUrl URLByResolvingSymlinksInPath]; if (!dirUrl) return; NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtURL: dirUrl includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsPackageDescendants errorHandler:nil]; for (NSURL* url in enumerator) { if ([[[url lastPathComponent] pathExtension] isEqualToString:@"vst3"]) { CFPtr archs ( CFBundleCopyExecutableArchitecturesForURL (static_cast (url))); if (archs) result.emplace_back ([url.path UTF8String]); } else { id resValue; if (![url getResourceValue:&resValue forKey:NSURLIsSymbolicLinkKey error:nil]) continue; if (!static_cast (resValue).boolValue) continue; auto resolvedUrl = [url URLByResolvingSymlinksInPath]; if (![resolvedUrl getResourceValue:&resValue forKey:NSURLIsDirectoryKey error:nil]) continue; if (!static_cast (resValue).boolValue) continue; findModulesInDirectory (resolvedUrl, result); } } } //------------------------------------------------------------------------ void getModules (NSSearchPathDomainMask domain, Module::PathList& result) { NSURL* libraryUrl = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory inDomain:domain appropriateForURL:nil create:NO error:nil]; if (libraryUrl == nil) return; NSURL* audioUrl = [libraryUrl URLByAppendingPathComponent:@"Audio"]; if (audioUrl == nil) return; NSURL* plugInsUrl = [audioUrl URLByAppendingPathComponent:@"Plug-Ins"]; if (plugInsUrl == nil) return; NSURL* vst3Url = [[plugInsUrl URLByAppendingPathComponent:@"VST3"] URLByResolvingSymlinksInPath]; if (vst3Url == nil) return; findModulesInDirectory (vst3Url, result); } //------------------------------------------------------------------------ void getApplicationModules (Module::PathList& result) { auto bundle = CFBundleGetMainBundle (); if (!bundle) return; auto bundleUrl = static_cast (CFBridgingRelease (CFBundleCopyBundleURL (bundle))); if (!bundleUrl) return; auto resUrl = [bundleUrl URLByAppendingPathComponent:@"Contents"]; if (!resUrl) return; auto vst3Url = [resUrl URLByAppendingPathComponent:@"VST3"]; if (!vst3Url) return; findModulesInDirectory (vst3Url, result); } //------------------------------------------------------------------------ void getModuleSnapshots (const std::string& path, Module::SnapshotList& result) { auto* nsString = [NSString stringWithUTF8String:path.data ()]; if (!nsString) return; auto bundleUrl = [NSURL fileURLWithPath:nsString]; if (!bundleUrl) return; auto urls = [NSBundle URLsForResourcesWithExtension:@"png" subdirectory:@"Snapshots" inBundleWithURL:bundleUrl]; if (!urls || [urls count] == 0) return; for (NSURL* url in urls) { std::string fullpath ([[url path] UTF8String]); std::string filename ([[[url path] lastPathComponent] UTF8String]); auto uid = Module::Snapshot::decodeUID (filename); if (!uid) continue; auto scaleFactor = 1.; if (auto decodedScaleFactor = Module::Snapshot::decodeScaleFactor (filename)) scaleFactor = *decodedScaleFactor; Module::Snapshot::ImageDesc desc; desc.scaleFactor = scaleFactor; desc.path = std::move (fullpath); bool found = false; for (auto& entry : result) { if (entry.uid != *uid) continue; found = true; entry.images.emplace_back (std::move (desc)); break; } if (found) continue; Module::Snapshot snapshot; snapshot.uid = *uid; snapshot.images.emplace_back (std::move (desc)); result.emplace_back (std::move (snapshot)); } } //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ Module::Ptr Module::create (const std::string& path, std::string& errorDescription) { auto module = std::make_shared (); if (module->load (path, errorDescription)) { module->path = path; auto it = std::find_if (path.rbegin (), path.rend (), [] (const std::string::value_type& c) { return c == '/'; }); if (it != path.rend ()) module->name = {it.base (), path.end ()}; return std::move (module); } return nullptr; } //------------------------------------------------------------------------ Module::PathList Module::getModulePaths () { PathList list; getModules (NSUserDomainMask, list); getModules (NSLocalDomainMask, list); // TODO getModules (NSNetworkDomainMask, list); getApplicationModules (list); return list; } //------------------------------------------------------------------------ Module::SnapshotList Module::getSnapshots (const std::string& modulePath) { SnapshotList list; getModuleSnapshots (modulePath, list); return list; } //------------------------------------------------------------------------ Optional Module::getModuleInfoPath (const std::string& modulePath) { auto* nsString = [NSString stringWithUTF8String:modulePath.data ()]; if (!nsString) return {}; auto bundleUrl = [NSURL fileURLWithPath:nsString]; if (!bundleUrl) return {}; auto moduleInfoUrl = [NSBundle URLForResource:@"moduleinfo" withExtension:@"json" subdirectory:nullptr inBundleWithURL:bundleUrl]; if (!moduleInfoUrl) return {}; NSError* error = nil; if ([moduleInfoUrl checkResourceIsReachableAndReturnError:&error]) return {std::string (moduleInfoUrl.fileSystemRepresentation)}; return {}; } //------------------------------------------------------------------------ bool Module::validateBundleStructure (const std::string& path, std::string& errorDescription) { auto* nsString = [NSString stringWithUTF8String:path.data ()]; if (!nsString) return false; return [NSBundle bundleWithPath:nsString] != nil; } //------------------------------------------------------------------------ } // Hosting } // VST3 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/module.h0000644000000000000000000000013215124701711023464 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/module.h0000644000175000001440000001542315124701711023461 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/module.h // Created by : Steinberg, 08/2016 // Description : hosting module classes // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "../utility/uid.h" #include "pluginterfaces/base/ipluginbase.h" #include #include //------------------------------------------------------------------------ namespace VST3 { namespace Hosting { //------------------------------------------------------------------------ class FactoryInfo { public: //------------------------------------------------------------------------ using PFactoryInfo = Steinberg::PFactoryInfo; FactoryInfo () noexcept {} ~FactoryInfo () noexcept {} FactoryInfo (const FactoryInfo&) noexcept = default; FactoryInfo (PFactoryInfo&&) noexcept; FactoryInfo (FactoryInfo&&) noexcept = default; FactoryInfo& operator= (const FactoryInfo&) noexcept = default; FactoryInfo& operator= (FactoryInfo&&) noexcept; FactoryInfo& operator= (PFactoryInfo&&) noexcept; std::string vendor () const noexcept; std::string url () const noexcept; std::string email () const noexcept; Steinberg::int32 flags () const noexcept; bool classesDiscardable () const noexcept; bool licenseCheck () const noexcept; bool componentNonDiscardable () const noexcept; PFactoryInfo& get () noexcept { return info; } //------------------------------------------------------------------------ private: PFactoryInfo info {}; }; //------------------------------------------------------------------------ class ClassInfo { public: //------------------------------------------------------------------------ using SubCategories = std::vector; using PClassInfo = Steinberg::PClassInfo; using PClassInfo2 = Steinberg::PClassInfo2; using PClassInfoW = Steinberg::PClassInfoW; //------------------------------------------------------------------------ ClassInfo () noexcept {} explicit ClassInfo (const PClassInfo& info) noexcept; explicit ClassInfo (const PClassInfo2& info) noexcept; explicit ClassInfo (const PClassInfoW& info) noexcept; ClassInfo (const ClassInfo&) = default; ClassInfo& operator= (const ClassInfo&) = default; ClassInfo (ClassInfo&&) = default; ClassInfo& operator= (ClassInfo&&) = default; const UID& ID () const noexcept; int32_t cardinality () const noexcept; const std::string& category () const noexcept; const std::string& name () const noexcept; const std::string& vendor () const noexcept; const std::string& version () const noexcept; const std::string& sdkVersion () const noexcept; const SubCategories& subCategories () const noexcept; std::string subCategoriesString () const noexcept; Steinberg::uint32 classFlags () const noexcept; struct Data { UID classID; int32_t cardinality; std::string category; std::string name; std::string vendor; std::string version; std::string sdkVersion; SubCategories subCategories; Steinberg::uint32 classFlags = 0; }; Data& get () noexcept { return data; } //------------------------------------------------------------------------ private: void parseSubCategories (const std::string& str) noexcept; Data data {}; }; //------------------------------------------------------------------------ class PluginFactory { public: //------------------------------------------------------------------------ using ClassInfos = std::vector; using PluginFactoryPtr = Steinberg::IPtr; //------------------------------------------------------------------------ explicit PluginFactory (const PluginFactoryPtr& factory) noexcept; void setHostContext (Steinberg::FUnknown* context) const noexcept; FactoryInfo info () const noexcept; uint32_t classCount () const noexcept; ClassInfos classInfos () const noexcept; template Steinberg::IPtr createInstance (const UID& classID) const noexcept; const PluginFactoryPtr& get () const noexcept { return factory; } //------------------------------------------------------------------------ private: PluginFactoryPtr factory; }; //------------------------------------------------------------------------ //------------------------------------------------------------------------ class Module { public: //------------------------------------------------------------------------ struct Snapshot { struct ImageDesc { double scaleFactor {1.}; std::string path; }; UID uid; std::vector images; static Optional decodeScaleFactor (const std::string& path); static Optional decodeUID (const std::string& filename); }; using Ptr = std::shared_ptr; using PathList = std::vector; using SnapshotList = std::vector; //------------------------------------------------------------------------ static Ptr create (const std::string& path, std::string& errorDescription); static PathList getModulePaths (); static SnapshotList getSnapshots (const std::string& modulePath); /** get the path to the module info json file if it exists */ static Optional getModuleInfoPath (const std::string& modulePath); /** validate the bundle structure */ static bool validateBundleStructure (const std::string& path, std::string& errorDescription); const std::string& getName () const noexcept { return name; } const std::string& getPath () const noexcept { return path; } const PluginFactory& getFactory () const noexcept { return factory; } bool isBundle () const noexcept { return hasBundleStructure; } //------------------------------------------------------------------------ protected: virtual ~Module () noexcept = default; virtual bool load (const std::string& path, std::string& errorDescription) = 0; PluginFactory factory {nullptr}; std::string name; std::string path; bool hasBundleStructure {true}; }; //------------------------------------------------------------------------ template inline Steinberg::IPtr PluginFactory::createInstance (const UID& classID) const noexcept { T* obj = nullptr; if (factory->createInstance (classID.data (), T::iid, reinterpret_cast (&obj)) == Steinberg::kResultTrue) return Steinberg::owned (obj); return nullptr; } //------------------------------------------------------------------------ } // Hosting } // VST3 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/module.cpp0000644000000000000000000000013215124701711024017 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/module.cpp0000644000175000001440000002605415124701711024016 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/module.cpp // Created by : Steinberg, 08/2016 // Description : hosting module classes // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "module.h" #include "public.sdk/source/vst/utility/stringconvert.h" #include "public.sdk/source/vst/utility/optional.h" #include "pluginterfaces/base/funknownimpl.h" #include #include //------------------------------------------------------------------------ namespace VST3 { namespace Hosting { //------------------------------------------------------------------------ FactoryInfo::FactoryInfo (PFactoryInfo&& other) noexcept { *this = std::move (other); } //------------------------------------------------------------------------ FactoryInfo& FactoryInfo::operator= (FactoryInfo&& other) noexcept { info = std::move (other.info); other.info = {}; return *this; } //------------------------------------------------------------------------ FactoryInfo& FactoryInfo::operator= (PFactoryInfo&& other) noexcept { info = std::move (other); other = {}; return *this; } //------------------------------------------------------------------------ std::string FactoryInfo::vendor () const noexcept { namespace StringConvert = Steinberg::Vst::StringConvert; return StringConvert::convert (info.vendor, PFactoryInfo::kNameSize); } //------------------------------------------------------------------------ std::string FactoryInfo::url () const noexcept { namespace StringConvert = Steinberg::Vst::StringConvert; return StringConvert::convert (info.url, PFactoryInfo::kURLSize); } //------------------------------------------------------------------------ std::string FactoryInfo::email () const noexcept { namespace StringConvert = Steinberg::Vst::StringConvert; return StringConvert::convert (info.email, PFactoryInfo::kEmailSize); } //------------------------------------------------------------------------ Steinberg::int32 FactoryInfo::flags () const noexcept { return info.flags; } //------------------------------------------------------------------------ bool FactoryInfo::classesDiscardable () const noexcept { return (info.flags & PFactoryInfo::kClassesDiscardable) != 0; } //------------------------------------------------------------------------ bool FactoryInfo::licenseCheck () const noexcept { return (info.flags & PFactoryInfo::kLicenseCheck) != 0; } //------------------------------------------------------------------------ bool FactoryInfo::componentNonDiscardable () const noexcept { return (info.flags & PFactoryInfo::kComponentNonDiscardable) != 0; } //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ PluginFactory::PluginFactory (const PluginFactoryPtr& factory) noexcept : factory (factory) { } //------------------------------------------------------------------------ void PluginFactory::setHostContext (Steinberg::FUnknown* context) const noexcept { if (auto f = Steinberg::FUnknownPtr (factory)) f->setHostContext (context); } //------------------------------------------------------------------------ FactoryInfo PluginFactory::info () const noexcept { Steinberg::PFactoryInfo i; factory->getFactoryInfo (&i); return FactoryInfo (std::move (i)); } //------------------------------------------------------------------------ uint32_t PluginFactory::classCount () const noexcept { auto count = factory->countClasses (); assert (count >= 0); return static_cast (count); } //------------------------------------------------------------------------ PluginFactory::ClassInfos PluginFactory::classInfos () const noexcept { auto count = classCount (); Optional factoryInfo; ClassInfos classes; classes.reserve (count); auto f3 = Steinberg::U::cast (factory); auto f2 = Steinberg::U::cast (factory); Steinberg::PClassInfo ci; Steinberg::PClassInfo2 ci2; Steinberg::PClassInfoW ci3; for (uint32_t i = 0; i < count; ++i) { if (f3 && f3->getClassInfoUnicode (i, &ci3) == Steinberg::kResultTrue) classes.emplace_back (ci3); else if (f2 && f2->getClassInfo2 (i, &ci2) == Steinberg::kResultTrue) classes.emplace_back (ci2); else if (factory->getClassInfo (i, &ci) == Steinberg::kResultTrue) classes.emplace_back (ci); auto& classInfo = classes.back (); if (classInfo.vendor ().empty ()) { if (!factoryInfo) factoryInfo = Optional (info ()); classInfo.get ().vendor = factoryInfo->vendor (); } } return classes; } //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ const UID& ClassInfo::ID () const noexcept { return data.classID; } //------------------------------------------------------------------------ int32_t ClassInfo::cardinality () const noexcept { return data.cardinality; } //------------------------------------------------------------------------ const std::string& ClassInfo::category () const noexcept { return data.category; } //------------------------------------------------------------------------ const std::string& ClassInfo::name () const noexcept { return data.name; } //------------------------------------------------------------------------ const std::string& ClassInfo::vendor () const noexcept { return data.vendor; } //------------------------------------------------------------------------ const std::string& ClassInfo::version () const noexcept { return data.version; } //------------------------------------------------------------------------ const std::string& ClassInfo::sdkVersion () const noexcept { return data.sdkVersion; } //------------------------------------------------------------------------ const ClassInfo::SubCategories& ClassInfo::subCategories () const noexcept { return data.subCategories; } //------------------------------------------------------------------------ Steinberg::uint32 ClassInfo::classFlags () const noexcept { return data.classFlags; } //------------------------------------------------------------------------ ClassInfo::ClassInfo (const PClassInfo& info) noexcept { namespace StringConvert = Steinberg::Vst::StringConvert; data.classID = info.cid; data.cardinality = info.cardinality; data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize); data.name = StringConvert::convert (info.name, PClassInfo::kNameSize); } //------------------------------------------------------------------------ ClassInfo::ClassInfo (const PClassInfo2& info) noexcept { namespace StringConvert = Steinberg::Vst::StringConvert; data.classID = info.cid; data.cardinality = info.cardinality; data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize); data.name = StringConvert::convert (info.name, PClassInfo::kNameSize); data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize); data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize); data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize); parseSubCategories ( StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize)); data.classFlags = info.classFlags; } //------------------------------------------------------------------------ ClassInfo::ClassInfo (const PClassInfoW& info) noexcept { namespace StringConvert = Steinberg::Vst::StringConvert; data.classID = info.cid; data.cardinality = info.cardinality; data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize); data.name = StringConvert::convert (info.name, PClassInfo::kNameSize); data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize); data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize); data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize); parseSubCategories ( StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize)); data.classFlags = info.classFlags; } //------------------------------------------------------------------------ void ClassInfo::parseSubCategories (const std::string& str) noexcept { std::stringstream stream (str); std::string item; while (std::getline (stream, item, '|')) data.subCategories.emplace_back (std::move (item)); } //------------------------------------------------------------------------ std::string ClassInfo::subCategoriesString () const noexcept { std::string result; if (data.subCategories.empty ()) return result; result = data.subCategories[0]; for (auto index = 1u; index < data.subCategories.size (); ++index) result += "|" + data.subCategories[index]; return result; } //------------------------------------------------------------------------ namespace { //------------------------------------------------------------------------ std::pair rangeOfScaleFactor (const std::string& name) { auto result = std::make_pair (std::string::npos, std::string::npos); size_t xIndex = name.find_last_of ('x'); if (xIndex == std::string::npos) return result; size_t indicatorIndex = name.find_last_of ('_'); if (indicatorIndex == std::string::npos) return result; if (xIndex < indicatorIndex) return result; result.first = indicatorIndex + 1; result.second = xIndex; return result; } //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ Optional Module::Snapshot::decodeScaleFactor (const std::string& name) { auto range = rangeOfScaleFactor (name); if (range.first == std::string::npos || range.second == std::string::npos) return {}; std::string tmp (name.data () + range.first, range.second - range.first); std::istringstream sstream (tmp); sstream.imbue (std::locale::classic ()); sstream.precision (static_cast (3)); double result; sstream >> result; return Optional (result); } //------------------------------------------------------------------------ Optional Module::Snapshot::decodeUID (const std::string& filename) { if (filename.size () < 45) return {}; if (filename.find ("_snapshot") != 32) return {}; auto uidStr = filename.substr (0, 32); return UID::fromString (uidStr); } //------------------------------------------------------------------------ } // Hosting } // VST3 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/test0000644000000000000000000000013215124701711022730 xustar0030 mtime=1767080905.281761729 30 atime=1767080905.281210866 30 ctime=1767080905.281761729 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/0000755000175000001440000000000015124701711022775 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/PaxHeaders/eventlisttest.cpp0000644000000000000000000000013215124701711026426 xustar0030 mtime=1767080905.281761729 30 atime=1767080905.281210866 30 ctime=1767080905.281761729 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/eventlisttest.cpp0000644000175000001440000000655515124701711026431 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/test/eventlisttest.cpp // Created by : Steinberg, 08/2021 // Description : Test event list // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/hosting/eventlist.h" #include "public.sdk/source/vst/utility/testing.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace { //------------------------------------------------------------------------ ModuleInitializer EventListTests ([] () { constexpr auto TestSuiteName = "EventList"; registerTest (TestSuiteName, STR ("Set and get single event"), [] (ITestResult* testResult) { EventList eventList; Event event1 = {}; event1.type = Event::kNoteOnEvent; event1.noteOn.noteId = 10; EXPECT_EQ (eventList.addEvent (event1), kResultTrue); Event event2; EXPECT_EQ (eventList.getEvent (0, event2), kResultTrue); EXPECT_EQ (memcmp (&event1, &event2, sizeof (Event)), 0); return true; }); registerTest (TestSuiteName, STR ("Count events"), [] (ITestResult* testResult) { EventList eventList; Event event = {}; for (auto i = 0; i < 20; ++i) { EXPECT_EQ (eventList.addEvent (event), kResultTrue); } EXPECT_EQ (eventList.getEventCount (), 20); return true; }); registerTest (TestSuiteName, STR ("Overflow"), [] (ITestResult* testResult) { EventList eventList (20); Event event = {}; for (auto i = 0; i < 20; ++i) { EXPECT_EQ (eventList.addEvent (event), kResultTrue); } EXPECT_EQ (eventList.getEventCount (), 20); EXPECT_NE (eventList.addEvent (event), kResultTrue); EXPECT_EQ (eventList.getEventCount (), 20); return true; }); registerTest (TestSuiteName, STR ("Get unknown event"), [] (ITestResult* testResult) { EventList eventList; Event event {}; EXPECT_NE (eventList.getEvent (0, event), kResultTrue); return true; }); registerTest (TestSuiteName, STR ("Resize"), [] (ITestResult* testResult) { EventList eventList (1); Event event {}; EXPECT_NE (eventList.getEvent (0, event), kResultTrue); event = {}; EXPECT_EQ (eventList.addEvent (event), kResultTrue); EXPECT_EQ (eventList.getEventCount (), 1); EXPECT_NE (eventList.addEvent (event), kResultTrue); EXPECT_EQ (eventList.getEventCount (), 1); eventList.setMaxSize (2); EXPECT_EQ (eventList.getEventCount (), 0); EXPECT_EQ (eventList.addEvent (event), kResultTrue); EXPECT_EQ (eventList.addEvent (event), kResultTrue); EXPECT_EQ (eventList.getEventCount (), 2); EXPECT_NE (eventList.addEvent (event), kResultTrue); EXPECT_EQ (eventList.getEventCount (), 2); return true; }); }); //------------------------------------------------------------------------ } // anonymous } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/PaxHeaders/processdatatest.cpp0000644000000000000000000000013215124701711026721 xustar0030 mtime=1767080905.281761729 30 atime=1767080905.281761729 30 ctime=1767080905.281761729 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/processdatatest.cpp0000644000175000001440000004172615124701711026723 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/test/processdatatest.cpp // Created by : Steinberg, 08/2021 // Description : Test process data helper // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/hosting/processdata.h" #include "public.sdk/source/vst/utility/testing.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstmessage.h" #include "pluginterfaces/vst/ivstunits.h" #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace { //------------------------------------------------------------------------ struct TestComponent : public IComponent { using GetBusCountFunc = std::function; using GetBusInfoFunc = std::function; tresult PLUGIN_API queryInterface (const TUID /*_iid*/, void** /*obj*/) override { return kNoInterface; } uint32 PLUGIN_API addRef () override { return 100; } uint32 PLUGIN_API release () override { return 100; } tresult PLUGIN_API initialize (FUnknown* /*context*/) override { return kResultTrue; } tresult PLUGIN_API terminate () override { return kResultTrue; } tresult PLUGIN_API getControllerClassId (TUID /*classId*/) override { return kNotImplemented; } tresult PLUGIN_API setIoMode (IoMode /*mode*/) override { return kNotImplemented; } int32 PLUGIN_API getBusCount (MediaType type, BusDirection dir) override { if (type != MediaTypes::kAudio) return 0; return getBusCountFunc (dir); } tresult PLUGIN_API getBusInfo (MediaType type, BusDirection dir, int32 index, BusInfo& bus) override { if (type != MediaTypes::kAudio) return kResultFalse; return getBusInfoFunc (dir, index, bus); } tresult PLUGIN_API getRoutingInfo (RoutingInfo& /*inInfo*/, RoutingInfo& /*outInfo*/) override { return kNotImplemented; } tresult PLUGIN_API activateBus (MediaType /*type*/, BusDirection /*dir*/, int32 /*index*/, TBool /*state*/) override { return kNotImplemented; } tresult PLUGIN_API setActive (TBool /*state*/) override { return kNotImplemented; } tresult PLUGIN_API setState (IBStream* /*state*/) override { return kNotImplemented; } tresult PLUGIN_API getState (IBStream* /*state*/) override { return kNotImplemented; } GetBusCountFunc getBusCountFunc = [] (BusDirection /*dir*/) { return 0; }; GetBusInfoFunc getBusInfoFunc = [] (BusDirection /*dir*/, int32 /*index*/, BusInfo& /*bus*/) { return kNotImplemented; }; }; //------------------------------------------------------------------------ ModuleInitializer HostProcessDataTests ([] () { constexpr auto TestSuiteName = "HostProcessData"; registerTest (TestSuiteName, STR ("No bus"), [] (ITestResult* testResult) { TestComponent tc; HostProcessData processData; EXPECT_TRUE (processData.prepare (tc, 1024, kSample32)); EXPECT_EQ (processData.numInputs, 0); EXPECT_EQ (processData.numOutputs, 0); return true; }); registerTest (TestSuiteName, STR ("1 out bus no channels"), [] (ITestResult* testResult) { TestComponent tc; tc.getBusCountFunc = [] (BusDirection dir) { return dir == BusDirections::kOutput ? 1 : 0; }; HostProcessData processData; EXPECT_TRUE (processData.prepare (tc, 1024, kSample32)); EXPECT_EQ (processData.numInputs, 0); EXPECT_EQ (processData.numOutputs, 1); EXPECT_EQ (processData.outputs[0].numChannels, 0); return true; }); registerTest (TestSuiteName, STR ("1 out bus 2 channels"), [] (ITestResult* testResult) { TestComponent tc; tc.getBusCountFunc = [] (BusDirection dir) { return dir == BusDirections::kOutput ? 1 : 0; }; tc.getBusInfoFunc = [] (BusDirection dir, int32 index, BusInfo& bus) { if (dir == BusDirections::kInput || index != 0) return kResultFalse; bus.channelCount = 2; return kResultTrue; }; HostProcessData processData; EXPECT_TRUE (processData.prepare (tc, 1024, kSample32)); EXPECT_EQ (processData.numInputs, 0); EXPECT_EQ (processData.numOutputs, 1); EXPECT_EQ (processData.outputs[0].numChannels, 2); EXPECT_NE (processData.outputs[0].channelBuffers32[0], nullptr); EXPECT_NE (processData.outputs[0].channelBuffers32[1], nullptr); return true; }); registerTest (TestSuiteName, STR ("1 in & out bus 2 channels"), [] (ITestResult* testResult) { TestComponent tc; tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; }; tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) { if (index != 0) return kResultFalse; bus.channelCount = 2; return kResultTrue; }; HostProcessData processData; EXPECT_TRUE (processData.prepare (tc, 1024, kSample32)); EXPECT_EQ (processData.numOutputs, 1); EXPECT_EQ (processData.outputs[0].numChannels, 2); EXPECT_NE (processData.outputs[0].channelBuffers32[0], nullptr); EXPECT_NE (processData.outputs[0].channelBuffers32[1], nullptr); EXPECT_EQ (processData.numInputs, 1); EXPECT_EQ (processData.inputs[0].numChannels, 2); EXPECT_NE (processData.inputs[0].channelBuffers32[0], nullptr); EXPECT_NE (processData.inputs[0].channelBuffers32[1], nullptr); return true; }); registerTest (TestSuiteName, STR ("2 in & out bus dif channels"), [] (ITestResult* testResult) { TestComponent tc; tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 2; }; tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) { if (index < 0 || index > 1) return kResultFalse; bus.channelCount = index == 0 ? 4 : 1; return kResultTrue; }; HostProcessData processData; EXPECT_TRUE (processData.prepare (tc, 1024, kSample32)); EXPECT_EQ (processData.numOutputs, 2); EXPECT_EQ (processData.outputs[0].numChannels, 4); EXPECT_NE (processData.outputs[0].channelBuffers32[0], nullptr); EXPECT_NE (processData.outputs[0].channelBuffers32[1], nullptr); EXPECT_NE (processData.outputs[0].channelBuffers32[2], nullptr); EXPECT_NE (processData.outputs[0].channelBuffers32[3], nullptr); EXPECT_EQ (processData.outputs[1].numChannels, 1); EXPECT_NE (processData.outputs[1].channelBuffers32[0], nullptr); EXPECT_EQ (processData.numInputs, 2); EXPECT_EQ (processData.inputs[0].numChannels, 4); EXPECT_NE (processData.inputs[0].channelBuffers32[0], nullptr); EXPECT_NE (processData.inputs[0].channelBuffers32[1], nullptr); EXPECT_NE (processData.inputs[0].channelBuffers32[2], nullptr); EXPECT_NE (processData.inputs[0].channelBuffers32[3], nullptr); EXPECT_EQ (processData.inputs[1].numChannels, 1); EXPECT_NE (processData.inputs[1].channelBuffers32[0], nullptr); return true; }); registerTest (TestSuiteName, STR ("Set all channel buffers 32"), [] (ITestResult* testResult) { TestComponent tc; tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; }; tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) { if (index != 0) return kResultFalse; bus.channelCount = 2; return kResultTrue; }; auto buffer = std::unique_ptr (new float[10]); HostProcessData processData; EXPECT_TRUE (processData.prepare (tc, 0, kSample32)); EXPECT_EQ (processData.numInputs, 1); EXPECT_EQ (processData.numOutputs, 1); EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kInput, 1, nullptr)); EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kInput, 1, nullptr)); EXPECT_TRUE (processData.setChannelBuffers (BusDirections::kInput, 0, buffer.get ())); EXPECT_TRUE (processData.setChannelBuffers (BusDirections::kOutput, 0, buffer.get ())); EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kInput, 0, nullptr)); EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kOutput, 0, nullptr)); EXPECT_EQ (processData.inputs[0].channelBuffers32[0], buffer.get ()); EXPECT_EQ (processData.inputs[0].channelBuffers32[1], buffer.get ()); EXPECT_EQ (processData.outputs[0].channelBuffers32[0], buffer.get ()); EXPECT_EQ (processData.outputs[0].channelBuffers32[1], buffer.get ()); return true; }); registerTest (TestSuiteName, STR ("Set all channel buffers 64"), [] (ITestResult* testResult) { TestComponent tc; tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; }; tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) { if (index != 0) return kResultFalse; bus.channelCount = 2; return kResultTrue; }; auto buffer = std::unique_ptr (new double[10]); HostProcessData processData; EXPECT_TRUE (processData.prepare (tc, 0, kSample64)); EXPECT_EQ (processData.numInputs, 1); EXPECT_EQ (processData.numOutputs, 1); EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kInput, 1, nullptr)); EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kInput, 1, nullptr)); EXPECT_TRUE (processData.setChannelBuffers64 (BusDirections::kInput, 0, buffer.get ())); EXPECT_TRUE (processData.setChannelBuffers64 (BusDirections::kOutput, 0, buffer.get ())); EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kInput, 0, nullptr)); EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kOutput, 0, nullptr)); EXPECT_EQ (processData.inputs[0].channelBuffers64[0], buffer.get ()); EXPECT_EQ (processData.inputs[0].channelBuffers64[1], buffer.get ()); EXPECT_EQ (processData.outputs[0].channelBuffers64[0], buffer.get ()); EXPECT_EQ (processData.outputs[0].channelBuffers64[1], buffer.get ()); return true; }); registerTest ( TestSuiteName, STR ("Set individual channel buffers 32"), [] (ITestResult* testResult) { TestComponent tc; tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; }; tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) { if (index != 0) return kResultFalse; bus.channelCount = 2; return kResultTrue; }; auto bufferL = std::unique_ptr (new float[10]); auto bufferR = std::unique_ptr (new float[10]); HostProcessData pd; EXPECT_TRUE (pd.prepare (tc, 0, kSample32)); EXPECT_EQ (pd.numInputs, 1); EXPECT_EQ (pd.numOutputs, 1); EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kInput, 1, 0, nullptr)); EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kInput, 1, 0, nullptr)); EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kInput, 0, 0, bufferL.get ())); EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kInput, 0, 1, bufferR.get ())); EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kOutput, 0, 0, bufferL.get ())); EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kOutput, 0, 1, bufferR.get ())); EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kInput, 0, 0, nullptr)); EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kOutput, 0, 1, nullptr)); EXPECT_EQ (pd.inputs[0].channelBuffers32[0], bufferL.get ()); EXPECT_EQ (pd.inputs[0].channelBuffers32[1], bufferR.get ()); EXPECT_EQ (pd.outputs[0].channelBuffers32[0], bufferL.get ()); EXPECT_EQ (pd.outputs[0].channelBuffers32[1], bufferR.get ()); return true; }); registerTest (TestSuiteName, STR ("Set individual channel buffers 32 combined"), [] (ITestResult* testResult) { TestComponent tc; tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; }; tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) { if (index != 0) return kResultFalse; bus.channelCount = 2; return kResultTrue; }; auto bufferL = std::unique_ptr (new float[10]); auto bufferR = std::unique_ptr (new float[10]); float* buffers[2] = {bufferL.get (), bufferR.get ()}; HostProcessData pd; EXPECT_TRUE (pd.prepare (tc, 0, kSample32)); EXPECT_EQ (pd.numInputs, 1); EXPECT_EQ (pd.numOutputs, 1); EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kInput, 1, nullptr, 0)); EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kInput, 1, nullptr, 0)); EXPECT_TRUE (pd.setChannelBuffers (BusDirections::kInput, 0, buffers, 2)); EXPECT_TRUE (pd.setChannelBuffers (BusDirections::kOutput, 0, buffers, 2)); EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kInput, 0, nullptr, 0)); EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kOutput, 0, nullptr, 0)); EXPECT_EQ (pd.inputs[0].channelBuffers32[0], bufferL.get ()); EXPECT_EQ (pd.inputs[0].channelBuffers32[1], bufferR.get ()); EXPECT_EQ (pd.outputs[0].channelBuffers32[0], bufferL.get ()); EXPECT_EQ (pd.outputs[0].channelBuffers32[1], bufferR.get ()); return true; }); registerTest ( TestSuiteName, STR ("Set individual channel buffers 64"), [] (ITestResult* testResult) { TestComponent tc; tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; }; tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) { if (index != 0) return kResultFalse; bus.channelCount = 2; return kResultTrue; }; auto bufferL = std::unique_ptr (new double[10]); auto bufferR = std::unique_ptr (new double[10]); HostProcessData pd; EXPECT_TRUE (pd.prepare (tc, 0, kSample64)); EXPECT_EQ (pd.numInputs, 1); EXPECT_EQ (pd.numOutputs, 1); EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kInput, 1, 0, nullptr)); EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kInput, 1, 0, nullptr)); EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kInput, 0, 0, bufferL.get ())); EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kInput, 0, 1, bufferR.get ())); EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kOutput, 0, 0, bufferL.get ())); EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kOutput, 0, 1, bufferR.get ())); EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kInput, 0, 0, nullptr)); EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kOutput, 0, 1, nullptr)); EXPECT_EQ (pd.inputs[0].channelBuffers64[0], bufferL.get ()); EXPECT_EQ (pd.inputs[0].channelBuffers64[1], bufferR.get ()); EXPECT_EQ (pd.outputs[0].channelBuffers64[0], bufferL.get ()); EXPECT_EQ (pd.outputs[0].channelBuffers64[1], bufferR.get ()); return true; }); registerTest (TestSuiteName, STR ("Set individual channel buffers 64 combined"), [] (ITestResult* testResult) { TestComponent tc; tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; }; tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) { if (index != 0) return kResultFalse; bus.channelCount = 2; return kResultTrue; }; auto bufferL = std::unique_ptr (new double[10]); auto bufferR = std::unique_ptr (new double[10]); double* buffers[2] = {bufferL.get (), bufferR.get ()}; HostProcessData pd; EXPECT_TRUE (pd.prepare (tc, 0, kSample64)); EXPECT_EQ (pd.numInputs, 1); EXPECT_EQ (pd.numOutputs, 1); EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kInput, 1, nullptr, 0)); EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kInput, 1, nullptr, 0)); EXPECT_TRUE (pd.setChannelBuffers64 (BusDirections::kInput, 0, buffers, 2)); EXPECT_TRUE (pd.setChannelBuffers64 (BusDirections::kOutput, 0, buffers, 2)); EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kInput, 0, nullptr, 0)); EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kOutput, 0, nullptr, 0)); EXPECT_EQ (pd.inputs[0].channelBuffers64[0], bufferL.get ()); EXPECT_EQ (pd.inputs[0].channelBuffers64[1], bufferR.get ()); EXPECT_EQ (pd.outputs[0].channelBuffers64[0], bufferL.get ()); EXPECT_EQ (pd.outputs[0].channelBuffers64[1], bufferR.get ()); return true; }); }); //------------------------------------------------------------------------ } // anonymous } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/PaxHeaders/connectionproxytest.cpp0000644000000000000000000000013215124701711027652 xustar0030 mtime=1767080905.281210866 30 atime=1767080905.281210866 30 ctime=1767080905.281210866 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/connectionproxytest.cpp0000644000175000001440000001005015124701711027636 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/test/connectionproxytest.cpp // Created by : Steinberg, 08/2021 // Description : Test connection proxy // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/hosting/connectionproxy.h" #include "public.sdk/source/vst/hosting/hostclasses.h" #include "public.sdk/source/vst/utility/testing.h" #include #include #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace { //------------------------------------------------------------------------ class ConnectionPoint : public IConnectionPoint { public: tresult PLUGIN_API connect (IConnectionPoint* inOther) override { other = inOther; return kResultTrue; } tresult PLUGIN_API disconnect (IConnectionPoint* inOther) override { if (inOther != other) return kResultFalse; return kResultTrue; } tresult PLUGIN_API notify (IMessage*) override { messageReceived = true; return kResultTrue; } tresult PLUGIN_API queryInterface (const TUID, void**) override { return kNotImplemented; } uint32 PLUGIN_API addRef () override { return 100; } uint32 PLUGIN_API release () override { return 100; } IConnectionPoint* other {nullptr}; bool messageReceived {false}; }; //------------------------------------------------------------------------ ModuleInitializer ConnectionProxyTests ([] () { constexpr auto TestSuiteName = "ConnectionProxy"; registerTest (TestSuiteName, STR ("Connect and disconnect"), [] (ITestResult* testResult) { ConnectionPoint cp1; ConnectionPoint cp2; ConnectionProxy proxy (&cp1); EXPECT_EQ (proxy.connect (&cp2), kResultTrue); EXPECT_EQ (proxy.disconnect (&cp2), kResultTrue); return true; }); registerTest (TestSuiteName, STR ("Disconnect wrong object"), [] (ITestResult* testResult) { ConnectionPoint cp1; ConnectionPoint cp2; ConnectionPoint cp3; ConnectionProxy proxy (&cp1); EXPECT_EQ (proxy.connect (&cp2), kResultTrue); EXPECT_NE (proxy.disconnect (&cp3), kResultTrue); return true; }); registerTest (TestSuiteName, STR ("Send message on UI thread"), [] (ITestResult* testResult) { ConnectionPoint cp1; ConnectionPoint cp2; ConnectionProxy proxy (&cp1); EXPECT_EQ (proxy.connect (&cp2), kResultTrue); EXPECT_FALSE (cp2.messageReceived); HostMessage msg; EXPECT_EQ (proxy.notify (&msg), kResultTrue); EXPECT_TRUE (cp2.messageReceived); return true; }); registerTest (TestSuiteName, STR ("Send message on 2nd thread"), [] (ITestResult* testResult) { ConnectionPoint cp1; ConnectionPoint cp2; ConnectionProxy proxy (&cp1); EXPECT_EQ (proxy.connect (&cp2), kResultTrue); EXPECT_FALSE (cp2.messageReceived); std::condition_variable cv; std::mutex m; std::optional notifyResult; std::thread thread ([&] () { HostMessage msg; { const std::scoped_lock sl (m); notifyResult = proxy.notify (&msg); } cv.notify_one (); }); std::unique_lock ul (m); cv.wait (ul, [&] { return notifyResult.has_value (); }); EXPECT_NE (*notifyResult, kResultTrue); EXPECT_FALSE (cp2.messageReceived); thread.join (); return true; }); }); //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ } // Vst } // Steinbergqtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/PaxHeaders/parameterchangestest.cpp0000644000000000000000000000013215124701711027722 xustar0030 mtime=1767080905.281761729 30 atime=1767080905.281761729 30 ctime=1767080905.281761729 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/parameterchangestest.cpp0000644000175000001440000002521615124701711027720 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/test/parameterchangestest.cpp // Created by : Steinberg, 08/2021 // Description : Test parameter changes // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/hosting/parameterchanges.h" #include "public.sdk/source/vst/utility/testing.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace { //------------------------------------------------------------------------ struct ValuePoint { int32 sampleOffset {}; ParamValue value {}; }; //------------------------------------------------------------------------ ModuleInitializer ParameterValueQueueTests ([] () { constexpr auto TestSuiteName = "ParameterValueQueue"; registerTest (TestSuiteName, STR ("Set paramID"), [] (ITestResult* testResult) { ParameterValueQueue queue (10); EXPECT_EQ (queue.getParameterId (), 10); queue.setParamID (5); EXPECT_EQ (queue.getParameterId (), 5); return true; }); registerTest (TestSuiteName, STR ("Set/get point"), [] (ITestResult* testResult) { ParameterValueQueue queue (0); ValuePoint vp {100, 0.5}; int32 index {}; EXPECT_EQ (queue.addPoint (vp.sampleOffset, vp.value, index), kResultTrue); EXPECT_EQ (queue.getPointCount (), 1); EXPECT_EQ (index, 0); ValuePoint test; EXPECT_EQ (queue.getPoint (index, test.sampleOffset, test.value), kResultTrue); EXPECT_EQ (vp.sampleOffset, test.sampleOffset); EXPECT_EQ (vp.value, test.value); return true; }); registerTest (TestSuiteName, STR ("Set/get multiple points"), [] (ITestResult* testResult) { ParameterValueQueue queue (0); ValuePoint vp1 {10, 0.1}; ValuePoint vp2 {30, 0.3}; ValuePoint vp3 {50, 0.6}; ValuePoint vp4 {70, 0.8}; int32 index {}; EXPECT_EQ (queue.addPoint (vp1.sampleOffset, vp1.value, index), kResultTrue); EXPECT_EQ (queue.addPoint (vp2.sampleOffset, vp2.value, index), kResultTrue); EXPECT_EQ (queue.addPoint (vp3.sampleOffset, vp3.value, index), kResultTrue); EXPECT_EQ (queue.addPoint (vp4.sampleOffset, vp4.value, index), kResultTrue); EXPECT_EQ (queue.getPointCount (), 4); EXPECT_EQ (index, 3); ValuePoint test; EXPECT_EQ (queue.getPoint (0, test.sampleOffset, test.value), kResultTrue); EXPECT_EQ (vp1.sampleOffset, test.sampleOffset); EXPECT_EQ (vp1.value, test.value); EXPECT_EQ (queue.getPoint (1, test.sampleOffset, test.value), kResultTrue); EXPECT_EQ (vp2.sampleOffset, test.sampleOffset); EXPECT_EQ (vp2.value, test.value); EXPECT_EQ (queue.getPoint (2, test.sampleOffset, test.value), kResultTrue); EXPECT_EQ (vp3.sampleOffset, test.sampleOffset); EXPECT_EQ (vp3.value, test.value); EXPECT_EQ (queue.getPoint (3, test.sampleOffset, test.value), kResultTrue); EXPECT_EQ (vp4.sampleOffset, test.sampleOffset); EXPECT_EQ (vp4.value, test.value); return true; }); registerTest (TestSuiteName, STR ("Ordered points"), [] (ITestResult* testResult) { ParameterValueQueue queue (0); ValuePoint vp1 {70, 0.1}; ValuePoint vp2 {50, 0.3}; ValuePoint vp3 {30, 0.6}; ValuePoint vp4 {10, 0.8}; int32 index {}; EXPECT_EQ (queue.addPoint (vp1.sampleOffset, vp1.value, index), kResultTrue); EXPECT_EQ (queue.addPoint (vp2.sampleOffset, vp2.value, index), kResultTrue); EXPECT_EQ (queue.addPoint (vp3.sampleOffset, vp3.value, index), kResultTrue); EXPECT_EQ (queue.addPoint (vp4.sampleOffset, vp4.value, index), kResultTrue); EXPECT_EQ (queue.getPointCount (), 4); ValuePoint test; EXPECT_EQ (queue.getPoint (0, test.sampleOffset, test.value), kResultTrue); EXPECT_EQ (vp4.sampleOffset, test.sampleOffset); EXPECT_EQ (vp4.value, test.value); EXPECT_EQ (queue.getPoint (1, test.sampleOffset, test.value), kResultTrue); EXPECT_EQ (vp3.sampleOffset, test.sampleOffset); EXPECT_EQ (vp3.value, test.value); EXPECT_EQ (queue.getPoint (2, test.sampleOffset, test.value), kResultTrue); EXPECT_EQ (vp2.sampleOffset, test.sampleOffset); EXPECT_EQ (vp2.value, test.value); EXPECT_EQ (queue.getPoint (3, test.sampleOffset, test.value), kResultTrue); EXPECT_EQ (vp1.sampleOffset, test.sampleOffset); EXPECT_EQ (vp1.value, test.value); return true; }); registerTest (TestSuiteName, STR ("Clear"), [] (ITestResult* testResult) { ParameterValueQueue queue (0); ValuePoint vp {100, 0.5}; int32 index {}; EXPECT_EQ (queue.addPoint (vp.sampleOffset, vp.value, index), kResultTrue); EXPECT_EQ (queue.getPointCount (), 1); EXPECT_EQ (index, 0); queue.clear (); EXPECT_EQ (queue.getPointCount (), 0); ValuePoint test; EXPECT_NE (queue.getPoint (index, test.sampleOffset, test.value), kResultTrue); return true; }); }); //------------------------------------------------------------------------ ModuleInitializer ParameterChangesTests ([] () { constexpr auto TestSuiteName = "ParameterChanges"; registerTest (TestSuiteName, STR ("Parameter count"), [] (ITestResult* testResult) { ParameterChanges changes (1); EXPECT_EQ (changes.getParameterCount (), 0); int32 index {}; auto queue = changes.addParameterData (0, index); EXPECT_NE (queue, nullptr); EXPECT_EQ (index, 0); EXPECT_EQ (changes.getParameterCount (), 1); return true; }); registerTest (TestSuiteName, STR ("Clear queue"), [] (ITestResult* testResult) { ParameterChanges changes (1); int32 index {}; EXPECT_EQ (changes.getParameterCount (), 0); changes.addParameterData (0, index); EXPECT_EQ (changes.getParameterCount (), 1); changes.clearQueue (); EXPECT_EQ (changes.getParameterCount (), 0); return true; }); registerTest (TestSuiteName, STR ("Increase max parameters"), [] (ITestResult* testResult) { ParameterChanges changes (1); int32 index {}; EXPECT_EQ (changes.getParameterCount (), 0); changes.addParameterData (0, index); EXPECT_EQ (changes.getParameterCount (), 1); EXPECT_NE (changes.addParameterData (1, index), nullptr); EXPECT_EQ (changes.getParameterCount (), 2); changes.setMaxParameters (4); EXPECT_EQ (changes.getParameterCount (), 2); return true; }); registerTest (TestSuiteName, STR ("Get parameter data"), [] (ITestResult* testResult) { ParameterChanges changes (1); int32 index {}; auto queue1 = changes.addParameterData (0, index); auto queue2 = changes.getParameterData (index); EXPECT_EQ (queue1, queue2); return true; }); }); //------------------------------------------------------------------------ struct ParamChange { ParamID id {}; ParamValue value {}; int32 sampleOffset {}; bool operator== (const ParamChange& o) const { return id == o.id && value == o.value && sampleOffset == o.sampleOffset; } bool operator!= (const ParamChange& o) const { return id != o.id || value != o.value || sampleOffset != o.sampleOffset; } }; //------------------------------------------------------------------------ ModuleInitializer ParameterChangeTransferTests ([] () { constexpr auto TestSuiteName = "ParameterChangeTransfer"; registerTest (TestSuiteName, STR ("Add/get change"), [] (ITestResult* testResult) { ParameterChangeTransfer transfer (1); ParamChange change {1, 0.8, 2}; transfer.addChange (change.id, change.value, change.sampleOffset); ParamChange test {}; EXPECT_NE (change, test); EXPECT_TRUE (transfer.getNextChange (test.id, test.value, test.sampleOffset)); EXPECT_EQ (change, test); return true; }); registerTest (TestSuiteName, STR ("Remove changes"), [] (ITestResult* testResult) { ParameterChangeTransfer transfer (1); ParamChange change {1, 0.8, 2}; transfer.addChange (change.id, change.value, change.sampleOffset); transfer.removeChanges (); ParamChange test {}; EXPECT_FALSE (transfer.getNextChange (test.id, test.value, test.sampleOffset)); return true; }); registerTest (TestSuiteName, STR ("Transfer changes to"), [] (ITestResult* testResult) { ParameterChangeTransfer transfer (10); ParamChange ch1 {1, 0.8, 2}; ParamChange ch2 {2, 0.4, 8}; transfer.addChange (ch1.id, ch1.value, ch1.sampleOffset); transfer.addChange (ch2.id, ch2.value, ch2.sampleOffset); ParameterChanges changes (2); transfer.transferChangesTo (changes); EXPECT_EQ (changes.getParameterCount (), 2); auto valueQueue1 = changes.getParameterData (0); EXPECT_NE (valueQueue1, nullptr); auto valueQueue2 = changes.getParameterData (1); EXPECT_NE (valueQueue2, nullptr); auto pid1 = valueQueue1->getParameterId (); auto pid2 = valueQueue2->getParameterId (); EXPECT (pid1 == ch1.id || pid1 == ch2.id); EXPECT (pid2 == ch1.id || pid2 == ch2.id); EXPECT_NE (pid1, pid2); ValuePoint vp1; ValuePoint vp2; if (pid1 == ch1.id) { EXPECT_EQ (valueQueue1->getPoint (0, vp1.sampleOffset, vp1.value), kResultTrue); EXPECT_EQ (valueQueue2->getPoint (0, vp2.sampleOffset, vp2.value), kResultTrue); } else { EXPECT_EQ (valueQueue2->getPoint (0, vp1.sampleOffset, vp1.value), kResultTrue); EXPECT_EQ (valueQueue1->getPoint (0, vp2.sampleOffset, vp2.value), kResultTrue); } return true; }); registerTest (TestSuiteName, STR ("Transfer changes from"), [] (ITestResult* testResult) { ParamChange ch1 {1, 0.8, 2}; ParamChange ch2 {2, 0.4, 8}; ParameterChangeTransfer transfer (2); ParameterChanges changes; int32 index {}; auto valueQueue = changes.addParameterData (ch1.id, index); EXPECT_NE (valueQueue, nullptr); EXPECT_EQ (valueQueue->addPoint (ch1.sampleOffset, ch1.value, index), kResultTrue); valueQueue = changes.addParameterData (ch2.id, index); EXPECT_NE (valueQueue, nullptr); EXPECT_EQ (valueQueue->addPoint (ch2.sampleOffset, ch2.value, index), kResultTrue); transfer.transferChangesFrom (changes); ParamChange test1 {}; ParamChange test2 {}; ParamChange test3 {}; EXPECT_TRUE (transfer.getNextChange (test1.id, test1.value, test1.sampleOffset)); EXPECT_TRUE (transfer.getNextChange (test2.id, test2.value, test2.sampleOffset)); EXPECT_FALSE (transfer.getNextChange (test3.id, test3.value, test3.sampleOffset)); EXPECT (test1 == ch1 || test1 == ch2); EXPECT (test2 == ch1 || test2 == ch2); EXPECT_NE (test1, test2); return true; }); }); //------------------------------------------------------------------------ } // anonymous } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/PaxHeaders/hostclassestest.cpp0000644000000000000000000000013215124701711026744 xustar0030 mtime=1767080905.281761729 30 atime=1767080905.281761729 30 ctime=1767080905.281761729 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/hostclassestest.cpp0000644000175000001440000001142515124701711026737 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/test/hostclassestest.cpp // Created by : Steinberg, 08/2021 // Description : Test host classes // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/hosting/hostclasses.h" #include "public.sdk/source/vst/utility/testing.h" #include "pluginterfaces/base/fstrdefs.h" #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace { //------------------------------------------------------------------------ ModuleInitializer HostApplicationTests ([] () { constexpr auto TestSuiteName = "HostApplication"; registerTest ( TestSuiteName, STR ("Create instance of IAttributeList"), [] (ITestResult* testResult) { HostApplication hostApp; FUnknown* instance {nullptr}; TUID iid; IAttributeList::iid.toTUID (iid); EXPECT_EQ (hostApp.createInstance (iid, iid, reinterpret_cast (&instance)), kResultTrue); EXPECT_NE (instance, nullptr); instance->release (); return true; }); registerTest (TestSuiteName, STR ("Create instance of IMessage"), [] (ITestResult* testResult) { HostApplication hostApp; FUnknown* instance {nullptr}; TUID iid; IMessage::iid.toTUID (iid); EXPECT_EQ (hostApp.createInstance (iid, iid, reinterpret_cast (&instance)), kResultTrue); EXPECT_NE (instance, nullptr); instance->release (); return true; }); }); //------------------------------------------------------------------------ ModuleInitializer HostAttributeListTests ([] () { constexpr auto TestSuiteName = "HostAttributeList"; registerTest (TestSuiteName, STR ("Int"), [] (ITestResult* testResult) { auto attrList = HostAttributeList::make (); constexpr int64 testValue = 5; EXPECT_EQ (attrList->setInt ("Int", testValue), kResultTrue); int64 value = 0; EXPECT_EQ (attrList->getInt ("Int", value), kResultTrue); EXPECT_EQ (value, testValue); return true; }); registerTest (TestSuiteName, STR ("Float"), [] (ITestResult* testResult) { auto attrList = HostAttributeList::make (); constexpr double testValue = 2.636; EXPECT_EQ (attrList->setFloat ("Float", testValue), kResultTrue); double value = 0; EXPECT_EQ (attrList->getFloat ("Float", value), kResultTrue); EXPECT_EQ (value, testValue); return true; }); registerTest (TestSuiteName, STR ("String"), [] (ITestResult* testResult) { auto attrList = HostAttributeList::make (); constexpr const TChar* testValue = STR ("TestValue"); EXPECT_EQ (attrList->setString ("Str", testValue), kResultTrue); TChar value[10]; EXPECT_EQ (attrList->getString ("Str", value, 10 * sizeof (TChar)), kResultTrue); EXPECT_EQ (tstrcmp (testValue, value), 0); return true; }); registerTest (TestSuiteName, STR ("Binary"), [] (ITestResult* testResult) { auto attrList = HostAttributeList::make (); std::array testData {}; int32 val = 0; for (auto item : testData) { item = val++; } uint32 testDataSize = static_cast(testData.size ()) * sizeof (int32); EXPECT_EQ (attrList->setBinary ("Binary", testData.data (), testDataSize), kResultTrue); const void* data; uint32 dataSize {0}; EXPECT_EQ (attrList->getBinary ("Binary", data, dataSize), kResultTrue); EXPECT_EQ (dataSize, testDataSize); auto s = reinterpret_cast (data); for (auto i : testData) { EXPECT_EQ (i, *s); s++; } return true; }); registerTest (TestSuiteName, STR ("Multiple Set"), [] (ITestResult* testResult) { auto attrList = HostAttributeList::make (); constexpr int64 testValue1 = 5; constexpr int64 testValue2 = 6; constexpr int64 testValue3 = 7; EXPECT_EQ (attrList->setInt ("Int", testValue1), kResultTrue); EXPECT_EQ (attrList->setInt ("Int", testValue2), kResultTrue); EXPECT_EQ (attrList->setInt ("Int", testValue3), kResultTrue); int64 value = 0; EXPECT_EQ (attrList->getInt ("Int", value), kResultTrue); EXPECT_EQ (value, testValue3); return true; }); }); //------------------------------------------------------------------------ } // anonymous } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/PaxHeaders/pluginterfacesupporttest.cpp0000644000000000000000000000013215124701711030676 xustar0030 mtime=1767080905.281761729 30 atime=1767080905.281761729 30 ctime=1767080905.281761729 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/test/pluginterfacesupporttest.cpp0000644000175000001440000000676415124701711030703 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/test/pluginterfacesupporttest.cpp // Created by : Steinberg, 08/2021 // Description : Test pluginterface support helper // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/hosting/pluginterfacesupport.h" #include "public.sdk/source/vst/utility/testing.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstmessage.h" #include "pluginterfaces/vst/ivstunits.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace { //------------------------------------------------------------------------ ModuleInitializer PlugInterfaceSupportTests ([] () { constexpr auto TestSuiteName = "PlugInterfaceSupport"; registerTest (TestSuiteName, STR ("Initial interfaces"), [] (ITestResult* testResult) { PlugInterfaceSupport pis; //---VST 3.0.0-------------------------------- EXPECT_EQ (pis.isPlugInterfaceSupported (IComponent::iid), kResultTrue); EXPECT_EQ (pis.isPlugInterfaceSupported (IAudioProcessor::iid), kResultTrue); EXPECT_EQ (pis.isPlugInterfaceSupported (IEditController::iid), kResultTrue); EXPECT_EQ (pis.isPlugInterfaceSupported (IConnectionPoint::iid), kResultTrue); EXPECT_EQ (pis.isPlugInterfaceSupported (IUnitInfo::iid), kResultTrue); EXPECT_EQ (pis.isPlugInterfaceSupported (IUnitData::iid), kResultTrue); EXPECT_EQ (pis.isPlugInterfaceSupported (IProgramListData::iid), kResultTrue); //---VST 3.0.1-------------------------------- EXPECT_EQ (pis.isPlugInterfaceSupported (IMidiMapping::iid), kResultTrue); //---VST 3.1---------------------------------- EXPECT_EQ (pis.isPlugInterfaceSupported (IEditController2::iid), kResultTrue); return true; }); registerTest (TestSuiteName, STR ("Add interface"), [] (ITestResult* testResult) { PlugInterfaceSupport pis; EXPECT_NE (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue); pis.addPlugInterfaceSupported (IEditControllerHostEditing::iid); EXPECT_EQ (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue); return true; }); registerTest (TestSuiteName, STR ("Remove interface"), [] (ITestResult* testResult) { PlugInterfaceSupport pis; EXPECT_NE (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue); pis.addPlugInterfaceSupported (IEditControllerHostEditing::iid); EXPECT_EQ (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue); EXPECT_TRUE (pis.removePlugInterfaceSupported (IEditControllerHostEditing::iid)); EXPECT_NE (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue); return true; }); }); //------------------------------------------------------------------------ } // anonymous } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/eventlist.h0000644000000000000000000000013215124701711024214 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/eventlist.h0000644000175000001440000000332615124701711024210 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/eventlist.h // Created by : Steinberg, 03/05/2008. // Description : VST 3 event list implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstevents.h" namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Example implementation of IEventList. \ingroup sdkBase */ class EventList : public IEventList { public: EventList (int32 maxSize = 50); virtual ~EventList (); int32 PLUGIN_API getEventCount () SMTG_OVERRIDE { return fillCount; } tresult PLUGIN_API getEvent (int32 index, Event& e) SMTG_OVERRIDE; tresult PLUGIN_API addEvent (Event& e) SMTG_OVERRIDE; void setMaxSize (int32 maxSize); void clear () { fillCount = 0; } Event* getEventByIndex (int32 index) const; //------------------------------------------------------------------------ DECLARE_FUNKNOWN_METHODS protected: Event* events {nullptr}; int32 maxSize {0}; int32 fillCount {0}; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/processdata.h0000644000000000000000000000013215124701711024507 xustar0030 mtime=1767080905.281210866 30 atime=1767080905.281210866 30 ctime=1767080905.281210866 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/processdata.h0000644000175000001440000001701315124701711024501 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/processdata.h // Created by : Steinberg, 10/2005 // Description : VST Hosting Utilities // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstcomponent.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Extension of ProcessData. Helps setting up the buffers for the process data structure for a component. When the prepare method is called with bufferSamples != 0 the buffer management is handled by this class. Otherwise the buffers need to be setup explicitly. \ingroup hostingBase */ class HostProcessData : public ProcessData { public: //------------------------------------------------------------------------ HostProcessData () = default; virtual ~HostProcessData () noexcept; /** Prepare buffer containers for all busses. If bufferSamples is not null buffers will be * created. */ bool prepare (IComponent& component, int32 bufferSamples, int32 _symbolicSampleSize); /** Remove bus buffers. */ void unprepare (); /** Sets one sample buffer for all channels inside a bus. */ bool setChannelBuffers (BusDirection dir, int32 busIndex, Sample32* sampleBuffer); bool setChannelBuffers64 (BusDirection dir, int32 busIndex, Sample64* sampleBuffer); /** Sets individual sample buffers per channel inside a bus. */ bool setChannelBuffers (BusDirection dir, int32 busIndex, Sample32* sampleBuffers[], int32 bufferCount); bool setChannelBuffers64 (BusDirection dir, int32 busIndex, Sample64* sampleBuffers[], int32 bufferCount); /** Sets one sample buffer for a given channel inside a bus. */ bool setChannelBuffer (BusDirection dir, int32 busIndex, int32 channelIndex, Sample32* sampleBuffer); bool setChannelBuffer64 (BusDirection dir, int32 busIndex, int32 channelIndex, Sample64* sampleBuffer); static constexpr uint64 kAllChannelsSilent = #if SMTG_OS_MACOS 0xffffffffffffffffULL; #else 0xffffffffffffffffUL; #endif //------------------------------------------------------------------------ protected: int32 createBuffers (IComponent& component, AudioBusBuffers*& buffers, BusDirection dir, int32 bufferSamples); void destroyBuffers (AudioBusBuffers*& buffers, int32& busCount); bool checkIfReallocationNeeded (IComponent& component, int32 bufferSamples, int32 _symbolicSampleSize) const; bool isValidBus (BusDirection dir, int32 busIndex) const; bool channelBufferOwner {false}; }; //------------------------------------------------------------------------ // inline //------------------------------------------------------------------------ inline bool HostProcessData::isValidBus (BusDirection dir, int32 busIndex) const { if (dir == kInput && (!inputs || busIndex >= numInputs)) return false; if (dir == kOutput && (!outputs || busIndex >= numOutputs)) return false; return true; } //------------------------------------------------------------------------ inline bool HostProcessData::setChannelBuffers (BusDirection dir, int32 busIndex, Sample32* sampleBuffer) { if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample32) return false; if (!isValidBus (dir, busIndex)) return false; AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; for (int32 i = 0; i < busBuffers.numChannels; i++) busBuffers.channelBuffers32[i] = sampleBuffer; return true; } //------------------------------------------------------------------------ inline bool HostProcessData::setChannelBuffers64 (BusDirection dir, int32 busIndex, Sample64* sampleBuffer) { if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample64) return false; if (!isValidBus (dir, busIndex)) return false; AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; for (int32 i = 0; i < busBuffers.numChannels; i++) busBuffers.channelBuffers64[i] = sampleBuffer; return true; } //------------------------------------------------------------------------ inline bool HostProcessData::setChannelBuffers (BusDirection dir, int32 busIndex, Sample32* sampleBuffers[], int32 bufferCount) { if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample32) return false; if (!isValidBus (dir, busIndex)) return false; AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; int32 count = bufferCount < busBuffers.numChannels ? bufferCount : busBuffers.numChannels; for (int32 i = 0; i < count; i++) busBuffers.channelBuffers32[i] = sampleBuffers ? sampleBuffers[i] : nullptr; return true; } //------------------------------------------------------------------------ inline bool HostProcessData::setChannelBuffers64 (BusDirection dir, int32 busIndex, Sample64* sampleBuffers[], int32 bufferCount) { if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample64) return false; if (!isValidBus (dir, busIndex)) return false; AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; int32 count = bufferCount < busBuffers.numChannels ? bufferCount : busBuffers.numChannels; for (int32 i = 0; i < count; i++) busBuffers.channelBuffers64[i] = sampleBuffers ? sampleBuffers[i] : nullptr; return true; } //------------------------------------------------------------------------ inline bool HostProcessData::setChannelBuffer (BusDirection dir, int32 busIndex, int32 channelIndex, Sample32* sampleBuffer) { if (symbolicSampleSize != SymbolicSampleSizes::kSample32) return false; if (!isValidBus (dir, busIndex)) return false; AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; if (channelIndex >= busBuffers.numChannels) return false; busBuffers.channelBuffers32[channelIndex] = sampleBuffer; return true; } //------------------------------------------------------------------------ inline bool HostProcessData::setChannelBuffer64 (BusDirection dir, int32 busIndex, int32 channelIndex, Sample64* sampleBuffer) { if (symbolicSampleSize != SymbolicSampleSizes::kSample64) return false; if (!isValidBus (dir, busIndex)) return false; AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex]; if (channelIndex >= busBuffers.numChannels) return false; busBuffers.channelBuffers64[channelIndex] = sampleBuffer; return true; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/parameterchanges.h0000644000000000000000000000013215124701711025510 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/parameterchanges.h0000644000175000001440000001003115124701711025473 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/parameterchanges.h // Created by : Steinberg, 03/05/2008. // Description : VST 3 parameter changes implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstparameterchanges.h" #include namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Implementation's example of IParamValueQueue - not threadsave!. \ingroup hostingBase */ class ParameterValueQueue : public IParamValueQueue { public: //------------------------------------------------------------------------ ParameterValueQueue (ParamID paramID); virtual ~ParameterValueQueue (); ParamID PLUGIN_API getParameterId () SMTG_OVERRIDE { return paramID; } int32 PLUGIN_API getPointCount () SMTG_OVERRIDE; tresult PLUGIN_API getPoint (int32 index, int32& sampleOffset, ParamValue& value) SMTG_OVERRIDE; tresult PLUGIN_API addPoint (int32 sampleOffset, ParamValue value, int32& index) SMTG_OVERRIDE; void setParamID (ParamID pID) {paramID = pID;} void clear (); //------------------------------------------------------------------------ DECLARE_FUNKNOWN_METHODS protected: ParamID paramID; struct ParameterQueueValue { ParameterQueueValue (ParamValue value, int32 sampleOffset) : value (value), sampleOffset (sampleOffset) {} ParamValue value; int32 sampleOffset; }; std::vector values; }; //------------------------------------------------------------------------ /** Implementation's example of IParameterChanges - not threadsave!. \ingroup hostingBase */ class ParameterChanges : public IParameterChanges { public: //------------------------------------------------------------------------ ParameterChanges (int32 maxParameters = 0); virtual ~ParameterChanges (); void clearQueue (); void setMaxParameters (int32 maxParameters); //---IParameterChanges----------------------------- int32 PLUGIN_API getParameterCount () SMTG_OVERRIDE; IParamValueQueue* PLUGIN_API getParameterData (int32 index) SMTG_OVERRIDE; IParamValueQueue* PLUGIN_API addParameterData (const ParamID& pid, int32& index) SMTG_OVERRIDE; //------------------------------------------------------------------------ DECLARE_FUNKNOWN_METHODS protected: std::vector> queues; int32 usedQueueCount {0}; }; //------------------------------------------------------------------------ /** Ring buffer for transferring parameter changes from a writer to a read thread . \ingroup hostingBase */ class ParameterChangeTransfer { public: //------------------------------------------------------------------------ ParameterChangeTransfer (int32 maxParameters = 0); virtual ~ParameterChangeTransfer (); void setMaxParameters (int32 maxParameters); void addChange (ParamID pid, ParamValue value, int32 sampleOffset); bool getNextChange (ParamID& pid, ParamValue& value, int32& sampleOffset); void transferChangesTo (ParameterChanges& dest); void transferChangesFrom (ParameterChanges& source); void removeChanges () { writeIndex = readIndex; } //------------------------------------------------------------------------ protected: struct ParameterChange { ParamID id; ParamValue value; int32 sampleOffset; }; int32 size; ParameterChange* changes; volatile int32 readIndex; volatile int32 writeIndex; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/parameterchanges.cpp0000644000000000000000000000013215124701711026043 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/parameterchanges.cpp0000644000175000001440000002015515124701711026036 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/parameterchanges.cpp // Created by : Steinberg, 03/05/2008. // Description : VST 3 parameter changes implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "parameterchanges.h" namespace Steinberg { namespace Vst { //----------------------------------------------------------------------------- IMPLEMENT_FUNKNOWN_METHODS (ParameterChanges, IParameterChanges, IParameterChanges::iid) IMPLEMENT_FUNKNOWN_METHODS (ParameterValueQueue, IParamValueQueue, IParamValueQueue::iid) constexpr int32 kQueueReservedPoints = 5; //----------------------------------------------------------------------------- ParameterValueQueue::ParameterValueQueue (ParamID paramID) : paramID (paramID) { values.reserve (kQueueReservedPoints); FUNKNOWN_CTOR } //----------------------------------------------------------------------------- ParameterValueQueue::~ParameterValueQueue () { FUNKNOWN_DTOR } //----------------------------------------------------------------------------- void ParameterValueQueue::clear () { values.clear (); } //----------------------------------------------------------------------------- int32 PLUGIN_API ParameterValueQueue::getPointCount () { return static_cast (values.size ()); } //----------------------------------------------------------------------------- tresult PLUGIN_API ParameterValueQueue::getPoint (int32 index, int32& sampleOffset, ParamValue& value) { if (index >= 0 && index < static_cast (values.size ())) { const ParameterQueueValue& queueValue = values[index]; sampleOffset = queueValue.sampleOffset; value = queueValue.value; return kResultTrue; } return kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API ParameterValueQueue::addPoint (int32 sampleOffset, ParamValue value, int32& index) { auto destIndex = static_cast(values.size ()); for (uint32 i = 0; i < values.size (); i++) { if (values[i].sampleOffset == sampleOffset) { values[i].value = value; index = i; return kResultTrue; } if (values[i].sampleOffset > sampleOffset) { destIndex = i; break; } } // need new point ParameterQueueValue queueValue (value, sampleOffset); if (destIndex == static_cast (values.size ())) values.emplace_back (queueValue); else values.insert (values.begin () + destIndex, queueValue); index = destIndex; return kResultTrue; } //----------------------------------------------------------------------------- // ParameterChanges //----------------------------------------------------------------------------- ParameterChanges::ParameterChanges (int32 maxParameters) { FUNKNOWN_CTOR setMaxParameters (maxParameters); } //----------------------------------------------------------------------------- ParameterChanges::~ParameterChanges () { FUNKNOWN_DTOR } //----------------------------------------------------------------------------- void ParameterChanges::setMaxParameters (int32 maxParameters) { if (maxParameters < 0) return; while (static_cast (queues.size ()) < maxParameters) { queues.emplace_back (owned (new ParameterValueQueue (kNoParamId))); } while (static_cast (queues.size ()) > maxParameters) { queues.pop_back (); } if (usedQueueCount > maxParameters) usedQueueCount = maxParameters; } //----------------------------------------------------------------------------- void ParameterChanges::clearQueue () { usedQueueCount = 0; } //----------------------------------------------------------------------------- int32 PLUGIN_API ParameterChanges::getParameterCount () { return usedQueueCount; } //----------------------------------------------------------------------------- IParamValueQueue* PLUGIN_API ParameterChanges::getParameterData (int32 index) { if (index >= 0 && index < usedQueueCount) return queues[index]; return nullptr; } //----------------------------------------------------------------------------- IParamValueQueue* PLUGIN_API ParameterChanges::addParameterData (const ParamID& pid, int32& index) { for (int32 i = 0; i < usedQueueCount; i++) { if (queues[i]->getParameterId () == pid) { index = i; return queues[i]; } } ParameterValueQueue* valueQueue = nullptr; if (usedQueueCount < static_cast (queues.size ())) { valueQueue = queues[usedQueueCount]; valueQueue->setParamID (pid); valueQueue->clear (); } else { queues.emplace_back (owned (new ParameterValueQueue (pid))); valueQueue = queues.back (); } index = usedQueueCount; usedQueueCount++; return valueQueue; } //----------------------------------------------------------------------------- // ParameterChangeTransfer //----------------------------------------------------------------------------- ParameterChangeTransfer::ParameterChangeTransfer (int32 maxParameters) : size (0) , changes (nullptr) , readIndex (0) , writeIndex (0) { setMaxParameters (maxParameters); } //----------------------------------------------------------------------------- ParameterChangeTransfer::~ParameterChangeTransfer () { setMaxParameters (0); } //----------------------------------------------------------------------------- void ParameterChangeTransfer::setMaxParameters (int32 maxParameters) { // reserve memory for twice the amount of all parameters int32 newSize = maxParameters * 2; if (size != newSize) { if (changes) delete [] changes; changes = nullptr; size = newSize; if (size > 0) changes = new ParameterChange [size]; } } //----------------------------------------------------------------------------- void ParameterChangeTransfer::addChange (ParamID pid, ParamValue value, int32 sampleOffset) { if (changes) { changes[writeIndex].id = pid; changes[writeIndex].value = value; changes[writeIndex].sampleOffset = sampleOffset; int32 newWriteIndex = writeIndex + 1; if (newWriteIndex >= size) newWriteIndex = 0; if (readIndex != newWriteIndex) writeIndex = newWriteIndex; } } //----------------------------------------------------------------------------- bool ParameterChangeTransfer::getNextChange (ParamID& pid, ParamValue& value, int32& sampleOffset) { if (!changes) return false; int32 currentWriteIndex = writeIndex; if (readIndex != currentWriteIndex) { pid = changes [readIndex].id; value = changes [readIndex].value; sampleOffset = changes [readIndex].sampleOffset; int32 newReadIndex = readIndex + 1; if (newReadIndex >= size) newReadIndex = 0; readIndex = newReadIndex; return true; } return false; } //----------------------------------------------------------------------------- void ParameterChangeTransfer::transferChangesTo (ParameterChanges& dest) { ParamID pid; ParamValue value; int32 sampleOffset; int32 index; while (getNextChange (pid, value, sampleOffset)) { IParamValueQueue* queue = dest.addParameterData (pid, index); if (queue) { queue->addPoint (sampleOffset, value, index); } } } //----------------------------------------------------------------------------- void ParameterChangeTransfer::transferChangesFrom (ParameterChanges& source) { ParamValue value; int32 sampleOffset; for (int32 i = 0; i < source.getParameterCount (); i++) { IParamValueQueue* queue = source.getParameterData (i); if (queue) { for (int32 j = 0; j < queue->getPointCount (); j++) { if (queue->getPoint (j, sampleOffset, value) == kResultTrue) { addChange (queue->getParameterId (), value, sampleOffset); } } } } } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/hostdataexchangehandler.h0000644000000000000000000000013215124701711027047 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/hostdataexchangehandler.h0000644000175000001440000001025415124701711027041 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // Category : Helpers // Filename : public.sdk/source/vst/hosting/hostdataexchangehandler.h // Created by : Steinberg, 06/2023 // Description : VST Data Exchange API Host Helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstdataexchange.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ struct IDataExchangeHandlerHost { virtual ~IDataExchangeHandlerHost () noexcept = default; /** return if the audioprocessor is in an inactive state * [main thread] */ virtual bool isProcessorInactive (IAudioProcessor* processor) = 0; /** return the data exchange receiver (most likely the edit controller) for the processor * [main thread] */ virtual IPtr findDataExchangeReceiver (IAudioProcessor* processor) = 0; /** check if the requested queue size should be allowed * [main thread] */ virtual bool allowAllocateSize (uint32 blockSize, uint32 numBlocks, uint32 alignment) = 0; /** check if this call is made on the main thread * [any thread] */ virtual bool isMainThread () = 0; /** check if the number of queues can be changed in this moment. * * this is only allowed if no other thread can access the IDataExchangeManagerHost in this * moment * [main thread] */ virtual bool allowQueueListResize (uint32 newNumQueues) = 0; /** notification that the number of open queues changed * [main thread] */ virtual void numberOfQueuesChanged (uint32 openMainThreadQueues, uint32 openBackgroundThreadQueues) = 0; /** notification that a new queue was opened */ virtual void onQueueOpened (IAudioProcessor* processor, DataExchangeQueueID queueID, bool dispatchOnMainThread) = 0; /** notification that a queue was closed */ virtual void onQueueClosed (IAudioProcessor* processor, DataExchangeQueueID queueID, bool dispatchOnMainThread) = 0; /** notification that a new block is ready to be send * [process thread] */ virtual void newBlockReadyToBeSend (DataExchangeQueueID queueID) = 0; }; //------------------------------------------------------------------------ struct HostDataExchangeHandler { /** Constructor * * allocate and deallocate this object on the main thread * * the number of queues is constant * * @param host the managing host * @param maxQueues number of maximal allowed open queues */ HostDataExchangeHandler (IDataExchangeHandlerHost& host, uint32 maxQueues = 64); ~HostDataExchangeHandler () noexcept; /** get the IHostDataExchangeManager interface * * the interface you must provide to the IAudioProcessor */ IDataExchangeHandler* getInterface () const; /** send blocks * * the host should periodically call this method on the main thread to send all queued blocks * which should be send on the main thread */ uint32 sendMainThreadBlocks (); /** send blocks * * the host should call this on a dedicated background thread * inside a mutex is used, so don't delete this object while calling this * * @param queueId only send blocks from the specified queue. If queueId is equal to * InvalidDataExchangeQueueID all blocks from all queues are send. */ uint32 sendBackgroundBlocks (DataExchangeQueueID queueId = InvalidDataExchangeQueueID); private: struct Impl; std::unique_ptr impl; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/module_win32.cpp0000644000000000000000000000013215124701711025041 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/module_win32.cpp0000644000175000001440000005074415124701711025043 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/module_win32.cpp // Created by : Steinberg, 08/2016 // Description : hosting module classes (win32 implementation) // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "module.h" #include "public.sdk/source/vst/utility/optional.h" #include "public.sdk/source/vst/utility/stringconvert.h" #include "pluginterfaces/base/funknownimpl.h" #include #include #include #include #if SMTG_CPP17 #if __has_include() #define USE_FILESYSTEM 1 #elif __has_include() #define USE_FILESYSTEM 0 #endif #else // !SMTG_CPP17 #define USE_FILESYSTEM 0 #endif // SMTG_CPP17 #if USE_FILESYSTEM == 1 #include namespace filesystem = std::filesystem; #else // USE_FILESYSTEM == 0 // The header is deprecated. It is superseded by the C++17 // header. You can define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING to silence the // warning, otherwise the build will fail in VS2019 16.3.0 #define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING #include namespace filesystem = std::experimental::filesystem; #endif // USE_FILESYSTEM #pragma comment(lib, "Shell32") //------------------------------------------------------------------------ extern "C" { using InitModuleFunc = bool (PLUGIN_API*) (); using ExitModuleFunc = bool (PLUGIN_API*) (); } //------------------------------------------------------------------------ namespace VST3 { namespace Hosting { constexpr unsigned long kIPPathNameMax = 1024; //------------------------------------------------------------------------ namespace { #define USE_OLE !USE_FILESYSTEM // for testing only #if 0 // DEVELOPMENT #define LOG_ENABLE 1 #else #define LOG_ENABLE 0 #endif #if SMTG_PLATFORM_64 #if SMTG_OS_WINDOWS_ARM #if SMTG_CPU_ARM_64EC constexpr auto architectureString = "arm64ec-win"; constexpr auto architectureX64String = "x86_64-win"; #else // !SMTG_CPU_ARM_64EC constexpr auto architectureString = "arm64-win"; #endif // SMTG_CPU_ARM_64EC constexpr auto architectureArm64XString = "arm64x-win"; #else // !SMTG_OS_WINDOWS_ARM constexpr auto architectureString = "x86_64-win"; #endif // SMTG_OS_WINDOWS_ARM #else // !SMTG_PLATFORM_64 #if SMTG_OS_WINDOWS_ARM constexpr auto architectureString = "arm-win"; #else // !SMTG_OS_WINDOWS_ARM constexpr auto architectureString = "x86-win"; #endif // SMTG_OS_WINDOWS_ARM #endif // SMTG_PLATFORM_64 #if USE_OLE //------------------------------------------------------------------------ struct Ole { static Ole& instance () { static Ole gInstance; return gInstance; } private: Ole () { OleInitialize (nullptr); } ~Ole () { OleUninitialize (); } }; #endif // USE_OLE //------------------------------------------------------------------------ class Win32Module : public Module { public: template T getFunctionPointer (const char* name) { return reinterpret_cast (GetProcAddress (mModule, name)); } ~Win32Module () override { factory = PluginFactory (nullptr); if (mModule) { // ExitDll is optional if (auto dllExit = getFunctionPointer ("ExitDll")) dllExit (); FreeLibrary ((HMODULE)mModule); } } //--- ----------------------------------------------------------------------- HINSTANCE loadAsPackage (const std::string& inPath, std::string& errorDescription, const char* archString = architectureString) { namespace StringConvert = Steinberg::Vst::StringConvert; filesystem::path p (inPath); auto filename = p.filename (); p /= "Contents"; p /= archString; p /= filename; const std::wstring wString = p.generic_wstring (); HINSTANCE instance = LoadLibraryW (reinterpret_cast (wString.data ())); #if SMTG_CPU_ARM_64EC if (instance == nullptr) instance = loadAsPackage (inPath, errorDescription, architectureArm64XString); if (instance == nullptr) instance = loadAsPackage (inPath, errorDescription, architectureX64String); #endif // SMTG_CPU_ARM_64EC if (instance == nullptr) getLastError (p.string (), errorDescription); return instance; } //--- ----------------------------------------------------------------------- HINSTANCE loadAsDll (const std::string& inPath, std::string& errorDescription) { namespace StringConvert = Steinberg::Vst::StringConvert; auto wideStr = StringConvert::convert (inPath); HINSTANCE instance = LoadLibraryW (reinterpret_cast (wideStr.data ())); if (instance == nullptr) { getLastError (inPath, errorDescription); } else { hasBundleStructure = false; } return instance; } //--- ----------------------------------------------------------------------- bool load (const std::string& inPath, std::string& errorDescription) override { // filesystem::u8path is deprecated in C++20 #if SMTG_CPP20 const filesystem::path tmp (inPath); #else const filesystem::path tmp = filesystem::u8path (inPath); #endif // SMTG_CPP20 std::error_code ec; if (filesystem::is_directory (tmp, ec)) { // try as package (bundle) mModule = loadAsPackage (inPath, errorDescription); } else { // try old definition without package mModule = loadAsDll (inPath, errorDescription); } if (mModule == nullptr) return false; auto factoryProc = getFunctionPointer ("GetPluginFactory"); if (!factoryProc) { errorDescription = "The dll does not export the required 'GetPluginFactory' function"; return false; } // InitDll is optional auto dllEntry = getFunctionPointer ("InitDll"); if (dllEntry && !dllEntry ()) { errorDescription = "Calling 'InitDll' failed"; return false; } auto f = Steinberg::U::cast (owned (factoryProc ())); if (!f) { errorDescription = "Calling 'GetPluginFactory' returned nullptr"; return false; } factory = PluginFactory (f); return true; } HINSTANCE mModule {nullptr}; private: //--- ----------------------------------------------------------------------- void getLastError (const std::string& inPath, std::string& errorDescription) { auto lastError = GetLastError (); LPVOID lpMessageBuffer {nullptr}; if (FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, lastError, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMessageBuffer, 0, nullptr) > 0) { errorDescription = "LoadLibraryW failed for path " + inPath + ": " + std::string ((char*)lpMessageBuffer); LocalFree (lpMessageBuffer); } else { errorDescription = "LoadLibraryW failed with error number: " + std::to_string (lastError) + " for path " + inPath; } } }; //------------------------------------------------------------------------ bool openVST3Package (const filesystem::path& p, const char* archString, filesystem::path* result = nullptr) { auto path = p; path /= "Contents"; path /= archString; path /= p.filename (); const std::wstring wString = path.generic_wstring (); auto hFile = CreateFileW (reinterpret_cast (wString.data ()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (hFile != INVALID_HANDLE_VALUE) { CloseHandle (hFile); if (result) *result = path; return true; } return false; } //------------------------------------------------------------------------ bool checkVST3Package (const filesystem::path& p, filesystem::path* result = nullptr, const char* archString = architectureString) { if (openVST3Package (p, archString, result)) return true; #if SMTG_CPU_ARM_64EC if (openVST3Package (p, architectureArm64XString, result)) return true; if (openVST3Package (p, architectureX64String, result)) return true; #endif // SMTG_CPU_ARM_64EC return false; } //------------------------------------------------------------------------ bool isFolderSymbolicLink (const filesystem::path& p) { #if USE_FILESYSTEM std::error_code ec; if (filesystem::is_symlink (p, ec)) return true; #else const std::wstring wString = p.generic_wstring (); auto attrib = GetFileAttributesW (reinterpret_cast (wString.data ())); if (attrib & FILE_ATTRIBUTE_REPARSE_POINT) { auto hFile = CreateFileW (reinterpret_cast (wString.data ()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (hFile == INVALID_HANDLE_VALUE) return true; CloseHandle (hFile); } #endif // USE_FILESYSTEM return false; } //------------------------------------------------------------------------ Optional getKnownFolder (REFKNOWNFOLDERID folderID) { namespace StringConvert = Steinberg::Vst::StringConvert; PWSTR wideStr {}; if (FAILED (SHGetKnownFolderPath (folderID, 0, nullptr, &wideStr))) return {}; return StringConvert::convert (Steinberg::wscast (wideStr)); } //------------------------------------------------------------------------ VST3::Optional resolveShellLink (const filesystem::path& p) { #if USE_FILESYSTEM std::error_code ec; auto target = filesystem::read_symlink (p, ec); if (ec) return {}; else return { target.lexically_normal () }; #elif USE_OLE Ole::instance (); IShellLink* shellLink = nullptr; if (!SUCCEEDED (CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast (&shellLink)))) return {}; IPersistFile* persistFile = nullptr; if (!SUCCEEDED ( shellLink->QueryInterface (IID_IPersistFile, reinterpret_cast (&persistFile)))) return {}; if (!SUCCEEDED (persistFile->Load (p.wstring ().data (), STGM_READ))) return {}; if (!SUCCEEDED (shellLink->Resolve (nullptr, MAKELONG (SLR_NO_UI, 500)))) return {}; WCHAR resolvedPath[kIPPathNameMax]; if (!SUCCEEDED (shellLink->GetPath (resolvedPath, kIPPathNameMax, nullptr, SLGP_SHORTPATH))) return {}; std::wstring longPath; longPath.resize (kIPPathNameMax); auto numChars = GetLongPathNameW (resolvedPath, const_cast (longPath.data ()), kIPPathNameMax); if (!numChars) return {}; longPath.resize (numChars); persistFile->Release (); shellLink->Release (); return {filesystem::path (longPath)}; #else return {}; #endif // USE_FILESYSTEM } //------------------------------------------------------------------------ void addToPathList (Module::PathList& pathList, const std::string& toAdd) { #if LOG_ENABLE std::cout << "=> add: " << toAdd << "\n"; #endif pathList.push_back (toAdd); } //------------------------------------------------------------------------ void findFilesWithExt (const filesystem::path& path, const std::string& ext, Module::PathList& pathList, bool recursive = true) { for (auto& p : filesystem::directory_iterator (path)) { #if USE_FILESYSTEM filesystem::path finalPath (p); if (isFolderSymbolicLink (p)) { if (auto res = resolveShellLink (p)) { finalPath = *res; std::error_code ec; if (!filesystem::exists (finalPath, ec)) continue; } else continue; } const auto& cpExt = finalPath.extension (); if (cpExt == ext) { filesystem::path result; if (checkVST3Package (finalPath, &result)) { #if SMTG_CPP20 std::u8string u8str = result.generic_u8string (); std::string str; str.assign (std::begin (u8str), std::end (u8str)); addToPathList (pathList, str); #else addToPathList (pathList, result.generic_u8string ()); #endif // SMTG_CPP20 continue; } } std::error_code ec; if (filesystem::is_directory (finalPath, ec)) { if (recursive) findFilesWithExt (finalPath, ext, pathList, recursive); } else if (cpExt == ext) { #if SMTG_CPP20 std::u8string u8str = finalPath.generic_u8string (); std::string str; str.assign (std::begin (u8str), std::end (u8str)); addToPathList (pathList, str); #else addToPathList (pathList, finalPath.generic_u8string ()); #endif // SMTG_CPP20 } #else // !USE_FILESYSTEM const auto& cp = p.path (); const auto& cpExt = cp.extension (); if (cpExt == ext) { if ((p.status ().type () == filesystem::file_type::directory) || isFolderSymbolicLink (p)) { filesystem::path result; if (checkVST3Package (p, &result)) { addToPathList (pathList, result.generic_u8string ()); continue; } findFilesWithExt (cp, ext, pathList, recursive); } else addToPathList (pathList, cp.generic_u8string ()); } else if (recursive) { if (p.status ().type () == filesystem::file_type::directory) { findFilesWithExt (cp, ext, pathList, recursive); } else if (cpExt == ".lnk") { if (auto resolvedLink = resolveShellLink (cp)) { if (resolvedLink->extension () == ext) { if (filesystem::is_directory (*resolvedLink) || isFolderSymbolicLink (*resolvedLink)) { filesystem::path result; if (checkVST3Package (*resolvedLink, &result)) { addToPathList (pathList, result.generic_u8string ()); continue; } findFilesWithExt (*resolvedLink, ext, pathList, recursive); } else addToPathList (pathList, resolvedLink->generic_u8string ()); } else if (filesystem::is_directory (*resolvedLink)) { const auto& str = resolvedLink->generic_u8string (); if (cp.generic_u8string ().compare (0, str.size (), str.data (), str.size ()) != 0) findFilesWithExt (*resolvedLink, ext, pathList, recursive); } } } } #endif // USE_FILESYSTEM } } //------------------------------------------------------------------------ void findModules (const filesystem::path& path, Module::PathList& pathList) { std::error_code ec; if (filesystem::exists (path, ec)) findFilesWithExt (path, ".vst3", pathList); } //------------------------------------------------------------------------ Optional getContentsDirectoryFromModuleExecutablePath ( const std::string& modulePath) { // filesystem::u8path is deprecated in C++20 #if SMTG_CPP20 filesystem::path path (modulePath); #else filesystem::path path = filesystem::u8path (modulePath); #endif // SMTG_CPP20 path = path.parent_path (); if (path.filename () != architectureString) return {}; path = path.parent_path (); if (path.filename () != "Contents") return {}; return Optional {std::move (path)}; } //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ Module::Ptr Module::create (const std::string& path, std::string& errorDescription) { auto _module = std::make_shared (); if (_module->load (path, errorDescription)) { _module->path = path; auto it = std::find_if (path.rbegin (), path.rend (), [] (const std::string::value_type& c) { return c == '/'; }); if (it != path.rend ()) _module->name = {it.base (), path.end ()}; return _module; } return nullptr; } //------------------------------------------------------------------------ Module::PathList Module::getModulePaths () { namespace StringConvert = Steinberg::Vst::StringConvert; // find plug-ins located in common/VST3 PathList list; if (auto knownFolder = getKnownFolder (FOLDERID_UserProgramFilesCommon)) { // filesystem::u8path is deprecated in C++20 #if SMTG_CPP20 filesystem::path path (*knownFolder); #else filesystem::path path = filesystem::u8path (*knownFolder); #endif // SMTG_CPP20 path.append ("VST3"); #if LOG_ENABLE std::cout << "Check folder: " << path << "\n"; #endif findModules (path, list); } if (auto knownFolder = getKnownFolder (FOLDERID_ProgramFilesCommon)) { // filesystem::u8path is deprecated in C++20 #if SMTG_CPP20 filesystem::path path (*knownFolder); #else filesystem::path path = filesystem::u8path (*knownFolder); #endif // SMTG_CPP20 path.append ("VST3"); #if LOG_ENABLE std::cout << "Check folder: " << path << "\n"; #endif findModules (path, list); } // find plug-ins located in VST3 (application folder) WCHAR modulePath[kIPPathNameMax]; GetModuleFileNameW (nullptr, modulePath, kIPPathNameMax); auto appPath = StringConvert::convert (Steinberg::wscast (modulePath)); // filesystem::u8path is deprecated in C++20 #if SMTG_CPP20 filesystem::path path (appPath); #else filesystem::path path = filesystem::u8path (appPath); #endif // SMTG_CPP20 path = path.parent_path (); path = path.append ("VST3"); #if LOG_ENABLE std::cout << "Check folder: " << path << "\n"; #endif findModules (path, list); return list; } //------------------------------------------------------------------------ Optional Module::getModuleInfoPath (const std::string& modulePath) { auto path = getContentsDirectoryFromModuleExecutablePath (modulePath); if (!path) { filesystem::path p; if (!checkVST3Package ({modulePath}, &p)) return {}; p = p.parent_path (); p = p.parent_path (); path = Optional {p}; } *path /= "Resources"; *path /= "moduleinfo.json"; std::error_code ec; if (filesystem::exists (*path, ec)) { return {path->generic_string ()}; } return {}; } //------------------------------------------------------------------------ bool Module::validateBundleStructure (const std::string& modulePath, std::string& errorDescription) { try { auto path = getContentsDirectoryFromModuleExecutablePath (modulePath); if (!path) { filesystem::path p; if (!checkVST3Package ({modulePath}, &p)) { errorDescription = "Not a bundle: '" + modulePath + "'."; return false; } p = p.parent_path (); p = p.parent_path (); path = Optional {p}; } if (path->filename () != "Contents") { errorDescription = "Unexpected directory name, should be 'Contents' but is '" + path->filename ().string () + "'."; return false; } auto bundlePath = path->parent_path (); *path /= architectureString; *path /= bundlePath.filename (); std::error_code ec; if (filesystem::exists (*path, ec) == false) { errorDescription = "Shared library name is not equal to bundle folder name. Must be '" + bundlePath.filename ().string () + "'."; return false; } return true; } catch (const std::exception& exc) { errorDescription = exc.what (); return false; } } //------------------------------------------------------------------------ Module::SnapshotList Module::getSnapshots (const std::string& modulePath) { SnapshotList result; auto path = getContentsDirectoryFromModuleExecutablePath (modulePath); if (!path) { filesystem::path p; if (!checkVST3Package ({modulePath}, &p)) return result; p = p.parent_path (); p = p.parent_path (); path = Optional (p); } *path /= "Resources"; *path /= "Snapshots"; std::error_code ec; if (filesystem::exists (*path, ec) == false) return result; PathList pngList; findFilesWithExt (*path, ".png", pngList, false); for (auto& png : pngList) { // filesystem::u8path is deprecated in C++20 #if SMTG_CPP20 const filesystem::path p (png); #else const filesystem::path p = filesystem::u8path (png); #endif // SMTG_CPP20 auto filename = p.filename ().generic_string (); auto uid = Snapshot::decodeUID (filename); if (!uid) continue; auto scaleFactor = 1.; if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename)) scaleFactor = *decodedScaleFactor; Module::Snapshot::ImageDesc desc; desc.scaleFactor = scaleFactor; desc.path = std::move (png); bool found = false; for (auto& entry : result) { if (entry.uid != *uid) continue; found = true; entry.images.emplace_back (std::move (desc)); break; } if (found) continue; Module::Snapshot snapshot; snapshot.uid = *uid; snapshot.images.emplace_back (std::move (desc)); result.emplace_back (std::move (snapshot)); } return result; } //------------------------------------------------------------------------ } // Hosting } // VST3 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/processdata.cpp0000644000000000000000000000013215124701711025042 xustar0030 mtime=1767080905.281210866 30 atime=1767080905.281210866 30 ctime=1767080905.281210866 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/processdata.cpp0000644000175000001440000001304415124701711025034 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/processdata.cpp // Created by : Steinberg, 10/2005 // Description : VST Hosting Utilities // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "processdata.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // HostProcessData //------------------------------------------------------------------------ HostProcessData::~HostProcessData () noexcept { unprepare (); } //------------------------------------------------------------------------ bool HostProcessData::prepare (IComponent& component, int32 bufferSamples, int32 _symbolicSampleSize) { if (checkIfReallocationNeeded (component, bufferSamples, _symbolicSampleSize)) { unprepare (); symbolicSampleSize = _symbolicSampleSize; channelBufferOwner = bufferSamples > 0; numInputs = createBuffers (component, inputs, kInput, bufferSamples); numOutputs = createBuffers (component, outputs, kOutput, bufferSamples); } else { // reset silence flags for (int32 i = 0; i < numInputs; i++) { inputs[i].silenceFlags = 0; } for (int32 i = 0; i < numOutputs; i++) { outputs[i].silenceFlags = 0; } } symbolicSampleSize = _symbolicSampleSize; return true; } //------------------------------------------------------------------------ void HostProcessData::unprepare () { destroyBuffers (inputs, numInputs); destroyBuffers (outputs, numOutputs); channelBufferOwner = false; } //------------------------------------------------------------------------ bool HostProcessData::checkIfReallocationNeeded (IComponent& component, int32 bufferSamples, int32 _symbolicSampleSize) const { if (channelBufferOwner != (bufferSamples > 0)) return true; if (symbolicSampleSize != _symbolicSampleSize) return true; int32 inBusCount = component.getBusCount (kAudio, kInput); if (inBusCount != numInputs) return true; int32 outBusCount = component.getBusCount (kAudio, kOutput); if (outBusCount != numOutputs) return true; for (int32 i = 0; i < inBusCount; i++) { BusInfo busInfo = {}; if (component.getBusInfo (kAudio, kInput, i, busInfo) == kResultTrue) { if (inputs[i].numChannels != busInfo.channelCount) return true; } } for (int32 i = 0; i < outBusCount; i++) { BusInfo busInfo = {}; if (component.getBusInfo (kAudio, kOutput, i, busInfo) == kResultTrue) { if (outputs[i].numChannels != busInfo.channelCount) return true; } } return false; } //----------------------------------------------------------------------------- int32 HostProcessData::createBuffers (IComponent& component, AudioBusBuffers*& buffers, BusDirection dir, int32 bufferSamples) { int32 busCount = component.getBusCount (kAudio, dir); if (busCount > 0) { buffers = new AudioBusBuffers[busCount]; for (int32 i = 0; i < busCount; i++) { BusInfo busInfo = {}; if (component.getBusInfo (kAudio, dir, i, busInfo) == kResultTrue) { buffers[i].numChannels = busInfo.channelCount; // allocate for each channel if (busInfo.channelCount > 0) { if (symbolicSampleSize == kSample64) buffers[i].channelBuffers64 = new Sample64*[busInfo.channelCount]; else buffers[i].channelBuffers32 = new Sample32*[busInfo.channelCount]; for (int32 j = 0; j < busInfo.channelCount; j++) { if (symbolicSampleSize == kSample64) { if (bufferSamples > 0) buffers[i].channelBuffers64[j] = new Sample64[bufferSamples]; else buffers[i].channelBuffers64[j] = nullptr; } else { if (bufferSamples > 0) buffers[i].channelBuffers32[j] = new Sample32[bufferSamples]; else buffers[i].channelBuffers32[j] = nullptr; } } } } } } return busCount; } //----------------------------------------------------------------------------- void HostProcessData::destroyBuffers (AudioBusBuffers*& buffers, int32& busCount) { if (buffers) { for (int32 i = 0; i < busCount; i++) { if (channelBufferOwner) { for (int32 j = 0; j < buffers[i].numChannels; j++) { if (symbolicSampleSize == kSample64) { if (buffers[i].channelBuffers64 && buffers[i].channelBuffers64[j]) delete[] buffers[i].channelBuffers64[j]; } else { if (buffers[i].channelBuffers32 && buffers[i].channelBuffers32[j]) delete[] buffers[i].channelBuffers32[j]; } } } if (symbolicSampleSize == kSample64) { if (buffers[i].channelBuffers64) delete[] buffers[i].channelBuffers64; } else { if (buffers[i].channelBuffers32) delete[] buffers[i].channelBuffers32; } } delete[] buffers; buffers = nullptr; } busCount = 0; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/plugprovider.cpp0000644000000000000000000000013215124701711025254 xustar0030 mtime=1767080905.281210866 30 atime=1767080905.281210866 30 ctime=1767080905.281210866 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/plugprovider.cpp0000644000175000001440000002056415124701711025253 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/plugprovider.cpp // Created by : Steinberg, 08/2016 // Description : VST 3 Plug-in Provider class // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "plugprovider.h" #include "connectionproxy.h" #include "pluginterfaces/vst/ivstcomponent.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstmessage.h" #include #include static std::ostream* errorStream = &std::cout; //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // PlugProvider //------------------------------------------------------------------------ PlugProvider::PlugProvider (const PluginFactory& factory, ClassInfo classInfo, bool plugIsGlobal) : factory (factory) , component (nullptr) , controller (nullptr) , classInfo (classInfo) , plugIsGlobal (plugIsGlobal) { } //------------------------------------------------------------------------ PlugProvider::~PlugProvider () { terminatePlugin (); } //------------------------------------------------------------------------ template void PlugProvider::printError (Proc p) const { if (errorStream) { p (*errorStream); } } //------------------------------------------------------------------------ bool PlugProvider::initialize () { if (plugIsGlobal) { return setupPlugin (PluginContextFactory::instance ().getPluginContext ()); } return true; } //------------------------------------------------------------------------ IComponent* PLUGIN_API PlugProvider::getComponent () { if (!component) setupPlugin (PluginContextFactory::instance ().getPluginContext ()); if (component) component->addRef (); return component; } //------------------------------------------------------------------------ IEditController* PLUGIN_API PlugProvider::getController () { if (controller) controller->addRef (); // 'iController == 0' is allowed! In this case the plug has no controller return controller; } //------------------------------------------------------------------------ IPluginFactory* PLUGIN_API PlugProvider::getPluginFactory () { if (auto f = factory.get ()) return f.get (); return nullptr; } //------------------------------------------------------------------------ tresult PLUGIN_API PlugProvider::getComponentUID (FUID& uid) const { uid = FUID::fromTUID (classInfo.ID ().data ()); return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API PlugProvider::releasePlugIn (IComponent* iComponent, IEditController* iController) { if (iComponent) iComponent->release (); if (iController) iController->release (); if (!plugIsGlobal) { terminatePlugin (); } return kResultOk; } //------------------------------------------------------------------------ bool PlugProvider::setupPlugin (FUnknown* hostContext) { bool res = false; bool isSingleComponent = false; //---create Plug-in here!-------------- // create its component part component = factory.createInstance (classInfo.ID ()); if (component) { // initialize the component with our context if (auto plugBase = U::cast (component)) { res = (plugBase->initialize (hostContext) == kResultOk); if (res == false) { printError ([&] (std::ostream& stream) { stream << "Failed to initialize component of " << classInfo.name () << "!\n"; }); return false; } } else { printError ([&] (std::ostream& stream) { stream << "Failed to get IPluginBase from component of " << classInfo.name () << "!\n"; }); return false; } // try to create the controller part from the component // (for Plug-ins which did not succeed to separate component from controller) if (component->queryInterface (IEditController::iid, (void**)&controller) == kResultTrue) { isSingleComponent = true; } else { TUID controllerCID; // ask for the associated controller class ID if (component->getControllerClassId (controllerCID) == kResultTrue) { // create its controller part created from the factory controller = factory.createInstance (VST3::UID (controllerCID)); if (controller) { // initialize the component with our context if (auto plugCtrlBase = U::cast (controller)) { res = (plugCtrlBase->initialize (hostContext) == kResultOk); if (res == false) { printError ([&] (std::ostream& stream) { stream << "Failed to initialize controller of " << classInfo.name () << "!\n"; }); } } else { printError ([&] (std::ostream& stream) { stream << "Failed to get IPluginBase from controller of " << classInfo.name () << "!\n"; }); return false; } } } else { printError ([&] (std::ostream& stream) { stream << "Component does not provide a required controller class ID [" << classInfo.name () << "]!\n"; }); } } if (!res) { component.reset (); controller.reset (); } } else if (errorStream) { printError ([&] (std::ostream& stream) { stream << "Failed to create component instance of " << classInfo.name () << "!\n"; }); } if (res && !isSingleComponent) return connectComponents (); return res; } //------------------------------------------------------------------------ bool PlugProvider::connectComponents () { if (!component || !controller) return false; auto compICP = U::cast (component); auto contrICP = U::cast (controller); if (!compICP || !contrICP) return false; componentCP = owned (new ConnectionProxy (compICP)); controllerCP = owned (new ConnectionProxy (contrICP)); tresult tres = componentCP->connect (contrICP); if (tres != kResultTrue) { printError ([&] (std::ostream& stream) { stream << "Failed to connect the component with the controller with result code '" << tres << "'!\n"; }); return false; } tres = controllerCP->connect (compICP); if (tres != kResultTrue) { printError ([&] (std::ostream& stream) { stream << "Failed to connect the controller with the component with result code '" << tres << "'!\n"; }); return false; } return true; } //------------------------------------------------------------------------ bool PlugProvider::disconnectComponents () { if (!componentCP || !controllerCP) return false; bool res = componentCP->disconnect (); res &= controllerCP->disconnect (); componentCP.reset (); controllerCP.reset (); return res; } //------------------------------------------------------------------------ void PlugProvider::terminatePlugin () { disconnectComponents (); bool controllerIsComponent = false; if (component) { controllerIsComponent = FUnknownPtr (component).getInterface () != nullptr; if (auto plugBase = U::cast (component)) plugBase->terminate (); else { printError ([&](std::ostream& stream) { stream << "Failed to get IPluginBase from component of " << classInfo.name () << "!\n"; }); } } if (controller && controllerIsComponent == false) { if (auto plugCtrlBase = U::cast (controller)) plugCtrlBase->terminate (); else { printError ([&](std::ostream& stream) { stream << "Failed to get IPluginBase from controller of " << classInfo.name () << "!\n"; }); } } component.reset (); controller.reset (); } //------------------------------------------------------------------------ void PlugProvider::setErrorStream (std::ostream* stream) { errorStream = stream; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/eventlist.cpp0000644000000000000000000000013215124701711024547 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/eventlist.cpp0000644000175000001440000000466315124701711024550 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/eventlist.cpp // Created by : Steinberg, 03/05/2008. // Description : VST 3 event list implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "eventlist.h" namespace Steinberg { namespace Vst { //----------------------------------------------------------------------------- IMPLEMENT_FUNKNOWN_METHODS (EventList, IEventList, IEventList::iid) //----------------------------------------------------------------------------- EventList::EventList (int32 inMaxSize) { FUNKNOWN_CTOR setMaxSize (inMaxSize); } //----------------------------------------------------------------------------- EventList::~EventList () { setMaxSize (0); FUNKNOWN_DTOR } //----------------------------------------------------------------------------- void EventList::setMaxSize (int32 newMaxSize) { if (events) { delete[] events; events = nullptr; fillCount = 0; } if (newMaxSize > 0) events = new Event[newMaxSize]; maxSize = newMaxSize; } //----------------------------------------------------------------------------- tresult PLUGIN_API EventList::getEvent (int32 index, Event& e) { if (auto event = getEventByIndex (index)) { memcpy (&e, event, sizeof (Event)); return kResultTrue; } return kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API EventList::addEvent (Event& e) { if (maxSize > fillCount) { memcpy (&events[fillCount], &e, sizeof (Event)); fillCount++; return kResultTrue; } return kResultFalse; } //----------------------------------------------------------------------------- Event* EventList::getEventByIndex (int32 index) const { if (index < fillCount) return &events[index]; return nullptr; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/pluginterfacesupport.h0000644000000000000000000000013215124701711026464 xustar0030 mtime=1767080905.281210866 30 atime=1767080905.281210866 30 ctime=1767080905.281210866 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/pluginterfacesupport.h0000644000175000001440000000322115124701711026452 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/pluginterfacesupport.h // Created by : Steinberg, 11/20018. // Description : VST 3 hostclasses, example implementations for IPlugInterfaceSupport // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstpluginterfacesupport.h" #include namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Example implementation of IPlugInterfaceSupport. \ingroup hostingBase */ class PlugInterfaceSupport : public IPlugInterfaceSupport { public: PlugInterfaceSupport (); virtual ~PlugInterfaceSupport () = default; //--- IPlugInterfaceSupport --------- tresult PLUGIN_API isPlugInterfaceSupported (const TUID _iid) SMTG_OVERRIDE; void addPlugInterfaceSupported (const TUID _iid); bool removePlugInterfaceSupported (const TUID _iid); DECLARE_FUNKNOWN_METHODS private: std::vector mFUIDArray; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/hostdataexchangehandler.cpp0000644000000000000000000000013215124701711027402 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/hostdataexchangehandler.cpp0000644000175000001440000002666415124701711027410 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // Category : Helpers // Filename : public.sdk/source/vst/hosting/hostdataexchangehandler.cpp // Created by : Steinberg, 06/2023 // Description : VST Data Exchange API Host Helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "hostdataexchangehandler.h" #include "../utility/alignedalloc.h" #include "../utility/ringbuffer.h" #include "pluginterfaces/base/funknownimpl.h" #include #include #include #include #ifdef _MSC_VER #include #endif //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ struct HostDataExchangeHandler::Impl : U::ImplementsNonDestroyable> { struct Block { Block () = default; Block (uint32 blockSize, uint32 alignment, DataExchangeBlockID id) : blockID (id), alignment (alignment) { data = aligned_alloc (blockSize, alignment); } Block (Block&& other) { *this = std::move (other); } ~Block () noexcept { if (data) aligned_free (data, alignment); } Block& operator= (Block&& other) { data = other.data; other.data = nullptr; blockID = other.blockID; other.blockID = InvalidDataExchangeBlockID; alignment = other.alignment; return *this; } void* data {nullptr}; DataExchangeBlockID blockID {InvalidDataExchangeBlockID}; uint32 alignment {0}; }; struct Queue { using BlockRingBuffer = OneReaderOneWriter::RingBuffer; // we do the assumption that the std::vector does not allocate memory when we don't push // more items than we reserve before using BlockVector = std::vector; Queue (IAudioProcessor* owner, IDataExchangeReceiver* receiver, DataExchangeUserContextID userContext, uint32 blockSize, uint32 numBlocks, uint32 alignment) : owner (owner) , receiver (receiver) , userContext (userContext) , blockSize (blockSize) , numBlocks (numBlocks) { receiver->queueOpened (userContext, blockSize, wantBlocksOnBackgroundThread); freeList.resize (numBlocks); sendList.resize (numBlocks); lockList.reserve (numBlocks); freeListOnRTThread.reserve (numBlocks); for (auto idx = 0u; idx < numBlocks; ++idx) freeList.push (Block (blockSize, alignment, idx)); } ~Queue () noexcept { if (receiver) receiver->queueClosed (userContext); } bool lock (DataExchangeBlock& block) { if (freeListOnRTThread.empty () == false) { auto& back = freeListOnRTThread.back (); block.data = back.data; block.size = blockSize; block.blockID = back.blockID; lockList.emplace_back (std::move (back)); freeListOnRTThread.pop_back (); return true; } Block b; if (freeList.pop (b)) { block.data = b.data; block.size = blockSize; block.blockID = b.blockID; lockList.emplace_back (std::move (b)); return true; } return false; } bool free (DataExchangeBlockID blockID) { if (blockID >= numBlocks) return false; auto it = std::find_if (lockList.begin (), lockList.end (), [&] (const auto& el) { return el.blockID == blockID; }); if (it == lockList.end ()) return false; Block b = std::move (*it); freeListOnRTThread.emplace_back (std::move (b)); lockList.erase (it); return true; } bool readyToSend (DataExchangeBlockID blockID) { if (blockID >= numBlocks) return false; auto it = std::find_if (lockList.begin (), lockList.end (), [&] (const auto& el) { return el.blockID == blockID; }); if (it == lockList.end ()) return false; Block b = std::move (*it); sendList.push (std::move (b)); lockList.erase (it); return true; } uint32 sendBlocks (DataExchangeQueueID queueID) { BlockVector blocks; Block b; while (sendList.pop (b)) { blocks.emplace_back (std::move (b)); } if (blocks.empty ()) return 0; std::vector debs; std::for_each (blocks.begin (), blocks.end (), [&] (const auto& el) { DataExchangeBlock block; block.data = el.data; block.size = blockSize; block.blockID = el.blockID; debs.push_back (block); }); receiver->onDataExchangeBlocksReceived (userContext, static_cast (debs.size ()), debs.data (), wantBlocksOnBackgroundThread); std::for_each (blocks.begin (), blocks.end (), [&] (auto&& el) { freeList.push (std::move (el)); }); return static_cast (debs.size ()); } IAudioProcessor* owner; IPtr receiver; DataExchangeUserContextID userContext {}; TBool wantBlocksOnBackgroundThread {false}; BlockRingBuffer freeList; BlockVector freeListOnRTThread; BlockVector lockList; BlockRingBuffer sendList; uint32 blockSize {0}; uint32 numBlocks {0}; }; using QueuePtr = std::unique_ptr; using QueueList = std::vector; Impl (IDataExchangeHandlerHost& host, uint32 maxQueues) : host (host) { queues.resize (maxQueues); } void setQueue (DataExchangeQueueID queueID, QueuePtr&& queue) { queuesLock.lock (); queues[queueID] = std::move (queue); if (queues[queueID]->wantBlocksOnBackgroundThread) ++numOpenBackgroundQueues; else ++numOpenMainThreadQueues; queuesLock.unlock (); host.onQueueOpened (queues[queueID]->owner, queueID, queues[queueID]->wantBlocksOnBackgroundThread); host.numberOfQueuesChanged (numOpenMainThreadQueues, numOpenBackgroundQueues); } tresult PLUGIN_API openQueue (IAudioProcessor* owner, uint32 blockSize, uint32 numBlocks, uint32 alignment, DataExchangeUserContextID userContext, DataExchangeQueueID* outID) override { if (!host.isMainThread ()) return kResultFalse; if (outID == nullptr) return kInvalidArgument; if (!host.isProcessorInactive (owner)) return kResultFalse; auto receiver = host.findDataExchangeReceiver (owner); if (!receiver) return kInvalidArgument; if (!host.allowAllocateSize (blockSize, numBlocks, alignment)) return kOutOfMemory; for (auto queueID = 0; queueID < queues.size (); ++queueID) { if (queues[queueID] == nullptr) { auto newQueue = std::make_unique (owner, receiver, userContext, blockSize, numBlocks, alignment); setQueue (queueID, std::move (newQueue)); *outID = queueID; return kResultTrue; } } auto queueSize = queues.size (); if (host.allowQueueListResize (static_cast (queueSize + 1))) { queues.resize (queueSize + 1); assert (queues.size () == queueSize + 1); DataExchangeQueueID queueID = static_cast (queueSize); auto newQueue = std::make_unique (owner, receiver, userContext, blockSize, numBlocks, alignment); setQueue (queueID, std::move (newQueue)); *outID = queueID; return kResultTrue; } return kOutOfMemory; } tresult PLUGIN_API closeQueue (DataExchangeQueueID queueID) override { if (!host.isMainThread ()) return kResultFalse; if (queues[queueID]) { if (!host.isProcessorInactive (queues[queueID]->owner)) return kResultFalse; QueuePtr q; queuesLock.lock (); std::swap (q, queues[queueID]); if (q->wantBlocksOnBackgroundThread) --numOpenBackgroundQueues; else --numOpenMainThreadQueues; queuesLock.unlock (); host.onQueueClosed (q->owner, queueID, q->wantBlocksOnBackgroundThread); host.numberOfQueuesChanged (numOpenMainThreadQueues, numOpenBackgroundQueues); q.reset (); return kResultTrue; } return kResultFalse; } tresult PLUGIN_API lockBlock (DataExchangeQueueID queueId, DataExchangeBlock* block) override { if (!block || queueId >= queues.size () || queues[queueId] == nullptr) return kInvalidArgument; if (queues[queueId]->lock (*block)) return kResultTrue; return kOutOfMemory; } tresult PLUGIN_API freeBlock (DataExchangeQueueID queueId, DataExchangeBlockID blockID, TBool sendToController) override { if (queueId >= queues.size () || queues[queueId] == nullptr) return kInvalidArgument; if (sendToController) { if (queues[queueId]->readyToSend (blockID)) { ++numReadyToSendBlocks; host.newBlockReadyToBeSend (queueId); return kResultTrue; } return kResultFalse; } return queues[queueId]->free (blockID) ? kResultTrue : kResultFalse; } bool sendBlocks (bool isMainThread, size_t queueID, uint32& numSendBlocks) { LockGuard guard (queuesLock); if (auto& queue = queues[queueID]) { if (queue->wantBlocksOnBackgroundThread != static_cast (isMainThread)) { numSendBlocks = queue->sendBlocks (static_cast (queueID)); } return true; } return false; } uint32 sendBlocks (bool isMainThread, DataExchangeQueueID queueFilter) { if (queueFilter != InvalidDataExchangeQueueID) { if (queueFilter < queues.size ()) { uint32 numSendBlocks; if (sendBlocks (isMainThread, queueFilter, numSendBlocks)) return numSendBlocks; } return 0; } uint32 totalSendBlocks = 0; uint32 openQueues = numOpenBackgroundQueues + numOpenMainThreadQueues; for (auto queueID = 0u; queueID < queues.size (); ++queueID) { uint32 numSendBlocks; if (sendBlocks (isMainThread, queueID, numSendBlocks)) { numReadyToSendBlocks -= numSendBlocks; totalSendBlocks += numSendBlocks; if ((--openQueues) == 0) break; } } return totalSendBlocks; } IDataExchangeHandlerHost& host; QueueList queues; std::atomic numReadyToSendBlocks {0}; std::atomic numOpenMainThreadQueues {0}; std::atomic numOpenBackgroundQueues {0}; using Mutex = std::recursive_mutex; using LockGuard = std::lock_guard; Mutex queuesLock; }; //------------------------------------------------------------------------ HostDataExchangeHandler::HostDataExchangeHandler (IDataExchangeHandlerHost& host, uint32 maxQueues) { impl = std::make_unique (host, maxQueues); } //------------------------------------------------------------------------ HostDataExchangeHandler::~HostDataExchangeHandler () noexcept = default; //------------------------------------------------------------------------ IDataExchangeHandler* HostDataExchangeHandler::getInterface () const { return impl.get (); } //------------------------------------------------------------------------ uint32 HostDataExchangeHandler::sendMainThreadBlocks () { return impl->sendBlocks (true, InvalidDataExchangeQueueID); } //------------------------------------------------------------------------ uint32 HostDataExchangeHandler::sendBackgroundBlocks (DataExchangeQueueID queueId) { return impl->sendBlocks (false, queueId); } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/plugprovider.h0000644000000000000000000000013215124701711024721 xustar0030 mtime=1767080905.281210866 30 atime=1767080905.281210866 30 ctime=1767080905.281210866 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/plugprovider.h0000644000175000001440000000675315124701711024724 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/plugprovider.h // Created by : Steinberg, 04/2005 // Description : VST 3 Plug-in Provider class // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/hosting/module.h" #include "pluginterfaces/vst/ivsttestplugprovider.h" #include "pluginterfaces/base/funknownimpl.h" #include namespace Steinberg { namespace Vst { class IComponent; class IEditController; class ConnectionProxy; //------------------------------------------------------------------------ /** Helper for creating and initializing component. \ingroup Validator */ //------------------------------------------------------------------------ class PlugProvider : public U::Implements, U::Indirectly> { public: using ClassInfo = VST3::Hosting::ClassInfo; using PluginFactory = VST3::Hosting::PluginFactory; //--- --------------------------------------------------------------------- PlugProvider (const PluginFactory& factory, ClassInfo info, bool plugIsGlobal = true); ~PlugProvider () override; bool initialize (); IPtr getComponentPtr () const { return component; } IPtr getControllerPtr () const { return controller; } const ClassInfo& getClassInfo () const { return classInfo; } //--- from ITestPlugProvider ------------------ IComponent* PLUGIN_API getComponent () SMTG_OVERRIDE; IEditController* PLUGIN_API getController () SMTG_OVERRIDE; tresult PLUGIN_API releasePlugIn (IComponent* component, IEditController* controller) SMTG_OVERRIDE; tresult PLUGIN_API getSubCategories (IStringResult& result) const SMTG_OVERRIDE { result.setText (classInfo.subCategoriesString ().data ()); return kResultTrue; } tresult PLUGIN_API getComponentUID (FUID& uid) const SMTG_OVERRIDE; //--- from ITestPlugProvider2 ------------------ IPluginFactory* PLUGIN_API getPluginFactory () SMTG_OVERRIDE; static void setErrorStream (std::ostream* stream); //------------------------------------------------------------------------ protected: bool setupPlugin (FUnknown* hostContext); bool connectComponents (); bool disconnectComponents (); void terminatePlugin (); template void printError (Proc p) const; PluginFactory factory; IPtr component; IPtr controller; ClassInfo classInfo; IPtr componentCP; IPtr controllerCP; bool plugIsGlobal; }; //------------------------------------------------------------------------ class PluginContextFactory { public: static PluginContextFactory& instance () { static PluginContextFactory factory; return factory; } void setPluginContext (FUnknown* obj) { context = obj; } FUnknown* getPluginContext () const { return context; } private: FUnknown* context; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/connectionproxy.cpp0000644000000000000000000000012715124701711025777 xustar0029 mtime=1767080905.28072064 29 atime=1767080905.28072064 29 ctime=1767080905.28072064 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/connectionproxy.cpp0000644000175000001440000000526115124701711025767 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/connectionproxy.cpp // Created by : Steinberg, 04/2019 // Description : VST 3 Plug-in connection class // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "connectionproxy.h" namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ IMPLEMENT_FUNKNOWN_METHODS (ConnectionProxy, IConnectionPoint, IConnectionPoint::iid) //------------------------------------------------------------------------ ConnectionProxy::ConnectionProxy (IConnectionPoint* srcConnection) : srcConnection (srcConnection) // share it { FUNKNOWN_CTOR } //------------------------------------------------------------------------ ConnectionProxy::~ConnectionProxy () { FUNKNOWN_DTOR } //------------------------------------------------------------------------ tresult PLUGIN_API ConnectionProxy::connect (IConnectionPoint* other) { if (other == nullptr) return kInvalidArgument; if (dstConnection) return kResultFalse; dstConnection = other; // share it tresult res = srcConnection->connect (this); if (res != kResultTrue) dstConnection = nullptr; return res; } //------------------------------------------------------------------------ tresult PLUGIN_API ConnectionProxy::disconnect (IConnectionPoint* other) { if (!other) return kInvalidArgument; if (other == dstConnection) { if (srcConnection) srcConnection->disconnect (this); dstConnection = nullptr; return kResultTrue; } return kInvalidArgument; } //------------------------------------------------------------------------ tresult PLUGIN_API ConnectionProxy::notify (IMessage* message) { if (dstConnection) { // We discard the message if we are not in the UI main thread if (threadChecker && threadChecker->test ()) return dstConnection->notify (message); } return kResultFalse; } //------------------------------------------------------------------------ bool ConnectionProxy::disconnect () { return disconnect (dstConnection) == kResultTrue; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/PaxHeaders/hostclasses.cpp0000644000000000000000000000013215124701711025065 xustar0030 mtime=1767080905.280856296 30 atime=1767080905.280856296 30 ctime=1767080905.280856296 qtractor-1.5.11/src/vst3/public.sdk/source/vst/hosting/hostclasses.cpp0000644000175000001440000002304015124701711025054 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/hosting/hostclasses.cpp // Created by : Steinberg, 03/05/2008. // Description : VST 3 hostclasses, example impl. for IHostApplication, IAttributeList and IMessage // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "hostclasses.h" #include "public.sdk/source/vst/utility/stringconvert.h" #include namespace Steinberg { namespace Vst { //----------------------------------------------------------------------------- HostApplication::HostApplication () { FUNKNOWN_CTOR mPlugInterfaceSupport = owned (new PlugInterfaceSupport); } //----------------------------------------------------------------------------- tresult PLUGIN_API HostApplication::getName (String128 name) { return StringConvert::convert ("My VST3 HostApplication", name) ? kResultTrue : kInternalError; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostApplication::createInstance (TUID cid, TUID _iid, void** obj) { if (FUnknownPrivate::iidEqual (cid, IMessage::iid) && FUnknownPrivate::iidEqual (_iid, IMessage::iid)) { *obj = new HostMessage; return kResultTrue; } if (FUnknownPrivate::iidEqual (cid, IAttributeList::iid) && FUnknownPrivate::iidEqual (_iid, IAttributeList::iid)) { if (auto al = HostAttributeList::make ()) { *obj = al.take (); return kResultTrue; } return kOutOfMemory; } *obj = nullptr; return kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostApplication::queryInterface (const char* _iid, void** obj) { QUERY_INTERFACE (_iid, obj, FUnknown::iid, IHostApplication) QUERY_INTERFACE (_iid, obj, IHostApplication::iid, IHostApplication) if (mPlugInterfaceSupport && mPlugInterfaceSupport->queryInterface (_iid, obj) == kResultTrue) return kResultOk; *obj = nullptr; return kResultFalse; } //----------------------------------------------------------------------------- uint32 PLUGIN_API HostApplication::addRef () { return 1; } //----------------------------------------------------------------------------- uint32 PLUGIN_API HostApplication::release () { return 1; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- IMPLEMENT_FUNKNOWN_METHODS (HostMessage, IMessage, IMessage::iid) //----------------------------------------------------------------------------- HostMessage::HostMessage () {FUNKNOWN_CTOR} //----------------------------------------------------------------------------- HostMessage::~HostMessage () noexcept { setMessageID (nullptr); FUNKNOWN_DTOR } //----------------------------------------------------------------------------- const char* PLUGIN_API HostMessage::getMessageID () { return messageId; } //----------------------------------------------------------------------------- void PLUGIN_API HostMessage::setMessageID (const char* mid) { if (messageId) delete[] messageId; messageId = nullptr; if (mid) { size_t len = strlen (mid) + 1; messageId = new char[len]; strcpy (messageId, mid); } } //----------------------------------------------------------------------------- IAttributeList* PLUGIN_API HostMessage::getAttributes () { if (!attributeList) attributeList = HostAttributeList::make (); return attributeList; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- struct HostAttributeList::Attribute { enum class Type { kUninitialized, kInteger, kFloat, kString, kBinary }; Attribute () = default; Attribute (int64 value) : type (Type::kInteger) { v.intValue = value; } Attribute (double value) : type (Type::kFloat) { v.floatValue = value; } /* size is in code unit (count of TChar) */ Attribute (const TChar* value, uint32 sizeInCodeUnit) : size (sizeInCodeUnit), type (Type::kString) { v.stringValue = new TChar[sizeInCodeUnit]; memcpy (v.stringValue, value, sizeInCodeUnit * sizeof (TChar)); } Attribute (const void* value, uint32 sizeInBytes) : size (sizeInBytes), type (Type::kBinary) { v.binaryValue = new char[sizeInBytes]; memcpy (v.binaryValue, value, sizeInBytes); } Attribute (Attribute&& o) SMTG_NOEXCEPT { *this = std::move (o); } Attribute& operator= (Attribute&& o) SMTG_NOEXCEPT { v = o.v; size = o.size; type = o.type; o.size = 0; o.type = Type::kUninitialized; o.v = {}; return *this; } ~Attribute () noexcept { if (size) delete[] v.binaryValue; } int64 intValue () const { return v.intValue; } double floatValue () const { return v.floatValue; } /* sizeInCodeUnit is in code unit (count of TChar) */ const TChar* stringValue (uint32& sizeInCodeUnit) { sizeInCodeUnit = size; return v.stringValue; } const void* binaryValue (uint32& sizeInBytes) { sizeInBytes = size; return v.binaryValue; } Type getType () const { return type; } private: union v { int64 intValue; double floatValue; TChar* stringValue; char* binaryValue; } v {}; uint32 size {0}; Type type {Type::kUninitialized}; }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- IMPLEMENT_FUNKNOWN_METHODS (HostAttributeList, IAttributeList, IAttributeList::iid) //----------------------------------------------------------------------------- IPtr HostAttributeList::make () { return owned (new HostAttributeList); } //----------------------------------------------------------------------------- HostAttributeList::HostAttributeList () {FUNKNOWN_CTOR} //----------------------------------------------------------------------------- HostAttributeList::~HostAttributeList () noexcept {FUNKNOWN_DTOR} //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::setInt (AttrID aid, int64 value) { if (!aid) return kInvalidArgument; list[aid] = Attribute (value); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::getInt (AttrID aid, int64& value) { if (!aid) return kInvalidArgument; auto it = list.find (aid); if (it != list.end () && it->second.getType () == Attribute::Type::kInteger) { value = it->second.intValue (); return kResultTrue; } return kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::setFloat (AttrID aid, double value) { if (!aid) return kInvalidArgument; list[aid] = Attribute (value); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::getFloat (AttrID aid, double& value) { if (!aid) return kInvalidArgument; auto it = list.find (aid); if (it != list.end () && it->second.getType () == Attribute::Type::kFloat) { value = it->second.floatValue (); return kResultTrue; } return kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::setString (AttrID aid, const TChar* string) { if (!aid) return kInvalidArgument; // + 1 for the null-terminate auto length = tstrlen (string) + 1; list[aid] = Attribute (string, length); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::getString (AttrID aid, TChar* string, uint32 sizeInBytes) { if (!aid) return kInvalidArgument; auto it = list.find (aid); if (it != list.end () && it->second.getType () == Attribute::Type::kString) { uint32 sizeInCodeUnit = 0; const TChar* _string = it->second.stringValue (sizeInCodeUnit); memcpy (string, _string, std::min (sizeInCodeUnit * sizeof (TChar), sizeInBytes)); return kResultTrue; } return kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::setBinary (AttrID aid, const void* data, uint32 sizeInBytes) { if (!aid) return kInvalidArgument; list[aid] = Attribute (data, sizeInBytes); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API HostAttributeList::getBinary (AttrID aid, const void*& data, uint32& sizeInBytes) { if (!aid) return kInvalidArgument; auto it = list.find (aid); if (it != list.end () && it->second.getType () == Attribute::Type::kBinary) { data = it->second.binaryValue (sizeInBytes); return kResultTrue; } sizeInBytes = 0; return kResultFalse; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstparameters.cpp0000644000000000000000000000012715124701711023763 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstparameters.cpp0000644000175000001440000003204715124701711023755 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstparameters.cpp // Created by : Steinberg, 03/2008 // Description : VST Parameter Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstparameters.h" #include "pluginterfaces/base/futils.h" #include "pluginterfaces/base/ustring.h" #include namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // Parameter Implementation //------------------------------------------------------------------------ Parameter::Parameter () { } //------------------------------------------------------------------------ Parameter::Parameter (const ParameterInfo& info) : info (info), valueNormalized (info.defaultNormalizedValue) { } //------------------------------------------------------------------------ Parameter::Parameter (const TChar* title, ParamID tag, const TChar* units, ParamValue defaultValueNormalized, int32 stepCount, int32 flags, UnitID unitID, const TChar* shortTitle) { UString (info.title, str16BufferSize (String128)).assign (title); if (units) UString (info.units, str16BufferSize (String128)).assign (units); if (shortTitle) UString (info.shortTitle, str16BufferSize (String128)).assign (shortTitle); info.stepCount = stepCount; info.defaultNormalizedValue = valueNormalized = defaultValueNormalized; info.flags = flags; info.id = tag; info.unitId = unitID; } //------------------------------------------------------------------------ Parameter::~Parameter () { } //------------------------------------------------------------------------ bool Parameter::setNormalized (ParamValue normValue) { if (normValue > 1.0) { normValue = 1.0; } else if (normValue < 0.) { normValue = 0.; } if (normValue != valueNormalized) { valueNormalized = normValue; changed (); return true; } return false; } //------------------------------------------------------------------------ void Parameter::toString (ParamValue normValue, String128 string) const { UString wrapper (string, str16BufferSize (String128)); if (info.stepCount == 1) { if (normValue > 0.5) { wrapper.assign (STR16 ("On")); } else { wrapper.assign (STR16 ("Off")); } } else { if (!wrapper.printFloat (normValue, precision)) string[0] = 0; } } //------------------------------------------------------------------------ bool Parameter::fromString (const TChar* string, ParamValue& normValue) const { UString wrapper (const_cast (string), tstrlen (string)); return wrapper.scanFloat (normValue); } //------------------------------------------------------------------------ ParamValue Parameter::toPlain (ParamValue normValue) const { return normValue; } //------------------------------------------------------------------------ ParamValue Parameter::toNormalized (ParamValue plainValue) const { return plainValue; } //------------------------------------------------------------------------ // RangeParameter Implementation //------------------------------------------------------------------------ RangeParameter::RangeParameter () : minPlain (0), maxPlain (1) { } //------------------------------------------------------------------------ RangeParameter::RangeParameter (const ParameterInfo& paramInfo, ParamValue _minPlain, ParamValue _maxPlain) : Parameter (paramInfo), minPlain (_minPlain), maxPlain (_maxPlain) { } //------------------------------------------------------------------------ RangeParameter::RangeParameter (const TChar* title, ParamID tag, const TChar* units, ParamValue minPlain, ParamValue maxPlain, ParamValue defaultValuePlain, int32 stepCount, int32 flags, UnitID unitID, const TChar* shortTitle) : minPlain (minPlain), maxPlain (maxPlain) { UString (info.title, str16BufferSize (String128)).assign (title); if (units) UString (info.units, str16BufferSize (String128)).assign (units); if (shortTitle) UString (info.shortTitle, str16BufferSize (String128)).assign (shortTitle); info.stepCount = stepCount; info.defaultNormalizedValue = valueNormalized = toNormalized (defaultValuePlain); info.flags = flags; info.id = tag; info.unitId = unitID; } //------------------------------------------------------------------------ void RangeParameter::toString (ParamValue _valueNormalized, String128 string) const { if (info.stepCount > 1) { UString wrapper (string, str16BufferSize (String128)); int64 plain = static_cast (toPlain (_valueNormalized)); if (!wrapper.printInt (plain)) string[0] = 0; } else { Parameter::toString (toPlain (_valueNormalized), string); } } //------------------------------------------------------------------------ bool RangeParameter::fromString (const TChar* string, ParamValue& _valueNormalized) const { UString wrapper (const_cast (string), tstrlen (string)); if (info.stepCount > 1) { int64 plainValue; if (wrapper.scanInt (plainValue)) { _valueNormalized = toNormalized ((ParamValue)plainValue); return true; } return false; } if (wrapper.scanFloat (_valueNormalized)) { if (_valueNormalized < getMin ()) _valueNormalized = getMin (); else if (_valueNormalized > getMax ()) _valueNormalized = getMax (); _valueNormalized = toNormalized (_valueNormalized); return true; } return false; } //------------------------------------------------------------------------ ParamValue RangeParameter::toPlain (ParamValue _valueNormalized) const { if (info.stepCount > 1) return FromNormalized (_valueNormalized, info.stepCount) + getMin (); return _valueNormalized * (getMax () - getMin ()) + getMin (); } //------------------------------------------------------------------------ ParamValue RangeParameter::toNormalized (ParamValue plainValue) const { if (info.stepCount > 1) return ToNormalized (plainValue - getMin (), info.stepCount); SMTG_ASSERT (getMax () - getMin () != 0); return (plainValue - getMin ()) / (getMax () - getMin ()); } //------------------------------------------------------------------------ // StringListParameter Implementation //------------------------------------------------------------------------ StringListParameter::StringListParameter (const ParameterInfo& paramInfo) : Parameter (paramInfo) { } //------------------------------------------------------------------------ StringListParameter::StringListParameter (const TChar* title, ParamID tag, const TChar* units, int32 flags, UnitID unitID, const TChar* shortTitle) { UString (info.title, str16BufferSize (String128)).assign (title); if (units) UString (info.units, str16BufferSize (String128)).assign (units); if (shortTitle) UString (info.shortTitle, str16BufferSize (String128)).assign (shortTitle); info.stepCount = -1; info.defaultNormalizedValue = 0; info.flags = flags; info.id = tag; info.unitId = unitID; } //------------------------------------------------------------------------ StringListParameter::~StringListParameter () { for (auto& string : strings) std::free (string); } //------------------------------------------------------------------------ void StringListParameter::appendString (const String128 string) { int32 length = strlen16 (string); TChar* buffer = (TChar*)std::malloc ((length + 1) * sizeof (TChar)); if (!buffer) return; memcpy (buffer, string, length * sizeof (TChar)); buffer[length] = 0; strings.push_back (buffer); info.stepCount++; } //------------------------------------------------------------------------ bool StringListParameter::replaceString (int32 index, const String128 string) { TChar* str = strings.at (index); if (!str) return false; int32 length = strlen16 (string); TChar* buffer = (TChar*)malloc ((length + 1) * sizeof (TChar)); if (!buffer) return false; memcpy (buffer, string, length * sizeof (TChar)); buffer[length] = 0; strings[index] = buffer; std::free (str); return true; } //------------------------------------------------------------------------ void StringListParameter::clear () { for (auto& string : strings) std::free (string); strings.clear (); info.stepCount = -1; } //------------------------------------------------------------------------ void StringListParameter::toString (ParamValue _valueNormalized, String128 string) const { int32 index = static_cast (toPlain (_valueNormalized)); if (const TChar* valueString = strings.at (index)) { UString (string, str16BufferSize (String128)).assign (valueString); } else string[0] = 0; } //------------------------------------------------------------------------ bool StringListParameter::fromString (const TChar* string, ParamValue& _valueNormalized) const { int32 index = 0; for (auto it = strings.begin (), end = strings.end (); it != end; ++it, ++index) { if (strcmp16 (*it, string) == 0) { _valueNormalized = toNormalized ((ParamValue)index); return true; } } return false; } //------------------------------------------------------------------------ ParamValue StringListParameter::toPlain (ParamValue _valueNormalized) const { if (info.stepCount <= 0) return 0; return FromNormalized (_valueNormalized, info.stepCount); } //------------------------------------------------------------------------ ParamValue StringListParameter::toNormalized (ParamValue plainValue) const { if (info.stepCount <= 0) return 0; return ToNormalized (plainValue, info.stepCount); } //------------------------------------------------------------------------ // ParameterContainer Implementation //------------------------------------------------------------------------ ParameterContainer::ParameterContainer () { } //------------------------------------------------------------------------ ParameterContainer::~ParameterContainer () { if (params) delete params; } //------------------------------------------------------------------------ void ParameterContainer::init (int32 initialSize, int32 /*resizeDelta*/) { if (!params) { params = new ParameterPtrVector; if (initialSize > 0) params->reserve (initialSize); } } //------------------------------------------------------------------------ Parameter* ParameterContainer::addParameter (Parameter* p) { if (!params) init (); id2index[p->getInfo ().id] = params->size (); params->push_back (IPtr (p, false)); return p; } //------------------------------------------------------------------------ Parameter* ParameterContainer::addParameter (const ParameterInfo& info) { if (!params) init (); auto* p = new Parameter (info); if (addParameter (p)) return p; p->release (); return nullptr; } //------------------------------------------------------------------------ Parameter* ParameterContainer::getParameterByIndex (int32 index) const { if (!params || index < 0 || index >= static_cast (params->size ())) return nullptr; return params->at (index); } //------------------------------------------------------------------------ Parameter* ParameterContainer::getParameter (ParamID tag) const { if (params) { auto it = id2index.find (tag); if (it != id2index.end ()) return params->at (it->second); } return nullptr; } //------------------------------------------------------------------------ bool ParameterContainer::removeParameter (ParamID tag) { if (!params) return false; IndexMap::const_iterator it = id2index.find (tag); if (it != id2index.end ()) { params->erase (params->begin () + it->second); id2index.erase (it); } return false; } //------------------------------------------------------------------------ Parameter* ParameterContainer::addParameter (const TChar* title, const TChar* units, int32 stepCount, ParamValue defaultNormalizedValue, int32 flags, int32 tag, UnitID unitID, const TChar* shortTitle) { if (!title) { return nullptr; } ParameterInfo info = {}; UString (info.title, str16BufferSize (String128)).assign (title); if (units) UString (info.units, str16BufferSize (String128)).assign (units); if (shortTitle) UString (info.shortTitle, str16BufferSize (String128)).assign (shortTitle); info.stepCount = stepCount; info.defaultNormalizedValue = defaultNormalizedValue; info.flags = flags; info.id = (tag >= 0) ? tag : getParameterCount (); info.unitId = unitID; return addParameter (info); } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/testsuite0000644000000000000000000000013215124701711022327 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.282785169 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/0000755000175000001440000000000015124701711022374 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/bus0000644000000000000000000000013215124701711023120 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.282785169 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/0000755000175000001440000000000015124701711023165 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/businvalidindex.h0000644000000000000000000000013215124701711026536 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/businvalidindex.h0000644000175000001440000000267015124701711026533 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/businvalidindex.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Bus Invalid Index. * \ingroup TestClass */ class BusInvalidIndexTest : public TestBase { public: BusInvalidIndexTest (ITestPlugProvider* plugProvider); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Bus Invalid Index") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/scanbusses.cpp0000644000000000000000000000013215124701711026052 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/scanbusses.cpp0000644000175000001440000000605615124701711026051 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/scanbusses.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/bus/scanbusses.h" #include "public.sdk/source/vst/utility/stringconvert.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ScanBussesTest //------------------------------------------------------------------------ ScanBussesTest::ScanBussesTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API ScanBussesTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); int32 numBusses = 0; for (MediaType mediaType = kAudio; mediaType < kNumMediaTypes; mediaType++) { int32 numInputs = vstPlug->getBusCount (mediaType, kInput); int32 numOutputs = vstPlug->getBusCount (mediaType, kOutput); numBusses += (numInputs + numOutputs); if ((mediaType == (kNumMediaTypes - 1)) && (numBusses == 0)) { addErrorMessage (testResult, STR ("This component does not export any buses!!!")); return false; } addMessage (testResult, printf ("=> %s Buses: [%d In(s) => %d Out(s)]", mediaType == kAudio ? "Audio" : "Event", numInputs, numOutputs)); for (int32 i = 0; i < numInputs + numOutputs; ++i) { BusDirection busDirection = i < numInputs ? kInput : kOutput; int32 busIndex = busDirection == kInput ? i : i - numInputs; BusInfo busInfo = {}; if (vstPlug->getBusInfo (mediaType, busDirection, busIndex, busInfo) == kResultTrue) { auto busName = StringConvert::convert (busInfo.name); if (busName.empty ()) { addErrorMessage (testResult, printf ("Bus %d has no name!!!", busIndex)); return false; } addMessage ( testResult, printf (" %s[%d]: \"%s\" (%s-%s) ", busDirection == kInput ? "In " : "Out", busIndex, busName.data (), busInfo.busType == kMain ? "Main" : "Aux", busInfo.kDefaultActive ? "Default Active" : "Default Inactive")); } else return false; } } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/checkaudiobusarrangement.h0000644000000000000000000000013215124701711030403 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/checkaudiobusarrangement.h0000644000175000001440000000274715124701711030405 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/checkaudiobusarrangement.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Check Audio Bus Arrangement. * \ingroup TestClass */ class CheckAudioBusArrangementTest : public TestBase { public: CheckAudioBusArrangementTest (ITestPlugProvider* plugProvider); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Check Audio Bus Arrangement") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/checkaudiobusarrangement.cpp0000644000000000000000000000013215124701711030736 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/checkaudiobusarrangement.cpp0000644000175000001440000000567715124701711030745 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/checkaudiobusarrangement.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/bus/checkaudiobusarrangement.h" #include "pluginterfaces/base/funknownimpl.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // CheckAudioBusArrangementTest //------------------------------------------------------------------------ CheckAudioBusArrangementTest::CheckAudioBusArrangementTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool CheckAudioBusArrangementTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); int32 numInputs = vstPlug->getBusCount (kAudio, kInput); int32 numOutputs = vstPlug->getBusCount (kAudio, kOutput); int32 arrangementMismatchs = 0; if (auto audioEffect = U::cast (vstPlug)) { for (int32 i = 0; i < numInputs + numOutputs; ++i) { BusDirection dir = i < numInputs ? kInput : kOutput; int32 busIndex = dir == kInput ? i : i - numInputs; addMessage (testResult, printf (" Check %s Audio Bus Arrangement (%d)", dir == kInput ? "Input" : "Output", busIndex)); BusInfo busInfo = {}; if (vstPlug->getBusInfo (kAudio, dir, busIndex, busInfo) == kResultTrue) { SpeakerArrangement arrangement; if (audioEffect->getBusArrangement (dir, busIndex, arrangement) == kResultTrue) { if (busInfo.channelCount != SpeakerArr::getChannelCount (arrangement)) { arrangementMismatchs++; addErrorMessage (testResult, STR ("channelCount is inconsistent!")); } } else { addErrorMessage (testResult, STR ("IAudioProcessor::getBusArrangement (..) failed!")); return false; } } else { addErrorMessage (testResult, STR ("IComponent::getBusInfo (..) failed!")); return false; } } } return (arrangementMismatchs == 0); } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/sidechainarrangement.h0000644000000000000000000000013215124701711027521 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/sidechainarrangement.h0000644000175000001440000000271715124701711027520 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/sidechainarrangement.h // Created by : Steinberg, 11/2020 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test SideChain Arrangement. * \ingroup TestClass */ class SideChainArrangementTest : public TestBase { public: SideChainArrangementTest (ITestPlugProvider* plugProvider); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("SideChain Arrangement") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/busactivation.cpp0000644000000000000000000000013215124701711026554 xustar0030 mtime=1767080905.283346151 30 atime=1767080905.283346151 30 ctime=1767080905.283346151 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/busactivation.cpp0000644000175000001440000000642115124701711026547 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/busactivation.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/bus/busactivation.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // BusActivationTest //------------------------------------------------------------------------ BusActivationTest::BusActivationTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API BusActivationTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); int32 numTotalBusses = 0; int32 numFailedActivations = 0; for (MediaType type = kAudio; type < kNumMediaTypes; type++) { int32 numInputs = vstPlug->getBusCount (type, kInput); int32 numOutputs = vstPlug->getBusCount (type, kOutput); numTotalBusses += (numInputs + numOutputs); for (int32 i = 0; i < numInputs + numOutputs; ++i) { BusDirection busDirection = i < numInputs ? kInput : kOutput; int32 busIndex = busDirection == kInput ? i : i - numInputs; BusInfo busInfo = {}; if (vstPlug->getBusInfo (type, busDirection, busIndex, busInfo) != kResultTrue) { addErrorMessage (testResult, STR ("IComponent::getBusInfo (..) failed.")); return false; } addMessage (testResult, printf (" Bus Activation: %s %s Bus (%d) (%s)", busDirection == kInput ? "Input" : "Output", type == kAudio ? "Audio" : "Event", busIndex, busInfo.busType == kMain ? "kMain" : "kAux")); if ((busInfo.flags & BusInfo::kDefaultActive) == false) { if (vstPlug->activateBus (type, busDirection, busIndex, true) != kResultOk) numFailedActivations++; if (vstPlug->activateBus (type, busDirection, busIndex, false) != kResultOk) numFailedActivations++; } else if ((busInfo.flags & BusInfo::kDefaultActive) == true) { if (vstPlug->activateBus (type, busDirection, busIndex, false) != kResultOk) numFailedActivations++; if (vstPlug->activateBus (type, busDirection, busIndex, true) != kResultOk) numFailedActivations++; } } } if (numFailedActivations > 0) addErrorMessage (testResult, STR ("Bus activation failed.")); return (numFailedActivations == 0); } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/businvalidindex.cpp0000644000000000000000000000013215124701711027071 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/businvalidindex.cpp0000644000175000001440000000517415124701711027070 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/businvalidindex.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/bus/businvalidindex.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // BusInvalidIndexTest //------------------------------------------------------------------------ BusInvalidIndexTest::BusInvalidIndexTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API BusInvalidIndexTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); bool failed = false; int32 numInvalidDesc = 0; for (MediaType mediaType = kAudio; mediaType < kNumMediaTypes; mediaType++) { int32 numBusses = vstPlug->getBusCount (mediaType, kInput) + vstPlug->getBusCount (mediaType, kOutput); for (BusDirection dir = kInput; dir <= kOutput; dir++) { BusInfo descBefore = {}; BusInfo descAfter = {}; int32 randIndex = 0; // todo: rand with negative numbers for (int32 i = 0; i <= numBusses * TestDefaults::instance ().numIterations; ++i) { randIndex = rand (); if (0 > randIndex || randIndex > numBusses) { /*tresult result =*/vstPlug->getBusInfo (mediaType, dir, randIndex, descAfter); if (memcmp ((void*)&descBefore, (void*)&descAfter, sizeof (BusInfo)) != 0) { failed |= true; numInvalidDesc++; } } } } } if (numInvalidDesc > 0) { addErrorMessage (testResult, printf ("The component returned %i buses queried with an invalid index!", numInvalidDesc)); } return failed == false; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/sidechainarrangement.cpp0000644000000000000000000000013215124701711030054 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/sidechainarrangement.cpp0000644000175000001440000001054315124701711030047 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/sidechainarrangement.cpp // Created by : Steinberg, 11/2019 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/bus/sidechainarrangement.h" #include "pluginterfaces/base/funknownimpl.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // SideChainArrangementTest //------------------------------------------------------------------------ SideChainArrangementTest::SideChainArrangementTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API SideChainArrangementTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); bool failed = false; auto audioEffect = U::cast (vstPlug); if (!audioEffect) return failed; // get the side chain arrangements // set Main/first Input and output to Mono // get the current arrangement and compare // check if Audio sideChain is supported bool hasInputSideChain = false; int32 numInBusses = vstPlug->getBusCount (kAudio, kInput); if (numInBusses < 2) return true; for (int32 busIndex = 0; busIndex < numInBusses; busIndex++) { BusInfo info; if (vstPlug->getBusInfo (kAudio, kInput, busIndex, info) != kResultTrue) { addErrorMessage (testResult, STR ("IComponent::getBusInfo (..) failed.")); continue; } if (info.busType == kAux) hasInputSideChain = true; } if (!hasInputSideChain) return true; auto* inputArrArray = new SpeakerArrangement[numInBusses]; for (int32 busIndex = 0; busIndex < numInBusses; busIndex++) { if (audioEffect->getBusArrangement (kInput, busIndex, inputArrArray[busIndex]) != kResultTrue) { addErrorMessage (testResult, STR ("IComponent::getBusArrangement (..) failed.")); } } int32 numOutBusses = vstPlug->getBusCount (kAudio, kOutput); SpeakerArrangement* outputArrArray = nullptr; if (numOutBusses > 0) { outputArrArray = new SpeakerArrangement[numOutBusses]; for (int32 busIndex = 0; busIndex < numOutBusses; busIndex++) { if (audioEffect->getBusArrangement (kOutput, busIndex, outputArrArray[busIndex]) != kResultTrue) { addErrorMessage (testResult, STR ("IComponent::getBusArrangement (..) failed.")); } } outputArrArray[0] = kSpeakerM; } inputArrArray[0] = kSpeakerM; if (audioEffect->setBusArrangements (inputArrArray, numInBusses, outputArrArray, numOutBusses) == kResultTrue) { for (int32 busIndex = 0; busIndex < numInBusses; busIndex++) { SpeakerArrangement tmp; if (audioEffect->getBusArrangement (kInput, busIndex, tmp) == kResultTrue) { if (tmp != inputArrArray[busIndex]) { addErrorMessage ( testResult, printf ( "Input %d: setBusArrangements was returning kResultTrue but getBusArrangement returns different arrangement!", busIndex)); failed = true; } } } for (int32 busIndex = 0; busIndex < numOutBusses; busIndex++) { SpeakerArrangement tmp; if (audioEffect->getBusArrangement (kOutput, busIndex, tmp) != kResultTrue) { if (tmp != outputArrArray[busIndex]) { addErrorMessage ( testResult, printf ( "Output %d: setBusArrangements was returning kResultTrue but getBusArrangement returns different arrangement!", busIndex)); failed = true; } } } } return failed == false; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/scanbusses.h0000644000000000000000000000013215124701711025517 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/scanbusses.h0000644000175000001440000000274615124701711025520 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/scanbusses.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Scan Buses. * \ingroup TestClass */ class ScanBussesTest : public TestBase { public: ScanBussesTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("Scan Buses") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/busconsistency.h0000644000000000000000000000013215124701711026421 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/busconsistency.h0000644000175000001440000000266115124701711026416 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/busconsistency.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Bus Consistency. * \ingroup TestClass */ class BusConsistencyTest : public TestBase { public: BusConsistencyTest (ITestPlugProvider* plugProvider); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Bus Consistency") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/busconsistency.cpp0000644000000000000000000000013215124701711026754 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/busconsistency.cpp0000644000175000001440000000620015124701711026742 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/busconsistency.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/bus/busconsistency.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // BusConsistencyTest //------------------------------------------------------------------------ BusConsistencyTest::BusConsistencyTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API BusConsistencyTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); bool failed = false; int32 numFalseDescQueries = 0; for (MediaType mediaType = kAudio; mediaType < kNumMediaTypes; mediaType++) { for (BusDirection dir = kInput; dir <= kOutput; dir++) { int32 numBusses = vstPlug->getBusCount (mediaType, dir); if (numBusses > 0) { auto* busArray = new BusInfo[numBusses]; if (busArray) { // get all bus descriptions and save them in an array int32 busIndex; for (busIndex = 0; busIndex < numBusses; busIndex++) { memset (&busArray[busIndex], 0, sizeof (BusInfo)); vstPlug->getBusInfo (mediaType, dir, busIndex, busArray[busIndex]); } // test by getting descriptions randomly and comparing with saved ones int32 randIndex = 0; BusInfo info = {}; for (busIndex = 0; busIndex <= numBusses * TestDefaults::instance ().numIterations; busIndex++) { randIndex = rand () % (numBusses); memset (&info, 0, sizeof (BusInfo)); /*tresult result =*/vstPlug->getBusInfo (mediaType, dir, randIndex, info); if (memcmp ((void*)&busArray[randIndex], (void*)&info, sizeof (BusInfo)) != TestDefaults::instance ().buffersAreEqual) { failed |= true; numFalseDescQueries++; } } delete[] busArray; } } } } if (numFalseDescQueries > 0) { addErrorMessage ( testResult, printf ( "The component returned %i inconsistent buses! (getBusInfo () returns sometime different info for the same bus!", numFalseDescQueries)); } return failed == false; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/PaxHeaders/busactivation.h0000644000000000000000000000013215124701711026221 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283346151 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/bus/busactivation.h0000644000175000001440000000265415124701711026220 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/bus/busactivation.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Bus Activation. * \ingroup TestClass */ class BusActivationTest : public TestBase { public: BusActivationTest (ITestPlugProvider* plugProvider); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Bus Activation") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/vststructsizecheck.h0000644000000000000000000000013215124701711026527 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.285285125 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/vststructsizecheck.h0000644000175000001440000001732415124701711026526 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/vststructsizecheck.h // Created by : Steinberg, 09/2010 // Description : struct size test. Checks that struct sizes and alignments do not change after publicly released // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginterfaces/vst/ivstattributes.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivstcomponent.h" #include "pluginterfaces/vst/ivstcontextmenu.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstevents.h" #include "pluginterfaces/vst/ivsthostapplication.h" #include "pluginterfaces/vst/ivstmessage.h" #include "pluginterfaces/vst/ivstmidicontrollers.h" #include "pluginterfaces/vst/ivstparameterchanges.h" #include "pluginterfaces/vst/ivstplugview.h" #include "pluginterfaces/vst/ivstprocesscontext.h" #include "pluginterfaces/vst/ivstrepresentation.h" #include "pluginterfaces/vst/ivstunits.h" #include "pluginterfaces/vst/vstpresetkeys.h" #include "pluginterfaces/vst/vsttypes.h" #include "pluginterfaces/base/typesizecheck.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { #define SMTG_VST_COMPILE_TIME_STRUCT_CHECK 1 #if SMTG_VST_COMPILE_TIME_STRUCT_CHECK // ipluginbase.h SMTG_TYPE_SIZE_CHECK (PFactoryInfo, 452, 452, 452, 452) SMTG_TYPE_SIZE_CHECK (PClassInfo, 116, 116, 116, 116) SMTG_TYPE_SIZE_CHECK (PClassInfo2, 440, 440, 440, 440) SMTG_TYPE_SIZE_CHECK (PClassInfoW, 696, 696, 696, 696) SMTG_TYPE_ALIGN_CHECK (PFactoryInfo, 4, 4, 4, 4) SMTG_TYPE_ALIGN_CHECK (PClassInfo, 4, 4, 4, 4) SMTG_TYPE_ALIGN_CHECK (PClassInfo2, 4, 4, 4, 4) SMTG_TYPE_ALIGN_CHECK (PClassInfoW, 4, 4, 4, 4) // ivstaudioprocessor.h SMTG_TYPE_SIZE_CHECK (ProcessSetup, 24, 20, 24, 24) SMTG_TYPE_SIZE_CHECK (AudioBusBuffers, 24, 16, 24, 24) SMTG_TYPE_SIZE_CHECK (ProcessData, 80, 48, 48, 48) SMTG_TYPE_ALIGN_CHECK (ProcessSetup, 8, 1, 8, 8) SMTG_TYPE_ALIGN_CHECK (AudioBusBuffers, 8, 1, 8, 8) SMTG_TYPE_ALIGN_CHECK (ProcessData, 8, 1, 4, 4) // ivstcomponent.h SMTG_TYPE_SIZE_CHECK (BusInfo, 276, 276, 276, 276) SMTG_TYPE_SIZE_CHECK (RoutingInfo, 12, 12, 12, 12) SMTG_TYPE_ALIGN_CHECK (BusInfo, 4, 1, 4, 4) SMTG_TYPE_ALIGN_CHECK (RoutingInfo, 4, 1, 4, 4) // ivstcontextmenu.h SMTG_TYPE_SIZE_CHECK (IContextMenuItem, 264, 264, 264, 264) SMTG_TYPE_ALIGN_CHECK (IContextMenuItem, 4, 1, 4, 4) // ivsteditcontroller.h SMTG_TYPE_SIZE_CHECK (ParameterInfo, 792, 792, 792, 792) SMTG_TYPE_ALIGN_CHECK (ParameterInfo, 8, 1, 8, 8) // ivstevents.h SMTG_TYPE_SIZE_CHECK (NoteOnEvent, 20, 20, 20, 20) SMTG_TYPE_SIZE_CHECK (NoteOffEvent, 16, 16, 16, 16) SMTG_TYPE_SIZE_CHECK (DataEvent, 16, 12, 12, 12) SMTG_TYPE_SIZE_CHECK (PolyPressureEvent, 12, 12, 12, 12) SMTG_TYPE_SIZE_CHECK (ChordEvent, 16, 12, 12, 12) SMTG_TYPE_SIZE_CHECK (ScaleEvent, 16, 10, 12, 12) SMTG_TYPE_SIZE_CHECK (LegacyMIDICCOutEvent, 4, 4, 4, 4) SMTG_TYPE_SIZE_CHECK (Event, 48, 40, 40, 48) SMTG_TYPE_ALIGN_CHECK (NoteOnEvent, 4, 1, 4, 4) SMTG_TYPE_ALIGN_CHECK (NoteOffEvent, 4, 1, 4, 4) SMTG_TYPE_ALIGN_CHECK (DataEvent, 8, 1, 4, 4) SMTG_TYPE_ALIGN_CHECK (PolyPressureEvent, 4, 1, 4, 4) SMTG_TYPE_ALIGN_CHECK (ChordEvent, 8, 1, 4, 4) SMTG_TYPE_ALIGN_CHECK (ScaleEvent, 8, 1, 4, 4) SMTG_TYPE_ALIGN_CHECK (LegacyMIDICCOutEvent, 1, 1, 1, 1) SMTG_TYPE_ALIGN_CHECK (Event, 8, 1, 8, 8) // ivstnoteexpression.h SMTG_TYPE_SIZE_CHECK (NoteExpressionValueDescription, 32, 28, 32, 32) SMTG_TYPE_SIZE_CHECK (NoteExpressionValueEvent, 16, 16, 16, 16) SMTG_TYPE_SIZE_CHECK (NoteExpressionTextEvent, 24, 16, 16, 16) SMTG_TYPE_SIZE_CHECK (NoteExpressionTypeInfo, 816, 812, 816, 816) SMTG_TYPE_SIZE_CHECK (KeyswitchInfo, 536, 536, 536, 536) SMTG_TYPE_ALIGN_CHECK (NoteExpressionValueDescription, 8, 1, 8, 8) SMTG_TYPE_ALIGN_CHECK (NoteExpressionValueEvent, 8, 1, 4, 8) SMTG_TYPE_ALIGN_CHECK (NoteExpressionTextEvent, 8, 1, 4, 4) SMTG_TYPE_ALIGN_CHECK (NoteExpressionTypeInfo, 8, 1, 8, 8) SMTG_TYPE_ALIGN_CHECK (KeyswitchInfo, 4, 1, 4, 4) // ivstprocesscontext.h SMTG_TYPE_SIZE_CHECK (FrameRate, 8, 8, 8, 8) SMTG_TYPE_SIZE_CHECK (Chord, 4, 4, 4, 4) SMTG_TYPE_SIZE_CHECK (ProcessContext, 112, 104, 112, 112) SMTG_TYPE_ALIGN_CHECK (FrameRate, 4, 1, 4, 4) SMTG_TYPE_ALIGN_CHECK (Chord, 2, 1, 2, 2) SMTG_TYPE_ALIGN_CHECK (ProcessContext, 8, 1, 8, 8) // ivstrepresentation.h SMTG_TYPE_SIZE_CHECK (RepresentationInfo, 256, 256, 256, 256) SMTG_TYPE_ALIGN_CHECK (RepresentationInfo, 1, 1, 1, 1) // ivstunits.h SMTG_TYPE_SIZE_CHECK (UnitInfo, 268, 268, 268, 268) SMTG_TYPE_SIZE_CHECK (ProgramListInfo, 264, 264, 264, 264) SMTG_TYPE_ALIGN_CHECK (UnitInfo, 4, 1, 4, 4) SMTG_TYPE_ALIGN_CHECK (ProgramListInfo, 4, 1, 4, 4) #endif // SMTG_VST_COMPILE_TIME_STRUCT_CHECK //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ #define SMTG_PRINT_TYPE_SIZE_ALIGN(T) \ { \ auto len = strlen (#T); \ std::printf (#T); \ for (auto i = len; i < 35; ++i) \ std::printf (" "); \ std::printf ("size = %3zu | align = %2zu\n", sizeof (T), alignof (T)); \ } //------------------------------------------------------------------------ inline void printStructSizes () { // ipluginbase.h SMTG_PRINT_TYPE_SIZE_ALIGN (PFactoryInfo); SMTG_PRINT_TYPE_SIZE_ALIGN (PClassInfo); SMTG_PRINT_TYPE_SIZE_ALIGN (PClassInfo2); SMTG_PRINT_TYPE_SIZE_ALIGN (PClassInfoW); // ivstaudioprocessor.h SMTG_PRINT_TYPE_SIZE_ALIGN (ProcessSetup); SMTG_PRINT_TYPE_SIZE_ALIGN (AudioBusBuffers); SMTG_PRINT_TYPE_SIZE_ALIGN (ProcessData); // ivstcomponent.h SMTG_PRINT_TYPE_SIZE_ALIGN (BusInfo); SMTG_PRINT_TYPE_SIZE_ALIGN (RoutingInfo); // ivstcontextmenu.h SMTG_PRINT_TYPE_SIZE_ALIGN (IContextMenuItem); // ivsteditcontroller.h SMTG_PRINT_TYPE_SIZE_ALIGN (ParameterInfo); // ivstevents.h SMTG_PRINT_TYPE_SIZE_ALIGN (NoteOnEvent); SMTG_PRINT_TYPE_SIZE_ALIGN (NoteOffEvent); SMTG_PRINT_TYPE_SIZE_ALIGN (DataEvent); SMTG_PRINT_TYPE_SIZE_ALIGN (PolyPressureEvent); SMTG_PRINT_TYPE_SIZE_ALIGN (ChordEvent); SMTG_PRINT_TYPE_SIZE_ALIGN (ScaleEvent); SMTG_PRINT_TYPE_SIZE_ALIGN (LegacyMIDICCOutEvent); SMTG_PRINT_TYPE_SIZE_ALIGN (Event); // ivstnoteexpression.h SMTG_PRINT_TYPE_SIZE_ALIGN (NoteExpressionValueDescription); SMTG_PRINT_TYPE_SIZE_ALIGN (NoteExpressionValueEvent); SMTG_PRINT_TYPE_SIZE_ALIGN (NoteExpressionTextEvent); SMTG_PRINT_TYPE_SIZE_ALIGN (NoteExpressionTypeInfo); SMTG_PRINT_TYPE_SIZE_ALIGN (KeyswitchInfo); // ivstprocesscontext.h SMTG_PRINT_TYPE_SIZE_ALIGN (FrameRate); SMTG_PRINT_TYPE_SIZE_ALIGN (Chord); SMTG_PRINT_TYPE_SIZE_ALIGN (ProcessContext); // ivstrepresentation.h SMTG_PRINT_TYPE_SIZE_ALIGN (RepresentationInfo); // ivstunits.h SMTG_PRINT_TYPE_SIZE_ALIGN (UnitInfo); SMTG_PRINT_TYPE_SIZE_ALIGN (ProgramListInfo); } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/general0000644000000000000000000000013015124701711023742 xustar0029 mtime=1767080905.28382083 30 atime=1767080905.283385111 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/0000755000175000001440000000000015124701711024011 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/suspendresume.cpp0000644000000000000000000000012715124701711027433 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/suspendresume.cpp0000644000175000001440000000461415124701711027424 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/suspendresume.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/general/suspendresume.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // SuspendResumeTest //------------------------------------------------------------------------ SuspendResumeTest::SuspendResumeTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : TestEnh (plugProvider, sampl) { } //------------------------------------------------------------------------ bool PLUGIN_API SuspendResumeTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); for (int32 i = 0; i < 3; ++i) { if (audioEffect) { if (audioEffect->canProcessSampleSize (kSample32) == kResultOk) processSetup.symbolicSampleSize = kSample32; else if (audioEffect->canProcessSampleSize (kSample64) == kResultOk) processSetup.symbolicSampleSize = kSample64; else { addErrorMessage (testResult, STR ("No appropriate symbolic sample size supported!")); return false; } if (audioEffect->setupProcessing (processSetup) != kResultOk) { addErrorMessage (testResult, STR ("Process setup failed!")); return false; } } tresult result = vstPlug->setActive (true); if (result != kResultOk) return false; result = vstPlug->setActive (false); if (result != kResultOk) return false; } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/terminit.h0000644000000000000000000000012715124701711026031 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/terminit.h0000644000175000001440000000313115124701711026013 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/terminit.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Terminate/Initialize. * \ingroup TestClass */ class TerminateInitializeTest : public TestBase { public: //------------------------------------------------------------------------ TerminateInitializeTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("Terminate/Initialize") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/plugcompat.cpp0000644000000000000000000000012715124701711026704 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/plugcompat.cpp0000644000175000001440000000576415124701711026704 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/plugcompat.cpp // Created by : Steinberg, 03/2022 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/general/plugcompat.h" #include "public.sdk/source/vst/moduleinfo/moduleinfoparser.h" #include "pluginterfaces/base/funknownimpl.h" #include //------------------------------------------------------------------------ namespace Steinberg { //------------------------------------------------------------------------ namespace { //------------------------------------------------------------------------ struct StringStream : U::ImplementsNonDestroyable> { std::string str; tresult PLUGIN_API read (void*, int32, int32*) override { return kNotImplemented; } tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten) override { str.append (static_cast (buffer), numBytes); if (numBytesWritten) *numBytesWritten = numBytes; return kResultTrue; } tresult PLUGIN_API seek (int64, int32, int64*) override { return kNotImplemented; } tresult PLUGIN_API tell (int64*) override { return kNotImplemented; } }; //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ bool checkPluginCompatibility (VST3::Hosting::Module::Ptr& module, IPtr compat, std::ostream* errorStream) { bool failure = false; if (auto moduleInfoPath = VST3::Hosting::Module::getModuleInfoPath (module->getPath ())) { if (errorStream) { *errorStream << "Warning: The module contains a moduleinfo.json file and the module factory exports a IPluginCompatibility class. The moduleinfo.json one is preferred.\n"; } } StringStream strStream; if (compat->getCompatibilityJSON (&strStream) != kResultTrue) { if (errorStream) { *errorStream << "Error: Call to IPluginCompatiblity::getCompatibilityJSON (IBStream*) failed\n"; } failure = true; } else if (auto result = ModuleInfoLib::parseCompatibilityJson (strStream.str, errorStream)) { // TODO: Check that the "New" classes are part of the Module; } else { failure = true; } return !failure; } //------------------------------------------------------------------------ } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/scanparameters.h0000644000000000000000000000012715124701711027206 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/scanparameters.h0000644000175000001440000000311315124701711027170 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/scanparameters.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Scan Parameters. * \ingroup TestClass */ class ScanParametersTest : public TestBase { public: //------------------------------------------------------------------------ ScanParametersTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("Scan Parameters") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/parameterfunctionname.cp0000644000000000000000000000012715124701711030740 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/parameterfunctionname.cpp0000644000175000001440000001054515124701711031111 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/parameterfunctionname.cpp // Created by : Steinberg, 04/2020 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/general/parameterfunctionname.h" #include "public.sdk/source/vst/utility/stringconvert.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/vst/ivstparameterfunctionname.h" #include "pluginterfaces/vst/ivstunits.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ParameterFunctionNameTest //------------------------------------------------------------------------ ParameterFunctionNameTest::ParameterFunctionNameTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API ParameterFunctionNameTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); if (!controller) { addMessage (testResult, STR ("No Edit Controller supplied!")); return true; } auto iParameterFunctionName = U::cast (controller); if (!iParameterFunctionName) { addMessage (testResult, STR ("No IParameterFunctionName support.")); return true; } addMessage (testResult, STR ("IParameterFunctionName supported.")); int32 numParameters = controller->getParameterCount (); if (numParameters <= 0) { addMessage (testResult, STR ("This component does not export any parameters!")); return true; } // used for ID check std::unordered_map paramIds; for (int32 i = 0; i < numParameters; ++i) { ParameterInfo paramInfo = {}; tresult result = controller->getParameterInfo (i, paramInfo); if (result != kResultOk) { addErrorMessage (testResult, printf ("Parameter %03d: is missing!!!", i)); return false; } int32 paramId = paramInfo.id; if (paramId < 0) { addErrorMessage (testResult, printf ("Parameter %03d (id=%d): Invalid Id!!!", i, paramId)); return false; } auto search = paramIds.find (paramId); if (search != paramIds.end ()) { addErrorMessage (testResult, printf ("Parameter %03d (id=%d): ID already used by idx=%03d!!!", i, paramId, search->second)); return false; } else paramIds[paramId] = i; } // end for each parameter auto iUnitInfo2 = U::cast (controller); const CString arrayFunctionName[] = {FunctionNameType::kCompGainReduction, FunctionNameType::kCompGainReductionMax, FunctionNameType::kCompGainReductionPeakHold, FunctionNameType::kCompResetGainReductionMax, FunctionNameType::kLowLatencyMode, FunctionNameType::kRandomize, FunctionNameType::kDryWetMix}; ParamID paramID; for (auto item : arrayFunctionName) { if (iParameterFunctionName->getParameterIDFromFunctionName (kRootUnitId, item, paramID) == kResultTrue) { addMessage (testResult, printf ("FunctionName %s supported => paramID %d", item, paramID)); auto search = paramIds.find (paramID); if (search == paramIds.end ()) { addErrorMessage ( testResult, printf ("Parameter (id=%d) for FunctionName %s: not Found!!!", paramID, item)); return false; } } } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/scanparameters.cpp0000644000000000000000000000012715124701711027541 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/scanparameters.cpp0000644000175000001440000002577315124701711027543 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/scanparameters.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/general/scanparameters.h" #include "public.sdk/source/vst/utility/stringconvert.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/vst/ivstunits.h" #include #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ScanParametersTest //------------------------------------------------------------------------ ScanParametersTest::ScanParametersTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API ScanParametersTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); if (!controller) { addMessage (testResult, STR ("No Edit Controller supplied!")); return true; } int32 numParameters = controller->getParameterCount (); if (numParameters <= 0) { addMessage (testResult, STR ("This component does not export any parameters!")); return true; } addMessage (testResult, printf ("This component exports %d parameter(s)", numParameters)); auto iUnitInfo2 = U::cast (controller); if (!iUnitInfo2 && numParameters > 20) { addMessage ( testResult, STR ("Note: it could be better to use UnitInfo in order to sort Parameters (>20).")); } struct SUnitCount { int32 idx {-1}; int32 numParams {0}; std::string name; }; std::unordered_map unitIds; // root unit unitIds[kRootUnitId].name = "Root"; if (iUnitInfo2) { for (int32 ui = 0, uc = iUnitInfo2->getUnitCount (); ui < uc; ++ui) { UnitInfo uinfo = {}; if (iUnitInfo2->getUnitInfo (ui, uinfo) == kResultTrue) { // check if ID is already used by another unit if (uinfo.id != kRootUnitId) { auto search = unitIds.find (uinfo.id); if (search != unitIds.end ()) { addErrorMessage ( testResult, printf ("=>Unit %03d (id=%d): ID already used by idx=%03d!!!", ui, uinfo.id, search->second.idx)); return false; } } std::string unitTitle; unitTitle = StringConvert::convert (uinfo.name); unitIds[uinfo.id] = {ui, 0, unitTitle}; // init counter } else { addErrorMessage (testResult, printf ("IUnitInfo::getUnitInfo (%d..) failed.", ui)); return false; } } } // used for ID check std::unordered_map paramIds; std::map> listTitleUnitMap; bool foundBypass = false; for (int32 i = 0; i < numParameters; ++i) { ParameterInfo paramInfo = {}; tresult result = controller->getParameterInfo (i, paramInfo); if (result != kResultOk) { addErrorMessage (testResult, printf ("=>Parameter %03d: is missing!!!", i)); return false; } int32 paramId = paramInfo.id; if (paramId < 0) { addErrorMessage (testResult, printf ("=>Parameter %03d (id=%d): Invalid Id!!!", i, paramId)); return false; } // check if ID is already used by another parameter auto search = paramIds.find (paramId); if (search != paramIds.end ()) { addErrorMessage (testResult, printf ("=>Parameter %03d (id=%d): ID already used by idx=%03d!!!", i, paramId, search->second)); return false; } paramIds[paramId] = i; const char8* paramType = kEmptyString8; if (paramInfo.stepCount < 0) { addErrorMessage ( testResult, printf ("=>Parameter %03d (id=%d): invalid stepcount (<0)!!!", i, paramId)); return false; } if (paramInfo.stepCount == 0) paramType = "Float"; else if (paramInfo.stepCount == 1) paramType = "Toggle"; else paramType = "Discrete"; auto paramTitle = StringConvert::convert (paramInfo.title); auto paramUnits = StringConvert::convert (paramInfo.units); addMessage ( testResult, printf ( R"( Parameter %03d (id=%d): [title="%s"] [unit="%s"] [type = %s, default = %lf, unit = %d])", i, paramId, paramTitle.data (), paramUnits.data (), paramType, paramInfo.defaultNormalizedValue, paramInfo.unitId)); if (paramTitle.empty ()) { addErrorMessage (testResult, printf ("=>Parameter %03d (id=%d): has no title!!!", i, paramId)); return false; } // check if the same title is present in the same unit auto it = listTitleUnitMap.find (paramInfo.unitId); if (it != listTitleUnitMap.end ()) { const std::vector& list = it->second; auto found = std::find (list.begin (), list.end (), paramTitle); if (found != list.end ()) { addMessage ( testResult, printf ( "=>Parameter %03d (id=%d): [title=\"%s\"] has the same title as another parameter in this unit = %d!", i, paramId, paramTitle.c_str (), paramInfo.unitId)); } } listTitleUnitMap[paramInfo.unitId].push_back (paramTitle); if (paramInfo.defaultNormalizedValue != -1.f && (paramInfo.defaultNormalizedValue < 0. || paramInfo.defaultNormalizedValue > 1.)) { addErrorMessage ( testResult, printf ( "=>Parameter %03d (id=%d): paramInfo.defaultNormalizedValue is not normalized!!!", i, paramId)); return false; } int32 unitId = paramInfo.unitId; if (unitId < -1) { addErrorMessage ( testResult, printf ("=>Parameter %03d (id=%d): No appropriate unit ID!!!", i, paramId)); return false; } if (unitId >= -1) { auto iUnitInfo = U::cast (controller); if (!iUnitInfo && unitId != kRootUnitId) { addErrorMessage ( testResult, printf ( "IUnitInfo interface is missing, but ParameterInfo::unitID is not %03d (kRootUnitId).", kRootUnitId)); return false; } auto searchUnit = unitIds.find (unitId); if (searchUnit != unitIds.end ()) { unitIds[unitId].numParams++; } else if (unitId != kRootUnitId) { addErrorMessage ( testResult, printf ( "=>Parameter %03d (id=%d) has a UnitID (%d), which isn't defined in IUnitInfo.", i, paramId, unitId)); return false; } else // if (unitId == kRootUnitId) unitIds[kRootUnitId].numParams++; } //---check for incompatible flags--------------------- // kCanAutomate and kIsReadOnly if (((paramInfo.flags & ParameterInfo::kCanAutomate) != 0) && ((paramInfo.flags & ParameterInfo::kIsReadOnly) != 0)) { addErrorMessage ( testResult, printf ( "=>Parameter %03d (id=%d) must not be kCanAutomate and kReadOnly at the same time.", i, paramId)); return false; } // kIsProgramChange and kIsReadOnly if (((paramInfo.flags & ParameterInfo::kIsProgramChange) != 0) && ((paramInfo.flags & ParameterInfo::kIsReadOnly) != 0)) { addErrorMessage ( testResult, printf ( "=>Parameter %03d (id=%d) must not be kIsProgramChange and kReadOnly at the same time.", i, paramId)); return false; } // kIsBypass only or kIsBypass and kCanAutomate only if (((paramInfo.flags & ParameterInfo::kIsBypass) != 0) && !((paramInfo.flags == ParameterInfo::kIsBypass) || ((paramInfo.flags & ParameterInfo::kCanAutomate) != 0))) { addErrorMessage ( testResult, printf ( "=>Parameter %03d (id=%d) is kIsBypass and could have only kCanAutomate as other flag at the same time.", i, paramId)); return false; } //---maybe wrong combination of flags------------------- // kIsBypass but not kCanAutomate if (paramInfo.flags == ParameterInfo::kIsBypass) { addMessage (testResult, printf ("=>Parameter %03d (id=%d) is kIsBypass, but not kCanAutomate!", i, paramId)); } // kIsHidden and (kCanAutomate or not kIsReadOnly) if (paramInfo.flags == ParameterInfo::kIsHidden) { if ((paramInfo.flags & ParameterInfo::kCanAutomate) != 0) { addMessage ( testResult, printf ("=>Parameter %03d (id=%d) is kIsHidden and kCanAutomate!", i, paramId)); } if ((paramInfo.flags & ParameterInfo::kIsReadOnly) == 0) { addMessage (testResult, printf ("=>Parameter %03d (id=%d) is kIsHidden and NOT kIsReadOnly!", i, paramId)); } } // kIsProgramChange and not kIsList if (((paramInfo.flags & ParameterInfo::kIsProgramChange) != 0) && ((paramInfo.flags & ParameterInfo::kIsList) == 0)) { addMessage (testResult, printf ("=>Parameter %03d (id=%d) is kIsProgramChange, but not a kIsList!", i, paramId)); } // kIsReadOnly and kIsWrapAround if (((paramInfo.flags & ParameterInfo::kIsReadOnly) != 0) && ((paramInfo.flags & ParameterInfo::kIsWrapAround) != 0)) { addMessage ( testResult, printf ("=>Parameter %03d (id=%d) is kIsReadOnly, no need to be kIsWrapAround too!", i, paramId)); } //---check bypass-------------------------------------- if ((paramInfo.flags & ParameterInfo::kIsBypass) != 0) { if (!foundBypass) foundBypass = true; else { addErrorMessage ( testResult, printf ("=>Parameter %03d (id=%d): There can only be one bypass (kIsBypass).", i, paramId)); return false; } } } // end for each parameter for (const auto& unit : unitIds) { if (unit.second.numParams > 128) // 128 due to MIDI CC mapped parameter { addMessage ( testResult, printf ( "Note: This Unit (idx=%d, id=%d, name=\"%s\") has %d parameters: it could be better to split it in sub-units!.", unit.second.idx, unit.first, unit.second.name.data (), unit.second.numParams)); } } if (foundBypass == false) { StringResult subCat; plugProvider->getSubCategories (subCat); if (subCat.get ().find ("Instrument") != std::string::npos) addMessage (testResult, STR ("No bypass parameter found. This is an instrument.")); else addMessage (testResult, STR ("Warning: No bypass parameter found. Is this intended ?")); } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/midilearn.cpp0000644000000000000000000000012715124701711026475 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/midilearn.cpp0000644000175000001440000000453715124701711026472 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/midilearn.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/general/midilearn.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/vst/ivstmidicontrollers.h" #include "pluginterfaces/vst/ivstmidilearn.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // MidiLearnTest //------------------------------------------------------------------------ MidiLearnTest::MidiLearnTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API MidiLearnTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); if (!controller) { addMessage (testResult, STR ("No Edit Controller supplied!")); return true; } auto midiLearn = U::cast (controller); if (!midiLearn) { addMessage (testResult, STR ("No MIDI Learn interface supplied!")); return true; } if (midiLearn->onLiveMIDIControllerInput (0, 0, ControllerNumbers::kCtrlPan) != kResultTrue) addMessage (testResult, STR ("onLiveMIDIControllerInput do not return kResultTrue!")); if (midiLearn->onLiveMIDIControllerInput (0, 0, ControllerNumbers::kCtrlVibratoDelay) != kResultTrue) addMessage (testResult, STR ("onLiveMIDIControllerInput do not return kResultTrue!")); return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/suspendresume.h0000644000000000000000000000012715124701711027100 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/suspendresume.h0000644000175000001440000000313615124701711027067 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/suspendresume.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Suspend/Resume. * \ingroup TestClass */ class SuspendResumeTest : public TestEnh { public: //------------------------------------------------------------------------ SuspendResumeTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); DECLARE_VSTTEST ("Suspend/Resume") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/midimapping.cpp0000644000000000000000000000012715124701711027027 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/midimapping.cpp0000644000175000001440000001000115124701711027003 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/midimapping.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/general/midimapping.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/vst/ivstmidicontrollers.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // MidiMappingTest //------------------------------------------------------------------------ MidiMappingTest::MidiMappingTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API MidiMappingTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); if (!controller) { addMessage (testResult, STR ("No Edit Controller supplied!")); return true; } auto midiMapping = U::cast (controller); if (!midiMapping) { addMessage (testResult, STR ("No MIDI Mapping interface supplied!")); return true; } int32 numParameters = controller->getParameterCount (); int32 eventBusCount = vstPlug->getBusCount (kEvent, kInput); bool interruptProcess = false; std::unordered_set parameterIds; for (int32 i = 0; i < numParameters; ++i) { ParameterInfo parameterInfo; if (controller->getParameterInfo (i, parameterInfo) == kResultTrue) parameterIds.insert (parameterInfo.id); } for (int32 bus = 0; bus < eventBusCount + 1; bus++) { if (interruptProcess) break; BusInfo info; if (vstPlug->getBusInfo (kEvent, kInput, bus, info) == kResultTrue) { if (bus >= eventBusCount) { addMessage (testResult, STR ("getBusInfo supplied for an unknown event bus")); break; } } else break; for (int16 channel = 0; channel < info.channelCount; channel++) { if (interruptProcess) break; int32 foundCount = 0; // test with the cc outside the valid range too (>=kCountCtrlNumber) for (CtrlNumber cc = 0; cc < kCountCtrlNumber + 1; cc++) { ParamID tag; if (midiMapping->getMidiControllerAssignment (bus, channel, cc, tag) == kResultTrue) { if (bus >= eventBusCount) { addMessage (testResult, STR ("MIDI Mapping supplied for an unknown event bus")); interruptProcess = true; break; } if (cc >= kCountCtrlNumber) { addMessage ( testResult, STR ( "MIDI Mapping supplied for a wrong ControllerNumbers value (bigger than the max)")); break; } if (parameterIds.find (tag) == parameterIds.end ()) { addErrorMessage ( testResult, printf ("Unknown ParamID [%d] returned for MIDI Mapping", tag)); return false; } foundCount++; } else { if (bus >= eventBusCount) interruptProcess = true; } } if (foundCount == 0 && (bus < eventBusCount)) { addMessage ( testResult, printf ( "MIDI Mapping getMidiControllerAssignment (%d, %d) : no assignment available!", bus, channel)); } } } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/midilearn.h0000644000000000000000000000012715124701711026142 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/midilearn.h0000644000175000001440000000274715124701711026140 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/midilearn.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test MIDI Learn. * \ingroup TestClass */ class MidiLearnTest : public TestBase { public: MidiLearnTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("MIDI Learn") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/midimapping.h0000644000000000000000000000012715124701711026474 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/midimapping.h0000644000175000001440000000307415124701711026464 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/midimapping.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test MIDI Mapping. * \ingroup TestClass */ class MidiMappingTest : public TestBase { public: //------------------------------------------------------------------------ MidiMappingTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("MIDI Mapping") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/terminit.cpp0000644000000000000000000000012715124701711026364 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/terminit.cpp0000644000175000001440000000421215124701711026347 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/terminit.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/general/terminit.h" #include "pluginterfaces/base/funknownimpl.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // TerminateInitializeTest //------------------------------------------------------------------------ TerminateInitializeTest::TerminateInitializeTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool TerminateInitializeTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); auto plugBase = U::cast (vstPlug); if (!plugBase) { addErrorMessage (testResult, STR ("No IPluginBase interface available.")); return false; } bool result = true; if (plugBase->terminate () != kResultTrue) { addErrorMessage (testResult, STR ("IPluginBase::terminate () failed.")); result = false; } if (plugBase->initialize (TestingPluginContext::get ()) != kResultTrue) { addErrorMessage (testResult, STR ("IPluginBase::initialize (..) failed.")); result = false; } return result; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/plugcompat.h0000644000000000000000000000012715124701711026351 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/plugcompat.h0000644000175000001440000000256515124701711026345 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/plugcompat.h // Created by : Steinberg, 03/2022 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/hosting/module.h" #include "pluginterfaces/base/iplugincompatibility.h" #include //------------------------------------------------------------------------ namespace Steinberg { //------------------------------------------------------------------------ bool checkPluginCompatibility (VST3::Hosting::Module::Ptr& module, IPtr compat, std::ostream* errorStream); //------------------------------------------------------------------------ } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/parameterfunctionname.h0000644000000000000000000000012715124701711030565 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/parameterfunctionname.h0000644000175000001440000000327315124701711030556 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/ParameterFunctionNameTest.h // Created by : Steinberg, 04/2020 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Parameter Function Name. \ingroup TestClass */ //------------------------------------------------------------------------ class ParameterFunctionNameTest : public TestBase { public: //------------------------------------------------------------------------ ParameterFunctionNameTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("Parameter Function Name") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/editorclasses.h0000644000000000000000000000013015124701711027034 xustar0029 mtime=1767080905.28382083 30 atime=1767080905.283385111 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/editorclasses.h0000644000175000001440000000300515124701711027024 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/editorclasses.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Scan Editor Classes. * \ingroup TestClass */ class EditorClassesTest : public TestBase { public: EditorClassesTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("Scan Editor Classes") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/PaxHeaders/editorclasses.cpp0000644000000000000000000000013215124701711027371 xustar0030 mtime=1767080905.283385111 30 atime=1767080905.283385111 30 ctime=1767080905.283385111 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/general/editorclasses.cpp0000644000175000001440000000472615124701711027372 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/general/editorclasses.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/general/editorclasses.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // EditorClassesTest //------------------------------------------------------------------------ EditorClassesTest::EditorClassesTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API EditorClassesTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); // no controller is allowed... if (FUnknownPtr (vstPlug).getInterface ()) { addMessage (testResult, STR ("Processor and edit controller united.")); return true; } TUID controllerClassTUID; if (vstPlug->getControllerClassId (controllerClassTUID) != kResultOk) { addMessage (testResult, STR ("This component does not export an edit controller class ID!!!")); return true; } FUID controllerClassUID; controllerClassUID = FUID::fromTUID (controllerClassTUID); if (controllerClassUID.isValid () == false) { addErrorMessage (testResult, STR ("The edit controller class has no valid UID!!!")); return false; } addMessage (testResult, STR ("This component has an edit controller class")); char8 cidString[50]; controllerClassUID.toRegistryString (cidString); addMessage (testResult, printf (" Controller CID: %s", cidString)); return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/processing0000644000000000000000000000013215124701711024503 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284343858 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/0000755000175000001440000000000015124701711024550 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/automation.h0000644000000000000000000000013215124701711027111 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284343858 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/automation.h0000644000175000001440000000751115124701711027105 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/automation.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/process.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { class ParamChanges; //------------------------------------------------------------------------ /** Test Automation. * \ingroup TestClass */ class AutomationTest : public ProcessTest, public IParameterChanges { public: AutomationTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl, int32 everyNSamples, int32 numParams, bool sampleAccuracy); ~AutomationTest () override; const char* getName () const SMTG_OVERRIDE; // ITest bool PLUGIN_API setup () SMTG_OVERRIDE; bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; bool PLUGIN_API teardown () SMTG_OVERRIDE; // IParameterChanges int32 PLUGIN_API getParameterCount () SMTG_OVERRIDE; IParamValueQueue* PLUGIN_API getParameterData (int32 index) SMTG_OVERRIDE; IParamValueQueue* PLUGIN_API addParameterData (const ParamID& id, int32& index) SMTG_OVERRIDE; // FUnknown DELEGATE_REFCOUNT (ProcessTest) tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) override; //------------------------------------------------------------------------ protected: bool preProcess (ITestResult* testResult) SMTG_OVERRIDE; bool postProcess (ITestResult* testResult) SMTG_OVERRIDE; ParamID bypassId; using ParamChangeVector = std::vector>; ParamChangeVector paramChanges; int32 countParamChanges; int32 everyNSamples; int32 numParams; bool sampleAccuracy; bool onceExecuted; }; //------------------------------------------------------------------------ /** Test Parameters Flush (no Buffer). * \ingroup TestClass */ class FlushParamTest : public AutomationTest { public: FlushParamTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Parameters Flush (no Buffer)") protected: virtual void prepareProcessData (); }; //------------------------------------------------------------------------ /** Test Parameters Flush 2 (no Buffer). * \ingroup TestClass */ class FlushParamTest2 : public FlushParamTest { public: FlushParamTest2 (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); bool PLUGIN_API teardown () SMTG_OVERRIDE; DECLARE_VSTTEST ("Parameters Flush 2 (only numChannel==0)") protected: void prepareProcessData () SMTG_OVERRIDE; int32 numInputs {0}; int32 numOutputs {0}; int32 numChannelsIn {0}; int32 numChannelsOut {0}; }; //------------------------------------------------------------------------ /** Test Parameters Flush 3 (no Buffer, no parameter change). * \ingroup TestClass */ class FlushParamTest3 : public FlushParamTest { public: FlushParamTest3 (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); DECLARE_VSTTEST ("Parameters Flush 2 (no Buffer, no parameter change)") protected: }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/processinputoverwriti0000644000000000000000000000013215124701711031214 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/processinputoverwriting.cpp0000644000175000001440000001175515124701711032323 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/processinputoverwriting.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/processinputoverwriting.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ProcessInputOverwritingTest //------------------------------------------------------------------------ ProcessInputOverwritingTest::ProcessInputOverwritingTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : ProcessTest (plugProvider, sampl) { } //------------------------------------------------------------------------ bool ProcessInputOverwritingTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); bool ret = ProcessTest::run (testResult); return ret; } //------------------------------------------------------------------------ bool ProcessInputOverwritingTest::preProcess (ITestResult* /*testResult*/) { int32 min = processData.numInputs < processData.numOutputs ? processData.numInputs : processData.numOutputs; noNeedtoProcess = true; for (int32 i = 0; i < min; i++) { if (!noNeedtoProcess) break; int32 minChannel = processData.inputs[i].numChannels < processData.outputs[i].numChannels ? processData.inputs[i].numChannels : processData.outputs[i].numChannels; auto ptrIn = processData.inputs[i].channelBuffers32; auto ptrOut = processData.outputs[i].channelBuffers32; for (int32 j = 0; j < minChannel; j++) { if (ptrIn[j] != ptrOut[j]) { noNeedtoProcess = false; break; } } } if (noNeedtoProcess) return true; for (int32 i = 0; i < processData.numInputs; i++) { if (processSetup.symbolicSampleSize == kSample32) { auto ptr = processData.inputs[i].channelBuffers32; if (ptr) { float inc = 1.f / (processData.numSamples - 1); for (int32 c = 0; c < processData.inputs[i].numChannels; c++) { auto chaBuf = ptr[c]; for (int32 j = 0; j < processData.numSamples; j++) { *chaBuf = inc * j; chaBuf++; } } } } else if (processSetup.symbolicSampleSize == kSample64) { auto ptr = processData.inputs[i].channelBuffers64; if (ptr) { double inc = 1.0 / (processData.numSamples - 1); for (int32 c = 0; c < processData.inputs[i].numChannels; c++) { auto chaBuf = ptr[c]; for (int32 j = 0; j < processData.numSamples; j++) { *chaBuf = inc * j; chaBuf++; } } } } } return true; } //------------------------------------------------------------------------ bool ProcessInputOverwritingTest::postProcess (ITestResult* testResult) { if (noNeedtoProcess) return true; for (int32 i = 0; i < processData.numInputs; i++) { if (processSetup.symbolicSampleSize == kSample32) { auto ptr = processData.inputs[i].channelBuffers32; if (ptr) { float inc = 1.f / (processData.numSamples - 1); for (int32 c = 0; c < processData.inputs[i].numChannels; c++) { auto chaBuf = ptr[c]; for (int32 j = 0; j < processData.numSamples; j++) { if (*chaBuf != inc * j) { addErrorMessage ( testResult, STR ( "IAudioProcessor::process overwrites input buffer (..with kSample32..)!")); return false; } chaBuf++; } } } } else if (processSetup.symbolicSampleSize == kSample64) { auto ptr = processData.inputs[i].channelBuffers64; if (ptr) { double inc = 1.0 / (processData.numSamples - 1); for (int32 c = 0; c < processData.inputs[i].numChannels; c++) { auto chaBuf = ptr[c]; for (int32 j = 0; j < processData.numSamples; j++) { if (*chaBuf != inc * j) { addErrorMessage ( testResult, STR ( "IAudioProcessor::process overwrites input buffer (..with kSample64..)!")); return false; } chaBuf++; } } } } } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/processcontextrequire0000644000000000000000000000013215124701711031163 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/processcontextrequirements.h0000644000175000001440000000301615124701711032450 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/processcontextrequirements.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Silence Flags. * \ingroup TestClass */ class ProcessContextRequirementsTest : public TestEnh { public: ProcessContextRequirementsTest (ITestPlugProvider* plugProvider); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; bool PLUGIN_API setup () SMTG_OVERRIDE; DECLARE_VSTTEST ("ProcessContext Requirements") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/variableblocksize.h0000644000000000000000000000013215124701711030424 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/variableblocksize.h0000644000175000001440000000275715124701711030427 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/variableblocksize.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/process.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Variable Block Size. * \ingroup TestClass */ class VariableBlockSizeTest : public ProcessTest { public: VariableBlockSizeTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Variable Block Size") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/speakerarrangement.cp0000644000000000000000000000013215124701711030762 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/speakerarrangement.cpp0000644000175000001440000002002615124701711031132 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/speakerarrangement.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/speakerarrangement.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // SpeakerArrangementTest //------------------------------------------------------------------------ SpeakerArrangementTest::SpeakerArrangementTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl, SpeakerArrangement inSpArr, SpeakerArrangement outSpArr) : ProcessTest (plugProvider, sampl), inSpArr (inSpArr), outSpArr (outSpArr) { } //------------------------------------------------------------------------ const char* SpeakerArrangementTest::getSpeakerArrangementName (SpeakerArrangement spArr) { const char* saName = nullptr; switch (spArr) { case SpeakerArr::kMono: saName = "Mono"; break; case SpeakerArr::kStereo: saName = "Stereo"; break; case SpeakerArr::kStereoSurround: saName = "StereoSurround"; break; case SpeakerArr::kStereoCenter: saName = "StereoCenter"; break; case SpeakerArr::kStereoSide: saName = "StereoSide"; break; case SpeakerArr::kStereoCLfe: saName = "StereoCLfe"; break; case SpeakerArr::k30Cine: saName = "30Cine"; break; case SpeakerArr::k30Music: saName = "30Music"; break; case SpeakerArr::k31Cine: saName = "31Cine"; break; case SpeakerArr::k31Music: saName = "31Music"; break; case SpeakerArr::k40Cine: saName = "40Cine"; break; case SpeakerArr::k40Music: saName = "40Music"; break; case SpeakerArr::k41Cine: saName = "41Cine"; break; case SpeakerArr::k41Music: saName = "41Music"; break; case SpeakerArr::k50: saName = "50"; break; case SpeakerArr::k51: saName = "51"; break; case SpeakerArr::k60Cine: saName = "60Cine"; break; case SpeakerArr::k60Music: saName = "60Music"; break; case SpeakerArr::k61Cine: saName = "61Cine"; break; case SpeakerArr::k61Music: saName = "61Music"; break; case SpeakerArr::k70Cine: saName = "70Cine"; break; case SpeakerArr::k70Music: saName = "70Music"; break; case SpeakerArr::k71Cine: saName = "71Cine"; break; case SpeakerArr::k71Music: saName = "71Music"; break; case SpeakerArr::k80Cine: saName = "80Cine"; break; case SpeakerArr::k80Music: saName = "80Music"; break; case SpeakerArr::k81Cine: saName = "81Cine"; break; case SpeakerArr::k81Music: saName = "81Music"; break; case SpeakerArr::k102: saName = "102"; break; case SpeakerArr::k122: saName = "122"; break; case SpeakerArr::k80Cube: saName = "80Cube"; break; case SpeakerArr::k90: saName = "9.0"; break; case SpeakerArr::k91: saName = "9.1"; break; case SpeakerArr::k100: saName = "10.0"; break; case SpeakerArr::k101: saName = "10.1"; break; case SpeakerArr::k110: saName = "11.0"; break; case SpeakerArr::k111: saName = "11.1"; break; case SpeakerArr::k130: saName = "13.0"; break; case SpeakerArr::k131: saName = "13.1"; break; case SpeakerArr::k222: saName = "22.2"; break; case SpeakerArr::kEmpty: saName = "Empty"; break; default: saName = "Unknown"; break; } return saName; } //------------------------------------------------------------------------ const char* SpeakerArrangementTest::getName () const { const auto inSaName = getSpeakerArrangementName (inSpArr); const auto outSaName = getSpeakerArrangementName (outSpArr); if (inSaName && outSaName) { static std::string str; str = "In: "; str += inSaName; str += ": "; str += std::to_string (SpeakerArr::getChannelCount (inSpArr)); str += " Channels, Out: "; str += outSaName; str += ": "; str += std::to_string (SpeakerArr::getChannelCount (outSpArr)); str += " Channels"; return str.data (); } return "error"; } //------------------------------------------------------------------------ bool SpeakerArrangementTest::prepareProcessing () { if (!vstPlug || !audioEffect) return false; bool ret = true; int32 is = vstPlug->getBusCount (kAudio, kInput); auto* inSpArrs = new SpeakerArrangement[is]; for (int32 i = 0; i < is; ++i) inSpArrs[i] = inSpArr; int32 os = vstPlug->getBusCount (kAudio, kOutput); auto* outSpArrs = new SpeakerArrangement[os]; for (int32 o = 0; o < os; o++) outSpArrs[o] = outSpArr; if (audioEffect->setBusArrangements (inSpArrs, is, outSpArrs, os) != kResultTrue) ret = false; // activate only the extra IO (index > 0), the main ones (index 0) were already activated in // TestBase::setup () for (int32 i = 1; i < is; i++) vstPlug->activateBus (kAudio, kInput, i, true); for (int32 i = 1; i < os; i++) vstPlug->activateBus (kAudio, kOutput, i, true); ret &= ProcessTest::prepareProcessing (); delete[] inSpArrs; delete[] outSpArrs; return ret; } //------------------------------------------------------------------------ bool SpeakerArrangementTest::run (ITestResult* testResult) { if (!testResult || !audioEffect || !vstPlug) return false; printTestHeader (testResult); SpeakerArrangement spArr = SpeakerArr::kEmpty; SpeakerArrangement compareSpArr = SpeakerArr::kEmpty; BusDirections bd = kInput; BusInfo busInfo = {}; int32 count = 0; do { count++; int32 numBusses = 0; if (bd == kInput) { numBusses = processData.numInputs; compareSpArr = inSpArr; } else { numBusses = processData.numOutputs; compareSpArr = outSpArr; } for (int32 i = 0; i < numBusses; ++i) { if (audioEffect->getBusArrangement (bd, i, spArr) != kResultTrue) { addErrorMessage (testResult, STR ("IAudioProcessor::getBusArrangement (..) failed.")); return false; } if (spArr != compareSpArr) { addMessage ( testResult, printf (" %s %sSpeakerArrangement is not supported. Plug-in suggests: %s.", getSpeakerArrangementName (compareSpArr), bd == kInput ? "Input-" : "Output-", getSpeakerArrangementName (spArr))); } if (vstPlug->getBusInfo (kAudio, bd, i, busInfo) != kResultTrue) { addErrorMessage (testResult, STR ("IComponent::getBusInfo (..) failed.")); return false; } if (spArr == compareSpArr && SpeakerArr::getChannelCount (spArr) != busInfo.channelCount) { addErrorMessage ( testResult, STR ("SpeakerArrangement mismatch (BusInfo::channelCount inconsistency).")); return false; } } bd = kOutput; } while (count < 2); bool ret = true; // not a Pb ret &= verifySA (processData.numInputs, processData.inputs, inSpArr, testResult); // not a Pb ret &= verifySA (processData.numOutputs, processData.outputs, outSpArr, testResult); ret &= ProcessTest::run (testResult); return ret; } //------------------------------------------------------------------------ bool SpeakerArrangementTest::verifySA (int32 numBusses, AudioBusBuffers* buses, SpeakerArrangement spArr, ITestResult* testResult) { if (!testResult || !buses) return false; for (int32 i = 0; i < numBusses; ++i) { if (buses[i].numChannels != SpeakerArr::getChannelCount (spArr)) { addErrorMessage (testResult, STR ("ChannelCount is not matching SpeakerArrangement.")); return false; } } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/processthreaded.h0000644000000000000000000000013215124701711030110 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/processthreaded.h0000644000175000001440000000320515124701711030100 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/processthreaded.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/process.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ProcessTest //------------------------------------------------------------------------ class ProcessThreadTest : public ProcessTest { public: //------------------------------------------------------------------------ ProcessThreadTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); ~ProcessThreadTest () override; DECLARE_VSTTEST ("Process function running in another thread") bool PLUGIN_API run (ITestResult* testResult) override; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/silenceflags.cpp0000644000000000000000000000013215124701711027723 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/silenceflags.cpp0000644000175000001440000000533115124701711027715 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/silenceflags.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/silenceflags.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // SilenceFlagsTest //------------------------------------------------------------------------ SilenceFlagsTest::SilenceFlagsTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : ProcessTest (plugProvider, sampl) { } //------------------------------------------------------------------------ bool PLUGIN_API SilenceFlagsTest::run (ITestResult* testResult) { if (!vstPlug || !testResult || !audioEffect) return false; if (!canProcessSampleSize (testResult)) return true; printTestHeader (testResult); if (processData.inputs != nullptr) { audioEffect->setProcessing (true); for (int32 inputsIndex = 0; inputsIndex < processData.numInputs; inputsIndex++) { int32 numSilenceFlagsCombinations = (1 << processData.inputs[inputsIndex].numChannels) - 1; for (int32 flagCombination = 0; flagCombination <= numSilenceFlagsCombinations; flagCombination++) { processData.inputs[inputsIndex].silenceFlags = flagCombination; tresult result = audioEffect->process (processData); if (result != kResultOk) { addErrorMessage ( testResult, printf ( "The component failed to process bus %i with silence flag combination %x!", inputsIndex, flagCombination)); audioEffect->setProcessing (false); return false; } } } } else if (processData.numInputs > 0) { addErrorMessage (testResult, STR ("ProcessData::inputs are 0 but ProcessData::numInputs are nonzero.")); return false; } audioEffect->setProcessing (false); return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/silenceprocessing.h0000644000000000000000000000013215124701711030450 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/silenceprocessing.h0000644000175000001440000000311015124701711030433 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/silenceprocessing.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/process.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Silence Processing. * \ingroup TestClass */ class SilenceProcessingTest : public ProcessTest { public: SilenceProcessingTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Silence Processing") protected: bool isBufferSilent (void* buffer, int32 numSamples, ProcessSampleSize sampl); }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/processthreaded.cpp0000644000000000000000000000013215124701711030443 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/processthreaded.cpp0000644000175000001440000000444415124701711030441 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/processthreaded.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/processthreaded.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ProcessTest //------------------------------------------------------------------------ ProcessThreadTest::ProcessThreadTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : ProcessTest (plugProvider, sampl) { } //------------------------------------------------------------------------ ProcessThreadTest::~ProcessThreadTest () { } //------------------------------------------------------------------------ bool ProcessThreadTest::run (ITestResult* testResult) { constexpr auto NUM_ITERATIONS = 9999; if (!vstPlug || !testResult || !audioEffect) return false; if (!canProcessSampleSize (testResult)) return true; printTestHeader (testResult); bool result = false; std::thread processThread ([&] () { result = true; audioEffect->setProcessing (true); for (auto i = 0; i < NUM_ITERATIONS; i++) { tresult tr = audioEffect->process (processData); if (tr != kResultTrue) { result = false; break; } } audioEffect->setProcessing (false); }); processThread.join (); if (!result) testResult->addErrorMessage (STR ("Processing failed.")); return result; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/processinputoverwriti0000644000000000000000000000013215124701711031214 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/processinputoverwriting.h0000644000175000001440000000324115124701711031757 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/processinputoverwriting.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/process.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Input Overwriting * \ingroup TestClass */ class ProcessInputOverwritingTest : public ProcessTest { public: ProcessInputOverwritingTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; bool preProcess (ITestResult* testResult) SMTG_OVERRIDE; bool postProcess (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Process Input Overwriting") private: bool noNeedtoProcess = false; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/process.cpp0000644000000000000000000000013215124701711026742 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/process.cpp0000644000175000001440000002033015124701711026730 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/process.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/process.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ProcessTest //------------------------------------------------------------------------ ProcessTest::ProcessTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : TestEnh (plugProvider, sampl) { processData.numSamples = TestDefaults::instance ().defaultBlockSize; processData.symbolicSampleSize = sampl; processSetup.processMode = kRealtime; processSetup.symbolicSampleSize = sampl; processSetup.maxSamplesPerBlock = TestDefaults::instance ().maxBlockSize; processSetup.sampleRate = TestDefaults::instance ().defaultSampleRate; } //------------------------------------------------------------------------ bool PLUGIN_API ProcessTest::setup () { if (!TestEnh::setup ()) return false; if (!vstPlug || !audioEffect) return false; if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) return false; if (audioEffect->canProcessSampleSize (processSetup.symbolicSampleSize) != kResultOk) return true; // this fails in run (..) prepareProcessing (); if (vstPlug->setActive (true) != kResultTrue) return false; return true; } //------------------------------------------------------------------------ bool PLUGIN_API ProcessTest::run (ITestResult* testResult) { if (!testResult || !audioEffect) return false; if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) return false; if (!canProcessSampleSize (testResult)) return true; audioEffect->setProcessing (true); for (int32 i = 0; i < TestDefaults::instance ().numAudioBlocksToProcess; ++i) { if (!preProcess (testResult)) return false; tresult result = audioEffect->process (processData); if (result != kResultOk) { if (processSetup.symbolicSampleSize == kSample32) addErrorMessage (testResult, STR ("IAudioProcessor::process (..with kSample32..) failed.")); else addErrorMessage (testResult, STR ("IAudioProcessor::process (..with kSample64..) failed.")); audioEffect->setProcessing (false); return false; } if (!postProcess (testResult)) { audioEffect->setProcessing (false); return false; } } audioEffect->setProcessing (false); return true; } //------------------------------------------------------------------------ bool ProcessTest::preProcess (ITestResult* /*testResult*/) { return true; } //------------------------------------------------------------------------ bool ProcessTest::postProcess (ITestResult* /*testResult*/) { return true; } //------------------------------------------------------------------------ bool ProcessTest::canProcessSampleSize (ITestResult* testResult) { if (!testResult || !audioEffect) return false; if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) return false; if (audioEffect->canProcessSampleSize (processSetup.symbolicSampleSize) != kResultOk) { if (processSetup.symbolicSampleSize == kSample32) addMessage (testResult, STR ("32bit Audio Processing not supported.")); else addMessage (testResult, STR ("64bit Audio Processing not supported.")); return false; } return true; } //------------------------------------------------------------------------ bool PLUGIN_API ProcessTest::teardown () { unprepareProcessing (); if (!vstPlug || (vstPlug->setActive (false) != kResultOk)) return false; return TestEnh::teardown (); } //------------------------------------------------------------------------ bool ProcessTest::prepareProcessing () { if (!vstPlug || !audioEffect) return false; if (audioEffect->setupProcessing (processSetup) == kResultOk) { processData.prepare (*vstPlug, 0, processSetup.symbolicSampleSize); for (BusDirection dir = kInput; dir <= kOutput; dir++) { int32 numBusses = vstPlug->getBusCount (kAudio, dir); AudioBusBuffers* audioBuffers = dir == kInput ? processData.inputs : processData.outputs; // new AudioBusBuffers [numBusses]; if (!setupBuffers (numBusses, audioBuffers, dir)) return false; if (dir == kInput) { processData.numInputs = numBusses; processData.inputs = audioBuffers; } else { processData.numOutputs = numBusses; processData.outputs = audioBuffers; } } return true; } return false; } //------------------------------------------------------------------------ bool ProcessTest::setupBuffers (int32 numBusses, AudioBusBuffers* audioBuffers, BusDirection dir) { if (((numBusses > 0) && !audioBuffers) || !vstPlug) return false; for (int32 busIndex = 0; busIndex < numBusses; busIndex++) // buses { BusInfo busInfo {}; if (vstPlug->getBusInfo (kAudio, dir, busIndex, busInfo) == kResultTrue) { if (!setupBuffers (audioBuffers[busIndex])) return false; if ((busInfo.flags & BusInfo::kDefaultActive) != 0) { for (int32 chIdx = 0; chIdx < busInfo.channelCount; chIdx++) // channels per bus audioBuffers[busIndex].silenceFlags |= (TestDefaults::instance ().channelIsSilent << chIdx); } } else return false; } return true; } //------------------------------------------------------------------------ bool ProcessTest::setupBuffers (AudioBusBuffers& audioBuffers) { if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) return false; audioBuffers.silenceFlags = 0; for (int32 chIdx = 0; chIdx < audioBuffers.numChannels; chIdx++) { if (processSetup.symbolicSampleSize == kSample32) { if (audioBuffers.channelBuffers32) { audioBuffers.channelBuffers32[chIdx] = new Sample32[processSetup.maxSamplesPerBlock]; if (audioBuffers.channelBuffers32[chIdx]) memset (audioBuffers.channelBuffers32[chIdx], 0, processSetup.maxSamplesPerBlock * sizeof (Sample32)); else return false; } else return false; } else if (processSetup.symbolicSampleSize == kSample64) { if (audioBuffers.channelBuffers64) { audioBuffers.channelBuffers64[chIdx] = new Sample64[processSetup.maxSamplesPerBlock]; if (audioBuffers.channelBuffers64[chIdx]) memset (audioBuffers.channelBuffers64[chIdx], 0, processSetup.maxSamplesPerBlock * sizeof (Sample64)); else return false; } else return false; } else return false; } return true; } //------------------------------------------------------------------------ bool ProcessTest::unprepareProcessing () { bool ret = true; ret &= freeBuffers (processData.numInputs, processData.inputs); ret &= freeBuffers (processData.numOutputs, processData.outputs); processData.unprepare (); return ret; } //------------------------------------------------------------------------ bool ProcessTest::freeBuffers (int32 numBuses, AudioBusBuffers* buses) { if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) return false; for (int32 busIndex = 0; busIndex < numBuses; busIndex++) { for (int32 chIdx = 0; chIdx < buses[busIndex].numChannels; chIdx++) { if (processSetup.symbolicSampleSize == kSample32) delete[] buses[busIndex].channelBuffers32[chIdx]; else if (processSetup.symbolicSampleSize == kSample64) delete[] buses[busIndex].channelBuffers64[chIdx]; else return false; } } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/variableblocksize.cpp0000644000000000000000000000013215124701711030757 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/variableblocksize.cpp0000644000175000001440000000522315124701711030751 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/variableblocksize.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/variableblocksize.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // VariableBlockSizeTest //------------------------------------------------------------------------ VariableBlockSizeTest::VariableBlockSizeTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : ProcessTest (plugProvider, sampl) { } //------------------------------------------------------------------------ bool PLUGIN_API VariableBlockSizeTest::run (ITestResult* testResult) { if (!vstPlug || !testResult || !audioEffect) return false; if (!canProcessSampleSize (testResult)) return true; printTestHeader (testResult); audioEffect->setProcessing (true); for (int32 i = 0; i <= TestDefaults::instance ().numIterations; ++i) { int32 sampleFrames = rand () % processSetup.maxSamplesPerBlock; processData.numSamples = sampleFrames; if (i == 0) processData.numSamples = 0; #if defined(TOUGHTESTS) && TOUGHTESTS else if (i == 1) processData.numSamples = -50000; else if (i == 2) processData.numSamples = processSetup.maxSamplesPerBlock * 2; #endif // TOUGHTESTS tresult result = audioEffect->process (processData); if ((result != kResultOk) #if defined(TOUGHTESTS) && TOUGHTESTS && (i > 1) #else && (i > 0) #endif // TOUGHTESTS ) { addErrorMessage ( testResult, printf ("The component failed to process an audioblock of size %i", sampleFrames)); audioEffect->setProcessing (false); return false; } } audioEffect->setProcessing (false); return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/process.h0000644000000000000000000000013215124701711026407 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/process.h0000644000175000001440000000450315124701711026401 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/process.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/hosting/processdata.h" #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Process Test. * \ingroup TestClass */ class ProcessTest : public TestEnh { public: ProcessTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); DECLARE_VSTTEST ("Process Test") // ITest bool PLUGIN_API setup () SMTG_OVERRIDE; bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; bool PLUGIN_API teardown () SMTG_OVERRIDE; //------------------------------------------------------------------------ protected: virtual bool prepareProcessing (); ///< setup ProcessData and allocate buffers virtual bool unprepareProcessing (); ///< free dynamic memory of ProcessData virtual bool preProcess (ITestResult* testResult); ///< is called just before the process call virtual bool postProcess (ITestResult* testResult); ///< is called right after the process call bool setupBuffers (int32 numBusses, AudioBusBuffers* audioBuffers, BusDirection dir); bool setupBuffers (AudioBusBuffers& audioBuffers); bool freeBuffers (int32 numBuses, AudioBusBuffers* buses); bool canProcessSampleSize (ITestResult* testResult); ///< audioEffect has to be available HostProcessData processData; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/automation.cpp0000644000000000000000000000013215124701711027444 xustar0030 mtime=1767080905.284343858 30 atime=1767080905.284343858 30 ctime=1767080905.284343858 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/automation.cpp0000644000175000001440000002237315124701711027443 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/automation.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/automation.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // AutomationTest //------------------------------------------------------------------------ //------------------------------------------------------------------------ AutomationTest::AutomationTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl, int32 everyNSamples, int32 numParams, bool sampleAccuracy) : ProcessTest (plugProvider, sampl) , bypassId (kNoParamId) , countParamChanges (0) , everyNSamples (everyNSamples) , numParams (numParams) , sampleAccuracy (sampleAccuracy) , onceExecuted (false) { } //------------------------------------------------------------------------ AutomationTest::~AutomationTest () { } //------------------------------------------------------------------------ tresult PLUGIN_API AutomationTest::queryInterface (const TUID _iid, void** obj) { QUERY_INTERFACE (_iid, obj, FUnknown::iid, IParameterChanges); QUERY_INTERFACE (_iid, obj, Vst::IParameterChanges::iid, IParameterChanges); return ProcessTest::queryInterface (_iid, obj); } //------------------------------------------------------------------------ const char* AutomationTest::getName () const { static std::string text; const char* accTxt = "Sample"; if (!sampleAccuracy) accTxt = "Block"; text = "Accuracy: "; text += accTxt; if (numParams < 1) text += ", All Parameters"; else { text += ", "; text += std::to_string (numParams); text += " Parameters"; } text += ", Change every"; text += std::to_string (everyNSamples); text += " Samples"; return text.data (); } //------------------------------------------------------------------------ bool AutomationTest::setup () { onceExecuted = false; if (!ProcessTest::setup ()) return false; if (!controller) return false; if ((numParams < 1) || (numParams > controller->getParameterCount ())) numParams = controller->getParameterCount (); if (audioEffect && (numParams > 0)) { ParameterInfo inf = {}; for (int32 i = 0; i < numParams; ++i) { paramChanges.push_back (owned (new ParamChanges)); tresult r = controller->getParameterInfo (i, inf); if (r != kResultTrue) return false; if ((inf.flags & inf.kCanAutomate) != 0) paramChanges[i]->init (inf.id, processSetup.maxSamplesPerBlock); } for (int32 i = 0; i < controller->getParameterCount (); ++i) { tresult r = controller->getParameterInfo (i, inf); if (r != kResultTrue) return false; if ((inf.flags & inf.kIsBypass) != 0) { bypassId = inf.id; break; } } return true; } return numParams == 0; } //------------------------------------------------------------------------ bool AutomationTest::run (ITestResult* testResult) { if (!testResult) return false; printTestHeader (testResult); if (numParams == 0) addMessage (testResult, STR ("No Parameters present.")); bool ret = ProcessTest::run (testResult); return ret; } //------------------------------------------------------------------------ bool AutomationTest::teardown () { paramChanges.clear (); return ProcessTest::teardown (); } //------------------------------------------------------------------------ bool AutomationTest::preProcess (ITestResult* testResult) { if (!testResult) return false; if (paramChanges.empty ()) return numParams == 0; bool check = true; for (int32 i = 0; i < numParams; ++i) { paramChanges[i]->resetPoints (); int32 point = 0; for (int32 pos = 0; pos < processData.numSamples; pos++) { bool add = (rand () % everyNSamples) == 0; if (!onceExecuted) { if (pos == 0) { add = true; if (!sampleAccuracy) onceExecuted = true; } else if ((pos == 1) && sampleAccuracy) { add = true; onceExecuted = true; } } if (add) check &= paramChanges[i]->setPoint (point++, pos, ((float)(rand () % 1000000000)) / 1000000000.0); } if (check) processData.inputParameterChanges = this; } return check; } //------------------------------------------------------------------------ bool AutomationTest::postProcess (ITestResult* testResult) { if (!testResult) return false; if (paramChanges.empty ()) return numParams == 0; for (int32 i = 0; i < numParams; ++i) { if ((paramChanges[i]->getPointCount () > 0) && !paramChanges[i]->havePointsBeenRead (!sampleAccuracy)) { if (sampleAccuracy) addMessage (testResult, STR (" Not all points have been read via IParameterChanges")); else addMessage (testResult, STR (" No point at all has been read via IParameterChanges")); return true; // should not be a problem } } return true; } //------------------------------------------------------------------------ int32 AutomationTest::getParameterCount () { if (paramChanges.empty ()) return numParams; return static_cast (paramChanges.size ()); } //------------------------------------------------------------------------ IParamValueQueue* AutomationTest::getParameterData (int32 index) { if ((index >= 0) && (index < getParameterCount ())) return paramChanges[index]; return nullptr; } //------------------------------------------------------------------------ IParamValueQueue* AutomationTest::addParameterData (const ParamID& /*id*/, int32& /*index*/) { return nullptr; } //------------------------------------------------------------------------ // FlushParamTest //------------------------------------------------------------------------ FlushParamTest::FlushParamTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : AutomationTest (plugProvider, sampl, 100, 1, false) { } //------------------------------------------------------------------------ void FlushParamTest::prepareProcessData () { processData.numSamples = 0; processData.numInputs = 0; processData.numOutputs = 0; processData.inputs = nullptr; processData.outputs = nullptr; } //------------------------------------------------------------------------ bool PLUGIN_API FlushParamTest::run (ITestResult* testResult) { if (!vstPlug || !testResult || !audioEffect) return false; if (!canProcessSampleSize (testResult)) return true; printTestHeader (testResult); unprepareProcessing (); prepareProcessData (); audioEffect->setProcessing (true); preProcess (testResult); tresult result = audioEffect->process (processData); if (result != kResultOk) { addErrorMessage (testResult, STR ("The component failed to process without audio buffers!")); audioEffect->setProcessing (false); return false; } postProcess (testResult); audioEffect->setProcessing (false); return true; } //------------------------------------------------------------------------ // FlushParamTest2 //------------------------------------------------------------------------ FlushParamTest2::FlushParamTest2 (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : FlushParamTest (plugProvider, sampl) { } //------------------------------------------------------------------------ void FlushParamTest2::prepareProcessData () { prepareProcessing (); processData.numSamples = 0; // remember original processData config std::swap (numInputs, processData.numInputs); std::swap (numOutputs, processData.numOutputs); if (processData.inputs) std::swap (numChannelsIn, processData.inputs[0].numChannels); if (processData.outputs) std::swap (numChannelsOut, processData.outputs[0].numChannels); } //------------------------------------------------------------------------ bool FlushParamTest2::teardown () { // restore original processData config for correct deallocation std::swap (numInputs, processData.numInputs); std::swap (numOutputs, processData.numOutputs); if (processData.inputs) std::swap (numChannelsIn, processData.inputs[0].numChannels); if (processData.outputs) std::swap (numChannelsOut, processData.outputs[0].numChannels); return FlushParamTest::teardown (); } //------------------------------------------------------------------------ // FlushParamTest3 //------------------------------------------------------------------------ FlushParamTest3::FlushParamTest3 (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : FlushParamTest (plugProvider, sampl) { paramChanges.clear (); } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/silenceflags.h0000644000000000000000000000013215124701711027370 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/silenceflags.h0000644000175000001440000000272415124701711027365 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/silenceflags.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/process.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Silence Flags. * \ingroup TestClass */ class SilenceFlagsTest : public ProcessTest { public: SilenceFlagsTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Silence Flags") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/processformat.h0000644000000000000000000000013215124701711027620 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/processformat.h0000644000175000001440000000273115124701711027613 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/processformat.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/process.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Process Format. * \ingroup TestClass */ class ProcessFormatTest : public ProcessTest { public: ProcessFormatTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Process Format") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/silenceprocessing.cpp0000644000000000000000000000013215124701711031003 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/silenceprocessing.cpp0000644000175000001440000001270715124701711031002 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/silenceprocessing.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/silenceprocessing.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // SilenceProcessingTest //------------------------------------------------------------------------ SilenceProcessingTest::SilenceProcessingTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : ProcessTest (plugProvider, sampl) { } //------------------------------------------------------------------------ bool SilenceProcessingTest::isBufferSilent (void* buffer, int32 numSamples, ProcessSampleSize sampl) { if (sampl == kSample32) { const float kSilenceThreshold = 0.000132184039f; float* floatBuffer = (float*)buffer; while (numSamples--) { if (fabsf (*floatBuffer) > kSilenceThreshold) return false; floatBuffer++; } } else if (sampl == kSample64) { const double kSilenceThreshold = 0.000132184039; double* floatBuffer = (double*)buffer; while (numSamples--) { if (fabs (*floatBuffer) > kSilenceThreshold) return false; floatBuffer++; } } return true; } //------------------------------------------------------------------------ bool PLUGIN_API SilenceProcessingTest::run (ITestResult* testResult) { if (!vstPlug || !testResult || !audioEffect) return false; if (!canProcessSampleSize (testResult)) return true; printTestHeader (testResult); if (processData.inputs != nullptr) { // process 20s before checking flags int32 numPasses = int32 (20 * processSetup.sampleRate / processData.numSamples + 0.5); audioEffect->setProcessing (true); for (int32 pass = 0; pass < numPasses; pass++) { for (int32 busIndex = 0; busIndex < processData.numInputs; busIndex++) { processData.inputs[busIndex].silenceFlags = 0; for (int32 channelIndex = 0; channelIndex < processData.inputs[busIndex].numChannels; channelIndex++) { processData.inputs[busIndex].silenceFlags |= (uint64)1 << (uint64)channelIndex; if (processData.symbolicSampleSize == kSample32) memset (processData.inputs[busIndex].channelBuffers32[channelIndex], 0, sizeof (float) * processData.numSamples); else if (processData.symbolicSampleSize == kSample64) memset (processData.inputs[busIndex].channelBuffers32[channelIndex], 0, sizeof (double) * processData.numSamples); } } for (int32 busIndex = 0; busIndex < processData.numOutputs; busIndex++) { if (processData.numInputs > busIndex) processData.outputs[busIndex].silenceFlags = processData.inputs[busIndex].silenceFlags; else { processData.outputs[busIndex].silenceFlags = 0; for (int32 channelIndex = 0; channelIndex < processData.outputs[busIndex].numChannels; channelIndex++) processData.outputs[busIndex].silenceFlags |= (uint64)1 << (uint64)channelIndex; } } tresult result = audioEffect->process (processData); if (result != kResultOk) { addErrorMessage (testResult, printf ("%s", "The component failed to process!")); audioEffect->setProcessing (false); return false; } } for (int32 busIndex = 0; busIndex < processData.numOutputs; busIndex++) { for (int32 channelIndex = 0; channelIndex < processData.outputs[busIndex].numChannels; channelIndex++) { bool channelShouldBeSilent = (processData.outputs[busIndex].silenceFlags & (uint64)1 << (uint64)channelIndex) != 0; bool channelIsSilent = isBufferSilent (processData.outputs[busIndex].channelBuffers32[channelIndex], processData.numSamples, processData.symbolicSampleSize); if (channelShouldBeSilent != channelIsSilent) { constexpr auto silentText = STR ( "The component reported a wrong silent flag for its output buffer! : output is silent but silenceFlags not set !"); constexpr auto nonSilentText = STR ( "The component reported a wrong silent flag for its output buffer! : silenceFlags is set to silence but output is not silent"); addMessage (testResult, channelIsSilent ? silentText : nonSilentText); break; } } } } else if (processData.numInputs > 0) { addErrorMessage (testResult, STR ("ProcessData::inputs are 0 but ProcessData::numInputs are nonzero.")); return false; } audioEffect->setProcessing (false); return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/processtail.h0000644000000000000000000000013215124701711027261 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/processtail.h0000644000175000001440000000357315124701711027261 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/processtail.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/process.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test ProcesTail. * \ingroup TestClass */ class ProcessTailTest : public ProcessTest { public: ProcessTailTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); ~ProcessTailTest () override; DECLARE_VSTTEST ("Check Tail processing") // ITest bool PLUGIN_API setup () SMTG_OVERRIDE; bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; bool preProcess (ITestResult* testResult) SMTG_OVERRIDE; bool postProcess (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ protected: private: uint32 mTailSamples; uint32 mInTail; float* dataPtrFloat; double* dataPtrDouble; bool mInSilenceInput; bool mDontTest; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/speakerarrangement.h0000644000000000000000000000013215124701711030607 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/speakerarrangement.h0000644000175000001440000000373715124701711030611 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/speakerarrangement.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/process.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Speaker Arrangement. * \ingroup TestClass */ class SpeakerArrangementTest : public ProcessTest { public: SpeakerArrangementTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl, SpeakerArrangement inSpArr, SpeakerArrangement outSpArr); const char* getName () const SMTG_OVERRIDE; static const char* getSpeakerArrangementName (SpeakerArrangement spArr); // ITest bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ protected: bool prepareProcessing () SMTG_OVERRIDE; bool verifySA (int32 numBusses, AudioBusBuffers* buses, SpeakerArrangement spArr, ITestResult* testResult); private: SpeakerArrangement inSpArr; SpeakerArrangement outSpArr; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/processcontextrequire0000644000000000000000000000013215124701711031163 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/processcontextrequirements.cpp0000644000175000001440000001263215124701711033007 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/processcontextrequirements.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/processcontextrequirements.h" #include "public.sdk/source/vst/hosting/module.h" #include "public.sdk/source/vst/utility/processcontextrequirements.h" #include "public.sdk/source/vst/utility/versionparser.h" #include "pluginterfaces/base/funknownimpl.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace { //------------------------------------------------------------------------ VST3::Optional getPluginSDKVersion (ITestPlugProvider* plugProvider, ITestResult* testResult) { auto pp2 = U::cast (plugProvider); if (!pp2) { addErrorMessage (testResult, STR ("Internal test Error. Expected Interface not there!")); return {}; } VST3::Hosting::PluginFactory pluginFactory (pp2->getPluginFactory ()); if (!pluginFactory.get ()) { addErrorMessage (testResult, STR ("Internal test Error. Expected PluginFactory not there!")); return {}; } FUID fuid; if (pp2->getComponentUID (fuid) != kResultTrue) { addErrorMessage (testResult, STR ("Internal test Error. Could not query the UID of the plug-in!")); return {}; } auto plugClassID = VST3::UID::fromTUID (fuid.toTUID ()); auto classInfos = pluginFactory.classInfos (); auto it = std::find_if (classInfos.begin (), classInfos.end (), [&] (const auto& element) { return element.ID () == plugClassID; }); if (it == classInfos.end ()) { addErrorMessage ( testResult, STR ("Internal test Error. Could not find the class info of the plug-in!")); return {}; } return VST3::Version::parse (it->sdkVersion ()); } //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ // ProcessContextRequirementsTest //------------------------------------------------------------------------ ProcessContextRequirementsTest::ProcessContextRequirementsTest (ITestPlugProvider* plugProvider) : TestEnh (plugProvider, kSample32) { } //------------------------------------------------------------------------ bool PLUGIN_API ProcessContextRequirementsTest::setup () { return TestEnh::setup (); } //------------------------------------------------------------------------ bool PLUGIN_API ProcessContextRequirementsTest::run (ITestResult* testResult) { if (!vstPlug || !testResult || !audioEffect) return false; printTestHeader (testResult); // check if plug-in is build with any earlier VST SDK which does not support this interface auto sdkVersion = getPluginSDKVersion (plugProvider, testResult); if (!sdkVersion) return false; if (sdkVersion->getMajor () < 3 || (sdkVersion->getMajor () == 3 && sdkVersion->getMinor () < 7)) { addMessage (testResult, STR ("No ProcessContextRequirements required. Plug-In built with older SDK.")); return true; } if (auto contextRequirements = U::cast (audioEffect)) { ProcessContextRequirements req (contextRequirements->getProcessContextRequirements ()); addMessage (testResult, STR ("ProcessContextRequirements:")); if (req.wantsNone ()) addMessage (testResult, STR (" - None")); else { if (req.wantsSystemTime ()) addMessage (testResult, STR (" - SystemTime")); if (req.wantsContinousTimeSamples ()) addMessage (testResult, STR (" - ContinousTimeSamples")); if (req.wantsProjectTimeMusic ()) addMessage (testResult, STR (" - ProjectTimeMusic")); if (req.wantsBarPositionMusic ()) addMessage (testResult, STR (" - BarPosititionMusic")); if (req.wantsCycleMusic ()) addMessage (testResult, STR (" - CycleMusic")); if (req.wantsSamplesToNextClock ()) addMessage (testResult, STR (" - SamplesToNextClock")); if (req.wantsTempo ()) addMessage (testResult, STR (" - Tempo")); if (req.wantsTimeSignature ()) addMessage (testResult, STR (" - TimeSignature")); if (req.wantsChord ()) addMessage (testResult, STR (" - Chord")); if (req.wantsFrameRate ()) addMessage (testResult, STR (" - FrameRate")); if (req.wantsTransportState ()) addMessage (testResult, STR (" - TransportState")); } return true; } addMessage (testResult, STR ("Since VST SDK 3.7 you need to implement IProcessContextRequirements!")); addErrorMessage (testResult, STR ("Missing mandatory IProcessContextRequirements extension!")); return false; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/processtail.cpp0000644000000000000000000000013215124701711027614 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/processtail.cpp0000644000175000001440000001623715124701711027615 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/processtail.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/processtail.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ProcessTailTest //------------------------------------------------------------------------ ProcessTailTest::ProcessTailTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : ProcessTest (plugProvider, sampl) , mTailSamples (0) , mInTail (0) , dataPtrFloat (nullptr) , dataPtrDouble (nullptr) , mInSilenceInput (false) , mDontTest (false) {FUNKNOWN_CTOR} //------------------------------------------------------------------------ ProcessTailTest::~ProcessTailTest () { if (dataPtrFloat) { delete[] dataPtrFloat; dataPtrFloat = nullptr; } if (dataPtrDouble) { delete[] dataPtrDouble; dataPtrDouble = nullptr; } } //------------------------------------------------------------------------ bool PLUGIN_API ProcessTailTest::setup () { bool result = ProcessTest::setup (); if (result) { mTailSamples = audioEffect->getTailSamples (); StringResult subCat; plugProvider->getSubCategories (subCat); if (subCat.get ().find ("Generator") != std::string::npos || subCat.get ().find ("Instrument") != std::string::npos) { mDontTest = true; } } return result; } //------------------------------------------------------------------------ bool ProcessTailTest::preProcess (ITestResult* /*testResult*/) { if (!mInSilenceInput) { if (processSetup.symbolicSampleSize == kSample32) { if (!dataPtrFloat) dataPtrFloat = new float[processData.numSamples]; float* ptr = dataPtrFloat; for (int32 i = 0; i < processData.numSamples; ++i) ptr[i] = (float)(2 * rand () / 32767.0 - 1); } else { if (!dataPtrDouble) dataPtrDouble = new double[processData.numSamples]; double* ptr = (double*)dataPtrDouble; for (int32 i = 0; i < processData.numSamples; ++i) ptr[i] = (double)(2 * rand () / 32767.0 - 1); } for (int32 i = 0; i < processData.numOutputs; ++i) { for (int32 c = 0; c < processData.outputs->numChannels; ++c) { if (processSetup.symbolicSampleSize == kSample32) memset (processData.outputs->channelBuffers32[c], 0, processData.numSamples * sizeof (float)); else memset (processData.outputs->channelBuffers64[c], 0, processData.numSamples * sizeof (double)); } } for (int32 i = 0; i < processData.numInputs; ++i) { for (int32 c = 0; c < processData.inputs->numChannels; ++c) { if (processSetup.symbolicSampleSize == kSample32) memcpy (processData.inputs->channelBuffers32[c], dataPtrFloat, processData.numSamples * sizeof (float)); else memcpy (processData.inputs->channelBuffers64[c], dataPtrDouble, processData.numSamples * sizeof (double)); } } } else { // process with silent buffers for (int32 i = 0; i < processData.numOutputs; ++i) { for (int32 c = 0; c < processData.outputs->numChannels; ++c) { if (processSetup.symbolicSampleSize == kSample32) memset (processData.outputs->channelBuffers32[c], 0, processData.numSamples * sizeof (float)); else memset (processData.outputs->channelBuffers64[c], 0, processData.numSamples * sizeof (double)); } } for (int32 i = 0; i < processData.numInputs; ++i) { for (int32 c = 0; c < processData.inputs->numChannels; ++c) { if (processSetup.symbolicSampleSize == kSample32) memset (processData.inputs->channelBuffers32[c], 0, processData.numSamples * sizeof (float)); else memset (processData.inputs->channelBuffers64[c], 0, processData.numSamples * sizeof (double)); } } } return true; } //------------------------------------------------------------------------ bool ProcessTailTest::postProcess (ITestResult* testResult) { if (mInSilenceInput) { // should be silence if (mTailSamples < mInTail + processData.numSamples) { int32 start = mTailSamples > mInTail ? mTailSamples - mInTail : 0; int32 end = processData.numSamples; for (int32 i = 0; i < processData.numOutputs; ++i) { for (int32 c = 0; c < processData.outputs->numChannels; ++c) { if (processSetup.symbolicSampleSize == kSample32) { for (int32 s = start; s < end; ++s) { if (fabsf (processData.outputs->channelBuffers32[c][s]) >= 1e-7) { addErrorMessage ( testResult, printf ( "IAudioProcessor::process (..) generates non silent output for silent input for tail above %d samples.", mTailSamples)); return false; } } } else { for (int32 s = start; s < end; ++s) { if (fabs (processData.outputs->channelBuffers64[c][s]) >= 1e-7) { addErrorMessage ( testResult, printf ( "IAudioProcessor::process (..) generates non silent output for silent input for tail above %d samples.", mTailSamples)); return false; } } } } } } mInTail += processData.numSamples; } return true; } //------------------------------------------------------------------------ bool PLUGIN_API ProcessTailTest::run (ITestResult* testResult) { if (!testResult || !audioEffect) return false; if (processSetup.symbolicSampleSize != processData.symbolicSampleSize) return false; if (!canProcessSampleSize (testResult)) return true; if (mDontTest) return true; addMessage (testResult, printf ("===%s == Tail=%d ======================", getName (), mTailSamples)); audioEffect->setProcessing (true); // process with signal (noise) and silence for (int32 i = 0; i < 20 * TestDefaults::instance ().numAudioBlocksToProcess; ++i) { mInSilenceInput = i > 10; if (!preProcess (testResult)) return false; tresult result = audioEffect->process (processData); if (result != kResultOk) { addErrorMessage (testResult, STR ("IAudioProcessor::process (..) failed.")); audioEffect->setProcessing (false); return false; } if (!postProcess (testResult)) { audioEffect->setProcessing (false); return false; } } audioEffect->setProcessing (false); return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/PaxHeaders/processformat.cpp0000644000000000000000000000013215124701711030153 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/processing/processformat.cpp0000644000175000001440000000677315124701711030160 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/processing/processformat.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/processing/processformat.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ProcessFormatTest //------------------------------------------------------------------------ ProcessFormatTest::ProcessFormatTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : ProcessTest (plugProvider, sampl) { } //------------------------------------------------------------------------ bool PLUGIN_API ProcessFormatTest::run (ITestResult* testResult) { if (!vstPlug || !testResult || !audioEffect) return false; if (!canProcessSampleSize (testResult)) return true; printTestHeader (testResult); int32 numFails = 0; const int32 numRates = 12; SampleRate sampleRateFormats[numRates] = {22050., 32000., 44100., 48000., 88200., 96000., 192000., 384000., 1234.5678, 12345.678, 123456.78, 1234567.8}; tresult result = vstPlug->setActive (false); if (result != kResultOk) { addErrorMessage (testResult, STR ("IComponent::setActive (false) failed.")); return false; } addMessage (testResult, STR ("***Tested Sample Rates***")); for (int32 i = 0; i < numRates; ++i) { processSetup.sampleRate = sampleRateFormats[i]; result = audioEffect->setupProcessing (processSetup); if (result == kResultOk) { result = vstPlug->setActive (true); if (result != kResultOk) { addErrorMessage (testResult, STR ("IComponent::setActive (true) failed.")); return false; } audioEffect->setProcessing (true); result = audioEffect->process (processData); audioEffect->setProcessing (false); if (result == kResultOk) { addMessage (testResult, printf (" %10.10G Hz - processed successfully!", sampleRateFormats[i])); } else { numFails++; addErrorMessage (testResult, printf (" %10.10G Hz - failed to process!", sampleRateFormats[i])); } result = vstPlug->setActive (false); if (result != kResultOk) { addErrorMessage (testResult, STR ("IComponent::setActive (false) failed.")); return false; } } else if (sampleRateFormats[i] > 0.) { addErrorMessage ( testResult, printf ("IAudioProcessor::setupProcessing (..) failed for samplerate %.3f Hz! ", sampleRateFormats[i])); // return false; } } result = vstPlug->setActive (true); if (result != kResultOk) return false; return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/vsttestsuite.h0000644000000000000000000000013215124701711025343 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.285285125 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/vsttestsuite.h0000644000175000001440000000602415124701711025335 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/vsttestsuite.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/bus/busactivation.h" #include "public.sdk/source/vst/testsuite/bus/busconsistency.h" #include "public.sdk/source/vst/testsuite/bus/businvalidindex.h" #include "public.sdk/source/vst/testsuite/bus/checkaudiobusarrangement.h" #include "public.sdk/source/vst/testsuite/bus/scanbusses.h" #include "public.sdk/source/vst/testsuite/bus/sidechainarrangement.h" #include "public.sdk/source/vst/testsuite/general/editorclasses.h" #include "public.sdk/source/vst/testsuite/general/midilearn.h" #include "public.sdk/source/vst/testsuite/general/midimapping.h" #include "public.sdk/source/vst/testsuite/general/parameterfunctionname.h" #include "public.sdk/source/vst/testsuite/general/scanparameters.h" #include "public.sdk/source/vst/testsuite/general/suspendresume.h" #include "public.sdk/source/vst/testsuite/general/terminit.h" #include "public.sdk/source/vst/testsuite/noteexpression/keyswitch.h" #include "public.sdk/source/vst/testsuite/noteexpression/noteexpression.h" #include "public.sdk/source/vst/testsuite/processing/automation.h" #include "public.sdk/source/vst/testsuite/processing/process.h" #include "public.sdk/source/vst/testsuite/processing/processcontextrequirements.h" #include "public.sdk/source/vst/testsuite/processing/processformat.h" #include "public.sdk/source/vst/testsuite/processing/processinputoverwriting.h" #include "public.sdk/source/vst/testsuite/processing/processtail.h" #include "public.sdk/source/vst/testsuite/processing/processthreaded.h" #include "public.sdk/source/vst/testsuite/processing/silenceflags.h" #include "public.sdk/source/vst/testsuite/processing/silenceprocessing.h" #include "public.sdk/source/vst/testsuite/processing/speakerarrangement.h" #include "public.sdk/source/vst/testsuite/processing/variableblocksize.h" #include "public.sdk/source/vst/testsuite/state/bypasspersistence.h" #include "public.sdk/source/vst/testsuite/state/invalidstatetransition.h" #include "public.sdk/source/vst/testsuite/state/repeatidenticalstatetransition.h" #include "public.sdk/source/vst/testsuite/state/validstatetransition.h" #include "public.sdk/source/vst/testsuite/unit/checkunitstructure.h" #include "public.sdk/source/vst/testsuite/unit/scanprograms.h" #include "public.sdk/source/vst/testsuite/unit/scanunits.h" qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/noteexpression0000644000000000000000000000013115124701711025413 xustar0030 mtime=1767080905.284343858 29 atime=1767080905.28382083 30 ctime=1767080905.284343858 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/noteexpression/0000755000175000001440000000000015124701711025461 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/noteexpression/PaxHeaders/noteexpression.h0000644000000000000000000000013215124701711030727 xustar0030 mtime=1767080905.284343858 30 atime=1767080905.284343858 30 ctime=1767080905.284343858 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/noteexpression/noteexpression.h0000644000175000001440000000312215124701711030715 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/noteexpression/noteexpression.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Note Expression. * \ingroup TestClass */ class NoteExpressionTest : public TestBase { public: //------------------------------------------------------------------------ NoteExpressionTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("Note Expression") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/noteexpression/PaxHeaders/keyswitch.cpp0000644000000000000000000000012715124701711030213 xustar0029 mtime=1767080905.28382083 29 atime=1767080905.28382083 29 ctime=1767080905.28382083 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/noteexpression/keyswitch.cpp0000644000175000001440000000545115124701711030204 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/noteexpression/keyswitch.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/noteexpression/keyswitch.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/vst/ivstnoteexpression.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // KeyswitchTest //------------------------------------------------------------------------ KeyswitchTest::KeyswitchTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API KeyswitchTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); if (!controller) { addMessage (testResult, STR ("No Edit Controller supplied!")); return true; } auto keyswitch = U::cast (controller); if (!keyswitch) { addMessage (testResult, STR ("No Keyswitch interface supplied!")); return true; } int32 eventBusCount = vstPlug->getBusCount (kEvent, kInput); for (int32 bus = 0; bus < eventBusCount; bus++) { BusInfo busInfo; vstPlug->getBusInfo (kEvent, kInput, bus, busInfo); for (int16 channel = 0; channel < busInfo.channelCount; channel++) { int32 count = keyswitch->getKeyswitchCount (bus, channel); if (count > 0) { addMessage (testResult, printf ("Keyswitch support bus[%d], channel[%d]: %d", bus, channel, count)); } for (int32 i = 0; i < count; ++i) { KeyswitchInfo info; if (keyswitch->getKeyswitchInfo (bus, channel, i, info) == kResultTrue) { } else { addErrorMessage ( testResult, printf ("Keyswitch getKeyswitchInfo (%d, %d, %d) return kResultFalse!", bus, channel, i)); return false; } } } } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/noteexpression/PaxHeaders/keyswitch.h0000644000000000000000000000013115124701711027653 xustar0030 mtime=1767080905.284343858 29 atime=1767080905.28382083 30 ctime=1767080905.284343858 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/noteexpression/keyswitch.h0000644000175000001440000000306715124701711027652 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/noteexpression/keyswitch.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Keyswitch. * \ingroup TestClass */ class KeyswitchTest : public TestBase { public: //------------------------------------------------------------------------ KeyswitchTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("Keyswitch") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/noteexpression/PaxHeaders/noteexpression.cp0000644000000000000000000000013215124701711031102 xustar0030 mtime=1767080905.284343858 30 atime=1767080905.284343858 30 ctime=1767080905.284343858 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/noteexpression/noteexpression.cpp0000644000175000001440000001223015124701711031250 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/noteexpression/noteexpression.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/noteexpression/noteexpression.h" #include "public.sdk/source/vst/utility/stringconvert.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/vst/ivstnoteexpression.h" #include "pluginterfaces/vst/ivstphysicalui.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // NoteExpressionTest //------------------------------------------------------------------------ NoteExpressionTest::NoteExpressionTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool PLUGIN_API NoteExpressionTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); if (!controller) { addMessage (testResult, STR ("No Edit Controller supplied!")); return true; } auto noteExpression = U::cast (controller); if (!noteExpression) { addMessage (testResult, STR ("No Note Expression interface supplied!")); return true; } auto noteExpressionPUIMapping = U::cast (controller); if (!noteExpressionPUIMapping) { addMessage (testResult, STR ("No Note Expression PhysicalUIMapping interface supplied!")); } int32 eventBusCount = vstPlug->getBusCount (kEvent, kInput); const uint32 maxPUI = kPUITypeCount; PhysicalUIMap puiArray[maxPUI]; PhysicalUIMapList puiMap; puiMap.count = maxPUI; puiMap.map = puiArray; for (uint32 i = 0; i < maxPUI; i++) { puiMap.map[i].physicalUITypeID = static_cast (i); } for (int32 bus = 0; bus < eventBusCount; bus++) { BusInfo busInfo; vstPlug->getBusInfo (kEvent, kInput, bus, busInfo); for (int16 channel = 0; channel < busInfo.channelCount; channel++) { int32 count = noteExpression->getNoteExpressionCount (bus, channel); if (count > 0) { addMessage (testResult, printf ("Note Expression count bus[%d], channel[%d]: %d", bus, channel, count)); } for (int32 i = 0; i < count; ++i) { NoteExpressionTypeInfo info; if (noteExpression->getNoteExpressionInfo (bus, channel, i, info) == kResultTrue) { addMessage (testResult, printf ("Note Expression TypeID: %d [%s]", info.typeId, StringConvert::convert (info.title).data ())); NoteExpressionTypeID id = info.typeId; NoteExpressionValue valueNormalized = info.valueDesc.defaultValue; String128 string; if (noteExpression->getNoteExpressionStringByValue ( bus, channel, id, valueNormalized, string) != kResultTrue) { addMessage ( testResult, printf ( "Note Expression getNoteExpressionStringByValue (%d, %d, %d) return kResultFalse!", bus, channel, id)); } if (noteExpression->getNoteExpressionValueByString ( bus, channel, id, string, valueNormalized) != kResultTrue) { addMessage ( testResult, printf ( "Note Expression getNoteExpressionValueByString (%d, %d, %d) return kResultFalse!", bus, channel, id)); } } else { addErrorMessage ( testResult, printf ( "Note Expression getNoteExpressionInfo (%d, %d, %d) return kResultFalse!", bus, channel, i)); return false; } } if (noteExpressionPUIMapping) { for (uint32 i = 0; i < maxPUI; i++) { puiMap.map[i].noteExpressionTypeID = kInvalidTypeID; } if (noteExpressionPUIMapping->getPhysicalUIMapping (bus, channel, puiMap) == kResultFalse) { addMessage ( testResult, printf ( "Note Expression getPhysicalUIMapping (%d, %d, ...) return kResultFalse!", bus, channel)); } else { for (uint32 i = 0; i < maxPUI; i++) { addMessage (testResult, printf ("Note Expression PhysicalUIMapping: %d => %d", puiMap.map[i].noteExpressionTypeID, puiMap.map[i].physicalUITypeID)); } } } } } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/state0000644000000000000000000000013215124701711023447 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.284485538 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/0000755000175000001440000000000015124701711023514 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/PaxHeaders/validstatetransition.cpp0000644000000000000000000000013215124701711030503 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.285058235 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/validstatetransition.cpp0000644000175000001440000000610115124701711030471 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/state/validstatetransition.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/state/validstatetransition.h" #include "pluginterfaces/base/funknownimpl.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ValidStateTransitionTest //------------------------------------------------------------------------ ValidStateTransitionTest::ValidStateTransitionTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampleSize) : ProcessTest (plugProvider, sampleSize) { if (sampleSize == kSample32) strcpy (name, "Valid State Transition 32bits"); else strcpy (name, "Valid State Transition 64bits"); } //------------------------------------------------------------------------ bool PLUGIN_API ValidStateTransitionTest::run (ITestResult* testResult) { if (!testResult || !vstPlug || !audioEffect) return false; printTestHeader (testResult); if (!canProcessSampleSize (testResult)) return true; // disable it, it was enabled in setup call tresult result = vstPlug->setActive (false); if (result != kResultTrue) return false; auto plugBase = U::cast (vstPlug); if (!plugBase) return false; for (int32 i = 0; i < 4; ++i) { result = audioEffect->setupProcessing (processSetup); if (result != kResultTrue) return false; result = vstPlug->setActive (true); if (result != kResultTrue) return false; result = vstPlug->setActive (false); if (result != kResultTrue) return false; if (activateMainIOBusses (false) == false) return false; result = plugBase->terminate (); if (result != kResultTrue) return false; result = plugBase->initialize (TestingPluginContext::get ()); if (result != kResultTrue) return false; // for the last 2 steps we decide to not reenable the buses, see // https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Change+History/3.0.0/Multiple+Dynamic+IO.html?highlight=kDefaultActive#information-about-busses if (i < 2) { if (activateMainIOBusses (true) == false) return false; } } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/PaxHeaders/repeatidenticalstatetransi0000644000000000000000000000013215124701711031066 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.285058235 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/repeatidenticalstatetransition.h0000644000175000001440000000300615124701711032175 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/state/repeatidenticalstatetransition.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Repeat Identical State Transition. * \ingroup TestClass */ class RepeatIdenticalStateTransitionTest : public TestEnh { public: RepeatIdenticalStateTransitionTest (ITestPlugProvider* plugProvider); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Repeat Identical State Transition") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/PaxHeaders/invalidstatetransition.cpp0000644000000000000000000000013215124701711031032 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.285058235 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/invalidstatetransition.cpp0000644000175000001440000000547015124701711031030 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/state/invalidstatetransition.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/state/invalidstatetransition.h" #include "pluginterfaces/base/funknownimpl.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // InvalidStateTransitionTest //------------------------------------------------------------------------ InvalidStateTransitionTest::InvalidStateTransitionTest (ITestPlugProvider* plugProvider) : TestEnh (plugProvider, kSample32) { } //------------------------------------------------------------------------ bool PLUGIN_API InvalidStateTransitionTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); auto plugBase = U::cast (vstPlug); if (!plugBase) return false; // created tresult result = plugBase->initialize (TestingPluginContext::get ()); if (result == kResultFalse) return false; // setupProcessing is missing ! /*result = audioEffect->setupProcessing (processSetup); if (result != kResultTrue) return false;*/ // initialized result = vstPlug->setActive (false); if (result == kResultOk) return false; result = vstPlug->setActive (true); if (result == kResultFalse) return false; // allocated result = plugBase->initialize (TestingPluginContext::get ()); if (result == kResultOk) return false; result = vstPlug->setActive (false); if (result == kResultFalse) return false; // deallocated (initialized) result = plugBase->initialize (TestingPluginContext::get ()); if (result == kResultOk) return false; result = plugBase->terminate (); if (result == kResultFalse) return false; // terminated (created) result = vstPlug->setActive (false); if (result == kResultOk) return false; result = plugBase->terminate (); if (result == kResultOk) return false; return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/PaxHeaders/validstatetransition.h0000644000000000000000000000013215124701711030150 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.285058235 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/validstatetransition.h0000644000175000001440000000314215124701711030140 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/state/validstatetransition.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/process.h" #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Valid State Transition. * \ingroup TestClass */ class ValidStateTransitionTest : public ProcessTest { public: ValidStateTransitionTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampleSize); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; const char* getName () const SMTG_OVERRIDE { return name; } protected: char name[256]; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/PaxHeaders/bypasspersistence.h0000644000000000000000000000013215124701711027443 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.284485538 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/bypasspersistence.h0000644000175000001440000000276715124701711027447 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/state/bypassstate.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/processing/automation.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Parameter Bypass persistence. * \ingroup TestClass */ class BypassPersistenceTest : public AutomationTest { public: BypassPersistenceTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); bool PLUGIN_API run (ITestResult* testResult) override; DECLARE_VSTTEST ("Parameter Bypass persistence") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/PaxHeaders/repeatidenticalstatetransi0000644000000000000000000000013215124701711031066 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.285058235 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/repeatidenticalstatetransition.cpp0000644000175000001440000000517015124701711032534 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/state/repeatidenticalstatetransition.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/state/repeatidenticalstatetransition.h" #include "pluginterfaces/base/funknownimpl.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // RepeatIdenticalStateTransitionTest //------------------------------------------------------------------------ RepeatIdenticalStateTransitionTest::RepeatIdenticalStateTransitionTest ( ITestPlugProvider* plugProvider) : TestEnh (plugProvider, kSample32) { } //------------------------------------------------------------------------ bool RepeatIdenticalStateTransitionTest::run (ITestResult* testResult) { if (!testResult || !vstPlug || !audioEffect) return false; printTestHeader (testResult); auto plugBase = U::cast (vstPlug); if (!plugBase) return false; tresult result = plugBase->initialize (TestingPluginContext::get ()); if (result != kResultFalse) return false; result = audioEffect->setupProcessing (processSetup); if (result != kResultTrue) return false; result = vstPlug->setActive (true); if (result != kResultOk) return false; result = vstPlug->setActive (true); if (result != kResultFalse) return false; result = vstPlug->setActive (false); if (result != kResultOk) return false; result = vstPlug->setActive (false); if (result == kResultOk) return false; result = plugBase->terminate (); if (result != kResultOk) return false; result = plugBase->terminate (); if (result == kResultOk) return false; result = plugBase->initialize (TestingPluginContext::get ()); if (result != kResultOk) return false; return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/PaxHeaders/bypasspersistence.cpp0000644000000000000000000000013215124701711027776 xustar0030 mtime=1767080905.284485538 30 atime=1767080905.284485538 30 ctime=1767080905.284485538 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/bypasspersistence.cpp0000644000175000001440000001015015124701711027763 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/state/bypassstate.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/state/bypasspersistence.h" #include "public.sdk/source/common/memorystream.h" #include "public.sdk/source/vst/vstpresetfile.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // VstBypassSaveParamTest //------------------------------------------------------------------------ BypassPersistenceTest::BypassPersistenceTest (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : AutomationTest (plugProvider, sampl, 100, 1, false) { } //------------------------------------------------------------------------ bool PLUGIN_API BypassPersistenceTest::run (ITestResult* testResult) { if (!vstPlug || !testResult || !audioEffect) return false; if (!canProcessSampleSize (testResult)) return true; printTestHeader (testResult); if (bypassId == kNoParamId) { testResult->addMessage (STR ("This plugin does not have a bypass parameter!!!")); return true; } unprepareProcessing (); processData.numSamples = 0; processData.numInputs = 0; processData.numOutputs = 0; processData.inputs = nullptr; processData.outputs = nullptr; audioEffect->setProcessing (true); preProcess (testResult); // set bypass on // if (paramChanges[0].getParameterId () == bypassId) { paramChanges[0]->init (bypassId, 1); paramChanges[0]->setPoint (0, 0, 1); controller->setParamNormalized (bypassId, 1); if (controller->getParamNormalized (bypassId) < 1) { testResult->addErrorMessage (STR ("The bypass parameter was not correctly set!")); } } // flush tresult result = audioEffect->process (processData); if (result != kResultOk) { testResult->addErrorMessage ( STR ("The component failed to process without audio buffers!")); audioEffect->setProcessing (false); return false; } postProcess (testResult); audioEffect->setProcessing (false); // save State FUID uid; plugProvider->getComponentUID (uid); MemoryStream stream; PresetFile::savePreset (&stream, uid, vstPlug, controller, nullptr, 0); audioEffect->setProcessing (true); preProcess (testResult); // set bypass off if (paramChanges[0]->getParameterId () == bypassId) { paramChanges[0]->init (bypassId, 1); paramChanges[0]->setPoint (0, 0, 0); controller->setParamNormalized (bypassId, 0); if (controller->getParamNormalized (bypassId) > 0) { testResult->addErrorMessage ( STR ("The bypass parameter was not correctly set in the controller!")); } } // flush result = audioEffect->process (processData); if (result != kResultOk) { testResult->addErrorMessage ( STR ("The component failed to process without audio buffers!")); audioEffect->setProcessing (false); return false; } postProcess (testResult); audioEffect->setProcessing (false); // load previous preset stream.seek (0, IBStream::kIBSeekSet, nullptr); PresetFile::loadPreset (&stream, uid, vstPlug, controller); if (controller->getParamNormalized (bypassId) < 1) { testResult->addErrorMessage ( STR ("The bypass parameter is not in sync in the controller!")); return false; } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/PaxHeaders/invalidstatetransition.h0000644000000000000000000000013215124701711030477 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.285058235 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/state/invalidstatetransition.h0000644000175000001440000000273415124701711030475 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/state/invalidstatetransition.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Invalid State Transition. * \ingroup TestClass */ class InvalidStateTransitionTest : public TestEnh { public: InvalidStateTransitionTest (ITestPlugProvider* plugProvider); bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; DECLARE_VSTTEST ("Invalid State Transition") }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/testbase.h0000644000000000000000000000013215124701711024367 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.285058235 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/testbase.h0000644000175000001440000001457315124701711024371 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/testbase.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/test/itest.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivstparameterchanges.h" #include "pluginterfaces/vst/ivsttestplugprovider.h" #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ void addMessage (ITestResult* testResult, const std::u16string& str); void addMessage (ITestResult* testResult, const tchar* str); void addErrorMessage (ITestResult* testResult, const tchar* str); void addErrorMessage (ITestResult* testResult, const std::u16string& str); std::u16string printf (const char8* format, ...); //------------------------------------------------------------------------ #define DECLARE_VSTTEST(name) \ const char* getName () const SMTG_OVERRIDE { return name; } //------------------------------------------------------------------------ struct TestingPluginContext { static FUnknown* get () { return instance ().context; } static void set (FUnknown* context) { instance ().context = context; } private: static TestingPluginContext& instance () { static TestingPluginContext gInstance; return gInstance; } FUnknown* context {nullptr}; }; //------------------------------------------------------------------------ struct TestDefaults { int32 numIterations {20}; int32 defaultSampleRate {44100}; int32 defaultBlockSize {64}; int32 maxBlockSize {8192}; int32 buffersAreEqual {0}; int32 numAudioBlocksToProcess {3}; uint64 channelIsSilent {1}; static TestDefaults& instance () { static TestDefaults gInstance; return gInstance; } }; //------------------------------------------------------------------------ /** Test Helper. * \ingroup TestClass */ class TestBase : public ITest { public: TestBase (ITestPlugProvider* plugProvider); virtual ~TestBase (); virtual const char* getName () const = 0; DECLARE_FUNKNOWN_METHODS bool PLUGIN_API setup () SMTG_OVERRIDE; bool PLUGIN_API run (ITestResult* /*testResult*/) SMTG_OVERRIDE = 0; bool PLUGIN_API teardown () SMTG_OVERRIDE; virtual bool activateMainIOBusses (bool val); virtual void printTestHeader (ITestResult* testResult); //------------------------------------------------------------------------ protected: ITestPlugProvider* plugProvider; IComponent* vstPlug; IEditController* controller; private: TestBase (); }; using ProcessSampleSize = int32; //------------------------------------------------------------------------ /** Test Helper. * \ingroup TestClass */ class TestEnh : public TestBase { public: TestEnh (ITestPlugProvider* plugProvider, ProcessSampleSize sampl); ~TestEnh () override; enum AudioDefaults { kBlockSize = 64, kMaxSamplesPerBlock = 8192, kSampleRate = 44100, }; bool PLUGIN_API setup () SMTG_OVERRIDE; bool PLUGIN_API teardown () SMTG_OVERRIDE; //------------------------------------------------------------------------ protected: // interfaces IAudioProcessor* audioEffect; ProcessSetup processSetup; }; //------------------------------------------------------------------------ /** AutomationTest helper classes. * \ingroup TestClass */ class ParamPoint { public: ParamPoint () : offsetSamples (-1), value (0.), read (false) {} void set (int32 _offsetSamples, double _value) { offsetSamples = _offsetSamples; value = _value; } void get (int32& _offsetSamples, double& _value) { _offsetSamples = offsetSamples; _value = value; read = true; } bool wasRead () const { return read; } private: int32 offsetSamples; double value; bool read; }; //------------------------------------------------------------------------ /** AutomationTest helper classes: implementation of IParamValueQueue. * \ingroup TestClass */ class ParamChanges : public IParamValueQueue { public: DECLARE_FUNKNOWN_METHODS ParamChanges (); virtual ~ParamChanges (); void init (ParamID _id, int32 _numPoints); bool setPoint (int32 index, int32 offsetSamples, double value); void resetPoints (); int32 getProcessedFrames () const; void setProcessedFrames (int32 amount); bool havePointsBeenRead (bool atAll); //---for IParamValueQueue------------------------- ParamID PLUGIN_API getParameterId () SMTG_OVERRIDE; int32 PLUGIN_API getPointCount () SMTG_OVERRIDE; tresult PLUGIN_API getPoint (int32 index, int32& offsetSamples, double& value) SMTG_OVERRIDE; tresult PLUGIN_API addPoint (int32 /*offsetSamples*/, double /*value*/, int32& /*index*/) SMTG_OVERRIDE; //--------------------------------------------------------- private: ParamID id = kNoParamId; int32 numPoints = 0; int32 numUsedPoints = 0; int32 processedFrames = 0; ParamPoint* points = nullptr; }; //------------------------------------------------------------------------ class StringResult final : public IStringResult { public: const std::string& get () const { return data; } void PLUGIN_API setText (const char8* text) override { data = text; } tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) override { QUERY_INTERFACE (_iid, obj, FUnknown::iid, IStringResult) QUERY_INTERFACE (_iid, obj, IStringResult::iid, IStringResult) *obj = nullptr; return kNoInterface; } uint32 PLUGIN_API addRef () override { return ++__refCount; } uint32 PLUGIN_API release () override { if (--__refCount == 0) { delete this; return 0; } return __refCount; } private: std::string data; std::atomic __refCount {0}; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/vsttestsuite.cpp0000644000000000000000000000013215124701711025676 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.285285125 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/vsttestsuite.cpp0000644000175000001440000000143115124701711025665 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/vsttestsuite.cpp // Created by : Steinberg, 10/2005 // Description : VST Hosting Utilities // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/testbase.cpp0000644000000000000000000000013215124701711024722 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.285058235 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/testbase.cpp0000644000175000001440000002032015124701711024707 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/testbase.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/testbase.h" #include "public.sdk/source/vst/utility/stringconvert.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // TestBase //------------------------------------------------------------------------ TestBase::TestBase (ITestPlugProvider* plugProvider) : plugProvider (plugProvider) , vstPlug (nullptr) , controller (nullptr) {FUNKNOWN_CTOR} //------------------------------------------------------------------------ TestBase::TestBase () : plugProvider (nullptr) , vstPlug (nullptr) , controller (nullptr) {FUNKNOWN_CTOR} //------------------------------------------------------------------------ TestBase::~TestBase () {FUNKNOWN_DTOR} //------------------------------------------------------------------------ IMPLEMENT_FUNKNOWN_METHODS (TestBase, ITest, ITest::iid); //------------------------------------------------------------------------ bool TestBase::setup () { if (plugProvider) { vstPlug = plugProvider->getComponent (); if (!vstPlug) return false; controller = plugProvider->getController (); return activateMainIOBusses (true); } return false; } //------------------------------------------------------------------------ bool TestBase::teardown () { if (vstPlug) { activateMainIOBusses (false); plugProvider->releasePlugIn (vstPlug, controller); } return true; } //------------------------------------------------------------------------ bool TestBase::activateMainIOBusses (bool val) { if (!vstPlug) return false; bool result = true; if (auto countIn = vstPlug->getBusCount (kAudio, kInput) > 0) { if (vstPlug->activateBus (kAudio, kInput, 0, val) == kResultFalse) result = false; } if (auto countOut = vstPlug->getBusCount (kAudio, kOutput) > 0) { if (vstPlug->activateBus (kAudio, kOutput, 0, val) == kResultFalse) result = false; } return result; } //------------------------------------------------------------------------ void TestBase::printTestHeader (ITestResult* testResult) { using StringConvert::convert; std::string str = "==="; str += getName (); str += " ===================================="; addMessage (testResult, convert (str)); } //------------------------------------------------------------------------ // Component Initialize / Terminate //------------------------------------------------------------------------ //------------------------------------------------------------------------ // VstTestEnh //------------------------------------------------------------------------ TestEnh::TestEnh (ITestPlugProvider* plugProvider, ProcessSampleSize sampl) : TestBase (plugProvider), audioEffect (nullptr) { // process setup defaults memset (&processSetup, 0, sizeof (ProcessSetup)); processSetup.processMode = kRealtime; processSetup.symbolicSampleSize = sampl; processSetup.maxSamplesPerBlock = kMaxSamplesPerBlock; processSetup.sampleRate = kSampleRate; } //------------------------------------------------------------------------ TestEnh::~TestEnh () { } //------------------------------------------------------------------------ bool TestEnh::setup () { bool res = TestBase::setup (); if (vstPlug) { tresult check = vstPlug->queryInterface (IAudioProcessor::iid, (void**)&audioEffect); if (check != kResultTrue) return false; } return (res && audioEffect); } //------------------------------------------------------------------------ bool TestEnh::teardown () { if (audioEffect) audioEffect->release (); bool res = TestBase::teardown (); return res && audioEffect; } //------------------------------------------------------------------------ void addMessage (ITestResult* testResult, const std::u16string& str) { testResult->addMessage (reinterpret_cast (str.data ())); } //------------------------------------------------------------------------ void addMessage (ITestResult* testResult, const tchar* str) { testResult->addMessage (str); } //------------------------------------------------------------------------ void addErrorMessage (ITestResult* testResult, const tchar* str) { testResult->addErrorMessage (str); } //------------------------------------------------------------------------ void addErrorMessage (ITestResult* testResult, const std::u16string& str) { testResult->addErrorMessage (reinterpret_cast (str.data ())); } //------------------------------------------------------------------------ std::u16string printf (const char8* format, ...) { using StringConvert::convert; char8 string[1024 * 4]; va_list marker; va_start (marker, format); vsnprintf (string, kPrintfBufferSize, format, marker); return convert (string); } IMPLEMENT_FUNKNOWN_METHODS (ParamChanges, IParamValueQueue, IParamValueQueue::iid) //------------------------------------------------------------------------ ParamChanges::ParamChanges () {FUNKNOWN_CTOR} //------------------------------------------------------------------------ ParamChanges::~ParamChanges () { if (points) delete[] points; FUNKNOWN_DTOR } //------------------------------------------------------------------------ void ParamChanges::init (ParamID _id, int32 _numPoints) { id = _id; numPoints = _numPoints; numUsedPoints = 0; if (points) delete[] points; points = new ParamPoint[numPoints]; processedFrames = 0; } //------------------------------------------------------------------------ bool ParamChanges::setPoint (int32 index, int32 offsetSamples, double value) { if (points && (index >= 0) && (index == numUsedPoints) && (index < numPoints)) { points[index].set (offsetSamples, value); numUsedPoints++; return true; } if (!points) return true; return false; } //------------------------------------------------------------------------ void ParamChanges::resetPoints () { numUsedPoints = 0; processedFrames = 0; } //------------------------------------------------------------------------ int32 ParamChanges::getProcessedFrames () const { return processedFrames; } //------------------------------------------------------------------------ void ParamChanges::setProcessedFrames (int32 amount) { processedFrames = amount; } //------------------------------------------------------------------------ bool ParamChanges::havePointsBeenRead (bool atAll) { for (int32 i = 0; i < getPointCount (); ++i) { if (points[i].wasRead ()) { if (atAll) return true; } else if (!atAll) return false; } return !atAll; } //------------------------------------------------------------------------ ParamID PLUGIN_API ParamChanges::getParameterId () { return id; } //------------------------------------------------------------------------ int32 PLUGIN_API ParamChanges::getPointCount () { return numUsedPoints; } //------------------------------------------------------------------------ tresult PLUGIN_API ParamChanges::getPoint (int32 index, int32& offsetSamples, double& value) { if (points && (index < numUsedPoints) && (index >= 0)) { points[index].get (offsetSamples, value); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API ParamChanges::addPoint (int32 /*offsetSamples*/, double /*value*/, int32& /*index*/) { return kResultFalse; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/PaxHeaders/unit0000644000000000000000000000013215124701711023306 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.285058235 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/0000755000175000001440000000000015124701711023353 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/PaxHeaders/checkunitstructure.h0000644000000000000000000000013215124701711027472 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.285058235 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/checkunitstructure.h0000644000175000001440000000301115124701711027455 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/unit/checkunitstructure.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Check Unit Structure. * \ingroup TestClass */ class UnitStructureTest : public TestBase { public: UnitStructureTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("Check Unit Structure") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/PaxHeaders/scanunits.h0000644000000000000000000000013215124701711025543 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.285285125 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/scanunits.h0000644000175000001440000000274215124701711025540 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/unit/scanunits.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Scan Units. * \ingroup TestClass */ class UnitInfoTest : public TestBase { public: UnitInfoTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("Scan Units") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/PaxHeaders/checkunitstructure.cpp0000644000000000000000000000013215124701711030025 xustar0030 mtime=1767080905.285058235 30 atime=1767080905.285058235 30 ctime=1767080905.285058235 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/checkunitstructure.cpp0000644000175000001440000000704115124701711030017 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/unit/checkunitstructure.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/unit/checkunitstructure.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/vst/ivstunits.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // UnitStructureTest //------------------------------------------------------------------------ UnitStructureTest::UnitStructureTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool UnitStructureTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); if (auto iUnitInfo = U::cast (controller)) { int32 unitCount = iUnitInfo->getUnitCount (); if (unitCount <= 0) { addMessage (testResult, STR ("No units found, while controller implements IUnitInfo !!!")); } UnitInfo unitInfo = {}; UnitInfo tmpInfo = {}; bool rootFound = false; for (int32 unitIndex = 0; unitIndex < unitCount; unitIndex++) { if (iUnitInfo->getUnitInfo (unitIndex, unitInfo) == kResultOk) { // check parent Id if (unitInfo.parentUnitId != kNoParentUnitId) //-1: connected to root { bool noParent = true; for (int32 i = 0; i < unitCount; ++i) { if (iUnitInfo->getUnitInfo (i, tmpInfo) == kResultOk) { if (unitInfo.parentUnitId == tmpInfo.id) { noParent = false; break; } } } if (noParent && unitInfo.parentUnitId != kRootUnitId) { addErrorMessage ( testResult, printf ("Unit %03d: Parent does not exist!!", unitInfo.id)); return false; } } else if (!rootFound) { // root Unit have always the rootID if (unitInfo.id != kRootUnitId) { // we should have a root unit id addErrorMessage ( testResult, printf ("Unit %03d: Should be the Root Unit => id should be %03d!!", unitInfo.id, kRootUnitId)); return false; } rootFound = true; } else { addErrorMessage ( testResult, printf ("Unit %03d: Has no parent, but there is a root already.", unitInfo.id)); return false; } } else { addErrorMessage (testResult, printf ("Unit %03d: No unit info.", unitInfo.id)); return false; } } addMessage (testResult, STR ("All units have valid parent IDs.")); } else { addMessage (testResult, STR ("This component does not support IUnitInfo!")); } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/PaxHeaders/scanprograms.cpp0000644000000000000000000000013215124701711026566 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.285285125 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/scanprograms.cpp0000644000175000001440000001525715124701711026570 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/unit/scanprograms.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/unit/scanprograms.h" #include "public.sdk/source/vst/utility/stringconvert.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/vst/ivstunits.h" #include "pluginterfaces/vst/vstpresetkeys.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ProgramInfoTest //------------------------------------------------------------------------ ProgramInfoTest::ProgramInfoTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool ProgramInfoTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); if (auto iUnitInfo = U::cast (controller)) { int32 programListCount = iUnitInfo->getProgramListCount (); if (programListCount == 0) { addMessage (testResult, STR ("This component does not export any programs.")); return true; } else if (programListCount < 0) { addErrorMessage (testResult, STR ("IUnitInfo::getProgramListCount () returned a negative number.")); return false; } // used to check double IDs auto programListIds = std::unique_ptr (new int32[programListCount]); for (int32 programListIndex = 0; programListIndex < programListCount; programListIndex++) { // get programm list info ProgramListInfo programListInfo; if (iUnitInfo->getProgramListInfo (programListIndex, programListInfo) == kResultOk) { int32 programListId = programListInfo.id; programListIds[programListIndex] = programListId; if (programListId < 0) { addErrorMessage (testResult, printf ("Programlist %03d: Invalid ID!!!", programListIndex)); return false; } // check if ID is already used by another parameter for (int32 idIndex = 0; idIndex < programListIndex; idIndex++) { if (programListIds[idIndex] == programListIds[programListIndex]) { addErrorMessage (testResult, printf ("Programlist %03d: ID already used!!!", programListIndex)); return false; } } auto programListName = StringConvert::convert (programListInfo.name); if (programListName.empty ()) { addErrorMessage (testResult, printf ("Programlist %03d (id=%d): No name!!!", programListIndex, programListId)); return false; } int32 programCount = programListInfo.programCount; if (programCount <= 0) { addMessage ( testResult, printf ( "Programlist %03d (id=%d): \"%s\" No programs!!! (programCount is null!)", programListIndex, programListId, StringConvert::convert (programListName).data ())); // return false; } addMessage (testResult, printf ("Programlist %03d (id=%d): \"%s\" (%d programs).", programListIndex, programListId, programListName.data (), programCount)); for (int32 programIndex = 0; programIndex < programCount; programIndex++) { TChar programName[256]; if (iUnitInfo->getProgramName (programListId, programIndex, programName) == kResultOk) { if (programName[0] == 0) { addErrorMessage ( testResult, printf ("Programlist %03d->Program %03d: has no name!!!", programListIndex, programIndex)); return false; } auto programNameUTF8 = StringConvert::convert (programName); auto msg = printf ("Programlist %03d->Program %03d: \"%s\"", programListIndex, programIndex, programNameUTF8.data ()); String128 programInfo {}; if (iUnitInfo->getProgramInfo (programListId, programIndex, PresetAttributes::kInstrument, programInfo) == kResultOk) { auto programInfoUTF8 = StringConvert::convert (programInfo); msg += StringConvert::convert (" (instrument = \""); msg += (const char16_t*)programInfo; msg += StringConvert::convert ("\")"); } addMessage (testResult, msg.data ()); if (iUnitInfo->hasProgramPitchNames (programListId, programIndex) == kResultOk) { addMessage (testResult, printf (" => \"%s\": supports PitchNames", programNameUTF8.data ())); String128 pitchName = {0}; for (int16 midiPitch = 0; midiPitch < 128; midiPitch++) { if (iUnitInfo->getProgramPitchName (programListId, programIndex, midiPitch, pitchName) == kResultOk) { msg = printf (" => MIDI Pitch %d => \"", midiPitch); msg += (const char16_t*)pitchName; msg += StringConvert::convert ("\""); addMessage (testResult, msg.data ()); } } } } } } } } else { addMessage (testResult, STR ("This component does not export any programs.")); // check if not more than 1 program change parameter is defined int32 numPrgChanges = 0; for (int32 i = 0; i < controller->getParameterCount (); ++i) { ParameterInfo paramInfo = {}; if (controller->getParameterInfo (i, paramInfo) != kResultOk) { if (paramInfo.flags & ParameterInfo::kIsProgramChange) numPrgChanges++; } } if (numPrgChanges > 1) { addErrorMessage ( testResult, printf ("More than 1 programChange Parameter (%d) without support of IUnitInfo!!!", numPrgChanges)); } } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/PaxHeaders/scanunits.cpp0000644000000000000000000000013215124701711026076 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.285285125 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/scanunits.cpp0000644000175000001440000001056315124701711026073 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/unit/scanunits.cpp // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/testsuite/unit/scanunits.h" #include "public.sdk/source/vst/utility/stringconvert.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/vst/ivstunits.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // UnitInfoTest //------------------------------------------------------------------------ UnitInfoTest::UnitInfoTest (ITestPlugProvider* plugProvider) : TestBase (plugProvider) { } //------------------------------------------------------------------------ bool UnitInfoTest::run (ITestResult* testResult) { if (!testResult || !vstPlug) return false; printTestHeader (testResult); if (auto iUnitInfo = U::cast (controller)) { int32 unitCount = iUnitInfo->getUnitCount (); if (unitCount <= 0) { addMessage (testResult, STR ("No units found, while controller implements IUnitInfo !!!")); } else { addMessage (testResult, printf ("This component has %d unit(s).", unitCount)); } auto unitIds = std::unique_ptr (new int32[unitCount]); for (int32 unitIndex = 0; unitIndex < unitCount; unitIndex++) { UnitInfo unitInfo = {}; if (iUnitInfo->getUnitInfo (unitIndex, unitInfo) == kResultOk) { int32 unitId = unitInfo.id; unitIds[unitIndex] = unitId; if (unitId < 0) { addErrorMessage (testResult, printf ("Unit %03d: Invalid ID!", unitIndex)); return false; } // check if ID is already used by another unit for (int32 idIndex = 0; idIndex < unitIndex; idIndex++) { if (unitIds[idIndex] == unitIds[unitIndex]) { addErrorMessage (testResult, printf ("Unit %03d: ID already used!!!", unitIndex)); return false; } } auto unitName = StringConvert::convert (unitInfo.name); if (unitName.empty ()) { addErrorMessage (testResult, printf ("Unit %03d: No name!", unitIndex)); return false; } int32 parentUnitId = unitInfo.parentUnitId; if (parentUnitId < -1) { addErrorMessage (testResult, printf ("Unit %03d: Invalid parent ID!", unitIndex)); return false; } else if (parentUnitId == unitId) { addErrorMessage ( testResult, printf ("Unit %03d: Parent ID is equal to Unit ID!", unitIndex)); return false; } int32 unitProgramListId = unitInfo.programListId; if (unitProgramListId < -1) { addErrorMessage (testResult, printf ("Unit %03d: Invalid programlist ID!", unitIndex)); return false; } addMessage ( testResult, printf (" Unit%03d (ID = %d): \"%s\" (parent ID = %d, programlist ID = %d)", unitIndex, unitId, unitName.data (), parentUnitId, unitProgramListId)); // test select Unit if (iUnitInfo->selectUnit (unitIndex) == kResultTrue) { UnitID newSelected = iUnitInfo->getSelectedUnit (); if (newSelected != unitIndex) { addMessage ( testResult, printf ( "The host has selected Unit ID = %d but getSelectedUnit returns ID = %d!!!", unitIndex, newSelected)); } } } else { addMessage (testResult, printf ("Unit%03d: No unit info!", unitIndex)); } } } else { addMessage (testResult, STR ("This component has no units.")); } return true; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/PaxHeaders/scanprograms.h0000644000000000000000000000013215124701711026233 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.285285125 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/testsuite/unit/scanprograms.h0000644000175000001440000000276115124701711026231 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/unit/scanprograms.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/testsuite/testbase.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Scan Programs. * \ingroup TestClass */ class ProgramInfoTest : public TestBase { public: ProgramInfoTest (ITestPlugProvider* plugProvider); DECLARE_VSTTEST ("Scan Programs") bool PLUGIN_API run (ITestResult* testResult) SMTG_OVERRIDE; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstcomponentbase.cpp0000644000000000000000000000013215124701711024451 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstcomponentbase.cpp0000644000175000001440000001057015124701711024444 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstcomponentbase.cpp // Created by : Steinberg, 05/2005 // Description : Base class for VST Component and Edit Controller // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstcomponentbase.h" #include "base/source/fstring.h" #include "pluginterfaces/base/funknownimpl.h" namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // ComponentBase Implementation //------------------------------------------------------------------------ ComponentBase::ComponentBase () { } //------------------------------------------------------------------------ ComponentBase::~ComponentBase () { } //------------------------------------------------------------------------ tresult PLUGIN_API ComponentBase::initialize (FUnknown* context) { // check if already initialized if (hostContext) return kResultFalse; hostContext = context; return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API ComponentBase::terminate () { // release host interfaces hostContext = nullptr; // in case host did not disconnect us, // release peer now if (peerConnection) { peerConnection->disconnect (this); peerConnection = nullptr; } return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API ComponentBase::connect (IConnectionPoint* other) { if (!other) return kInvalidArgument; // check if already connected if (peerConnection) return kResultFalse; peerConnection = other; return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API ComponentBase::disconnect (IConnectionPoint* other) { if (peerConnection && other == peerConnection) { peerConnection = nullptr; return kResultOk; } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API ComponentBase::notify (IMessage* message) { if (!message) return kInvalidArgument; if (FIDStringsEqual (message->getMessageID (), "TextMessage")) { TChar string[256] = {0}; if (message->getAttributes ()->getString ("Text", string, sizeof (string)) == kResultOk) { String tmp (string); tmp.toMultiByte (kCP_Utf8); return receiveText (tmp.text8 ()); } } return kResultFalse; } //------------------------------------------------------------------------ IMessage* ComponentBase::allocateMessage () const { if (auto hostApp = U::cast (hostContext)) return Vst::allocateMessage (hostApp); return nullptr; } //------------------------------------------------------------------------ tresult ComponentBase::sendMessage (IMessage* message) const { if (message != nullptr && getPeer () != nullptr) return getPeer ()->notify (message); return kResultFalse; } //------------------------------------------------------------------------ tresult ComponentBase::sendTextMessage (const char8* text) const { if (auto msg = owned (allocateMessage ())) { msg->setMessageID ("TextMessage"); String tmp (text, kCP_Utf8); if (tmp.length () >= 256) tmp.remove (255); msg->getAttributes ()->setString ("Text", tmp.text16 ()); return sendMessage (msg); } return kResultFalse; } //------------------------------------------------------------------------ tresult ComponentBase::sendMessageID (const char8* messageID) const { if (auto msg = owned (allocateMessage ())) { msg->setMessageID (messageID); return sendMessage (msg); } return kResultFalse; } //------------------------------------------------------------------------ tresult ComponentBase::receiveText (const char8* /*text*/) { return kResultOk; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/moduleinfo0000644000000000000000000000013215124701711022437 xustar0030 mtime=1767080905.282785169 30 atime=1767080905.282210861 30 ctime=1767080905.282785169 qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/0000755000175000001440000000000015124701711022504 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/PaxHeaders/moduleinfoparser.h0000644000000000000000000000013215124701711026243 xustar0030 mtime=1767080905.282785169 30 atime=1767080905.282785169 30 ctime=1767080905.282785169 qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/moduleinfoparser.h0000644000175000001440000000415715124701711026242 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // // Category : moduleinfo // Filename : public.sdk/source/vst/moduleinfo/moduleinfoparser.h // Created by : Steinberg, 01/2022 // Description : utility functions to parse moduleinfo json files // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "moduleinfo.h" #include #include #include //------------------------------------------------------------------------ namespace Steinberg::ModuleInfoLib { //------------------------------------------------------------------------ /** parse a json formatted string to a ModuleInfo struct * * @param jsonData a string view to a json formatted string * @param optErrorOutput optional error output stream where to print parse error * @return ModuleInfo if parsing succeeded */ std::optional parseJson (std::string_view jsonData, std::ostream* optErrorOutput); //------------------------------------------------------------------------ /** parse a json formatted string to a ModuleInfo::CompatibilityList * * @param jsonData a string view to a json formatted string * @param optErrorOutput optional error output stream where to print parse error * @return ModuleInfo::CompatibilityList if parsing succeeded */ std::optional parseCompatibilityJson (std::string_view jsonData, std::ostream* optErrorOutput); //------------------------------------------------------------------------ } // Steinberg::ModuelInfoLib qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/PaxHeaders/json.h0000644000000000000000000000013215124701711023636 xustar0030 mtime=1767080905.282785169 30 atime=1767080905.282210861 30 ctime=1767080905.282785169 qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/json.h0000644000175000001440000030464715124701711023644 0ustar00rncbcusers/* The latest version of this library is available on GitHub; https://github.com/sheredom/json.h. */ /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to . */ #ifndef SHEREDOM_JSON_H_INCLUDED #define SHEREDOM_JSON_H_INCLUDED #if defined(_MSC_VER) #pragma warning(push) /* disable warning: no function prototype given: converting '()' to '(void)' */ #pragma warning(disable : 4255) /* disable warning: '__cplusplus' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */ #pragma warning(disable : 4668) /* disable warning: 'bytes padding added after construct' */ #pragma warning(disable : 4820) #endif #include #include #if defined(_MSC_VER) || defined(__MINGW32__) #define json_weak __inline #elif defined(__clang__) || defined(__GNUC__) #define json_weak __attribute__((weak)) #else #error Non clang, non gcc, non MSVC compiler found! #endif #ifdef __cplusplus extern "C" { #endif struct json_value_s; struct json_parse_result_s; enum json_parse_flags_e { json_parse_flags_default = 0, /* allow trailing commas in objects and arrays. For example, both [true,] and {"a" : null,} would be allowed with this option on. */ json_parse_flags_allow_trailing_comma = 0x1, /* allow unquoted keys for objects. For example, {a : null} would be allowed with this option on. */ json_parse_flags_allow_unquoted_keys = 0x2, /* allow a global unbracketed object. For example, a : null, b : true, c : {} would be allowed with this option on. */ json_parse_flags_allow_global_object = 0x4, /* allow objects to use '=' instead of ':' between key/value pairs. For example, a = null, b : true would be allowed with this option on. */ json_parse_flags_allow_equals_in_object = 0x8, /* allow that objects don't have to have comma separators between key/value pairs. */ json_parse_flags_allow_no_commas = 0x10, /* allow c-style comments (either variants) to be ignored in the input JSON file. */ json_parse_flags_allow_c_style_comments = 0x20, /* deprecated flag, unused. */ json_parse_flags_deprecated = 0x40, /* record location information for each value. */ json_parse_flags_allow_location_information = 0x80, /* allow strings to be 'single quoted'. */ json_parse_flags_allow_single_quoted_strings = 0x100, /* allow numbers to be hexadecimal. */ json_parse_flags_allow_hexadecimal_numbers = 0x200, /* allow numbers like +123 to be parsed. */ json_parse_flags_allow_leading_plus_sign = 0x400, /* allow numbers like .0123 or 123. to be parsed. */ json_parse_flags_allow_leading_or_trailing_decimal_point = 0x800, /* allow Infinity, -Infinity, NaN, -NaN. */ json_parse_flags_allow_inf_and_nan = 0x1000, /* allow multi line string values. */ json_parse_flags_allow_multi_line_strings = 0x2000, /* allow simplified JSON to be parsed. Simplified JSON is an enabling of a set of other parsing options. */ json_parse_flags_allow_simplified_json = (json_parse_flags_allow_trailing_comma | json_parse_flags_allow_unquoted_keys | json_parse_flags_allow_global_object | json_parse_flags_allow_equals_in_object | json_parse_flags_allow_no_commas), /* allow JSON5 to be parsed. JSON5 is an enabling of a set of other parsing options. */ json_parse_flags_allow_json5 = (json_parse_flags_allow_trailing_comma | json_parse_flags_allow_unquoted_keys | json_parse_flags_allow_c_style_comments | json_parse_flags_allow_single_quoted_strings | json_parse_flags_allow_hexadecimal_numbers | json_parse_flags_allow_leading_plus_sign | json_parse_flags_allow_leading_or_trailing_decimal_point | json_parse_flags_allow_inf_and_nan | json_parse_flags_allow_multi_line_strings) }; /* Parse a JSON text file, returning a pointer to the root of the JSON * structure. json_parse performs 1 call to malloc for the entire encoding. * Returns 0 if an error occurred (malformed JSON input, or malloc failed). */ json_weak struct json_value_s *json_parse(const void *src, size_t src_size); /* Parse a JSON text file, returning a pointer to the root of the JSON * structure. json_parse performs 1 call to alloc_func_ptr for the entire * encoding. Returns 0 if an error occurred (malformed JSON input, or malloc * failed). If an error occurred, the result struct (if not NULL) will explain * the type of error, and the location in the input it occurred. If * alloc_func_ptr is null then malloc is used. */ json_weak struct json_value_s * json_parse_ex(const void *src, size_t src_size, size_t flags_bitset, void *(*alloc_func_ptr)(void *, size_t), void *user_data, struct json_parse_result_s *result); /* Extracts a value and all the data that makes it up into a newly created * value. json_extract_value performs 1 call to malloc for the entire encoding. */ json_weak struct json_value_s * json_extract_value(const struct json_value_s *value); /* Extracts a value and all the data that makes it up into a newly created * value. json_extract_value performs 1 call to alloc_func_ptr for the entire * encoding. If alloc_func_ptr is null then malloc is used. */ json_weak struct json_value_s * json_extract_value_ex(const struct json_value_s *value, void *(*alloc_func_ptr)(void *, size_t), void *user_data); /* Write out a minified JSON utf-8 string. This string is an encoding of the * minimal string characters required to still encode the same data. * json_write_minified performs 1 call to malloc for the entire encoding. Return * 0 if an error occurred (malformed JSON input, or malloc failed). The out_size * parameter is optional as the utf-8 string is null terminated. */ json_weak void *json_write_minified(const struct json_value_s *value, size_t *out_size); /* Write out a pretty JSON utf-8 string. This string is encoded such that the * resultant JSON is pretty in that it is easily human readable. The indent and * newline parameters allow a user to specify what kind of indentation and * newline they want (two spaces / three spaces / tabs? \r, \n, \r\n ?). Both * indent and newline can be NULL, indent defaults to two spaces (" "), and * newline defaults to linux newlines ('\n' as the newline character). * json_write_pretty performs 1 call to malloc for the entire encoding. Return 0 * if an error occurred (malformed JSON input, or malloc failed). The out_size * parameter is optional as the utf-8 string is null terminated. */ json_weak void *json_write_pretty(const struct json_value_s *value, const char *indent, const char *newline, size_t *out_size); /* Reinterpret a JSON value as a string. Returns null is the value was not a * string. */ json_weak struct json_string_s * json_value_as_string(struct json_value_s *const value); /* Reinterpret a JSON value as a number. Returns null is the value was not a * number. */ json_weak struct json_number_s * json_value_as_number(struct json_value_s *const value); /* Reinterpret a JSON value as an object. Returns null is the value was not an * object. */ json_weak struct json_object_s * json_value_as_object(struct json_value_s *const value); /* Reinterpret a JSON value as an array. Returns null is the value was not an * array. */ json_weak struct json_array_s * json_value_as_array(struct json_value_s *const value); /* Whether the value is true. */ json_weak int json_value_is_true(const struct json_value_s *const value); /* Whether the value is false. */ json_weak int json_value_is_false(const struct json_value_s *const value); /* Whether the value is null. */ json_weak int json_value_is_null(const struct json_value_s *const value); /* The various types JSON values can be. Used to identify what a value is. */ enum json_type_e { json_type_string, json_type_number, json_type_object, json_type_array, json_type_true, json_type_false, json_type_null }; /* A JSON string value. */ struct json_string_s { /* utf-8 string */ const char *string; /* The size (in bytes) of the string */ size_t string_size; }; /* A JSON string value (extended). */ struct json_string_ex_s { /* The JSON string this extends. */ struct json_string_s string; /* The character offset for the value in the JSON input. */ size_t offset; /* The line number for the value in the JSON input. */ size_t line_no; /* The row number for the value in the JSON input, in bytes. */ size_t row_no; }; /* A JSON number value. */ struct json_number_s { /* ASCII string containing representation of the number. */ const char *number; /* the size (in bytes) of the number. */ size_t number_size; }; /* an element of a JSON object. */ struct json_object_element_s { /* the name of this element. */ struct json_string_s *name; /* the value of this element. */ struct json_value_s *value; /* the next object element (can be NULL if the last element in the object). */ struct json_object_element_s *next; }; /* a JSON object value. */ struct json_object_s { /* a linked list of the elements in the object. */ struct json_object_element_s *start; /* the number of elements in the object. */ size_t length; }; /* an element of a JSON array. */ struct json_array_element_s { /* the value of this element. */ struct json_value_s *value; /* the next array element (can be NULL if the last element in the array). */ struct json_array_element_s *next; }; /* a JSON array value. */ struct json_array_s { /* a linked list of the elements in the array. */ struct json_array_element_s *start; /* the number of elements in the array. */ size_t length; }; /* a JSON value. */ struct json_value_s { /* a pointer to either a json_string_s, json_number_s, json_object_s, or. */ /* json_array_s. Should be cast to the appropriate struct type based on what. */ /* the type of this value is. */ void *payload; /* must be one of json_type_e. If type is json_type_true, json_type_false, or. */ /* json_type_null, payload will be NULL. */ size_t type; }; /* a JSON value (extended). */ struct json_value_ex_s { /* the JSON value this extends. */ struct json_value_s value; /* the character offset for the value in the JSON input. */ size_t offset; /* the line number for the value in the JSON input. */ size_t line_no; /* the row number for the value in the JSON input, in bytes. */ size_t row_no; }; /* a parsing error code. */ enum json_parse_error_e { /* no error occurred (huzzah!). */ json_parse_error_none = 0, /* expected either a comma or a closing '}' or ']' to close an object or. */ /* array! */ json_parse_error_expected_comma_or_closing_bracket, /* colon separating name/value pair was missing! */ json_parse_error_expected_colon, /* expected string to begin with '"'! */ json_parse_error_expected_opening_quote, /* invalid escaped sequence in string! */ json_parse_error_invalid_string_escape_sequence, /* invalid number format! */ json_parse_error_invalid_number_format, /* invalid value! */ json_parse_error_invalid_value, /* reached end of buffer before object/array was complete! */ json_parse_error_premature_end_of_buffer, /* string was malformed! */ json_parse_error_invalid_string, /* a call to malloc, or a user provider allocator, failed. */ json_parse_error_allocator_failed, /* the JSON input had unexpected trailing characters that weren't part of the. */ /* JSON value. */ json_parse_error_unexpected_trailing_characters, /* catch-all error for everything else that exploded (real bad chi!). */ json_parse_error_unknown }; /* error report from json_parse_ex(). */ struct json_parse_result_s { /* the error code (one of json_parse_error_e). */ size_t error; /* the character offset for the error in the JSON input. */ size_t error_offset; /* the line number for the error in the JSON input. */ size_t error_line_no; /* the row number for the error, in bytes. */ size_t error_row_no; }; #ifdef __cplusplus } /* extern "C". */ #endif #include #if defined(_MSC_VER) #pragma warning(pop) #endif #if defined(_MSC_VER) && (_MSC_VER < 1920) #define json_uintmax_t unsigned __int64 #else #include #define json_uintmax_t uintmax_t #endif #if defined(_MSC_VER) #define json_strtoumax _strtoui64 #else #define json_strtoumax strtoumax #endif #if defined(__cplusplus) && (__cplusplus >= 201103L) #define json_null nullptr #else #define json_null 0 #endif #if defined(__clang__) #pragma clang diagnostic push /* we do one big allocation via malloc, then cast aligned slices of this for. */ /* our structures - we don't have a way to tell the compiler we know what we. */ /* are doing, so disable the warning instead! */ #pragma clang diagnostic ignored "-Wcast-align" /* We use C style casts everywhere. */ #pragma clang diagnostic ignored "-Wold-style-cast" /* We need long long for strtoull. */ #pragma clang diagnostic ignored "-Wc++11-long-long" /* Who cares if nullptr doesn't work with C++98, we don't use it there! */ #pragma clang diagnostic ignored "-Wc++98-compat" #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" #elif defined(_MSC_VER) #pragma warning(push) /* disable 'function selected for inline expansion' warning. */ #pragma warning(disable : 4711) /* disable '#pragma warning: there is no warning number' warning. */ #pragma warning(disable : 4619) /* disable 'warning number not a valid compiler warning' warning. */ #pragma warning(disable : 4616) /* disable 'Compiler will insert Spectre mitigation for memory load if * /Qspectre. */ /* switch specified' warning. */ #pragma warning(disable : 5045) #endif struct json_parse_state_s { const char *src; size_t size; size_t offset; size_t flags_bitset; char *data; char *dom; size_t dom_size; size_t data_size; size_t line_no; /* line counter for error reporting. */ size_t line_offset; /* (offset-line_offset) is the character number (in bytes). */ size_t error; }; json_weak int json_hexadecimal_digit(const char c); int json_hexadecimal_digit(const char c) { if ('0' <= c && c <= '9') { return c - '0'; } if ('a' <= c && c <= 'f') { return c - 'a' + 10; } if ('A' <= c && c <= 'F') { return c - 'A' + 10; } return -1; } json_weak int json_hexadecimal_value(const char *c, const unsigned long size, unsigned long *result); int json_hexadecimal_value(const char *c, const unsigned long size, unsigned long *result) { const char *p; int digit; if (size > sizeof(unsigned long) * 2) { return 0; } *result = 0; for (p = c; (unsigned long)(p - c) < size; ++p) { *result <<= 4; digit = json_hexadecimal_digit(*p); if (digit < 0 || digit > 15) { return 0; } *result |= (unsigned char)digit; } return 1; } json_weak int json_skip_whitespace(struct json_parse_state_s *state); int json_skip_whitespace(struct json_parse_state_s *state) { size_t offset = state->offset; const size_t size = state->size; const char *const src = state->src; /* the only valid whitespace according to ECMA-404 is ' ', '\n', '\r' and * '\t'. */ switch (src[offset]) { default: return 0; case ' ': case '\r': case '\t': case '\n': break; } do { switch (src[offset]) { default: /* Update offset. */ state->offset = offset; return 1; case ' ': case '\r': case '\t': break; case '\n': state->line_no++; state->line_offset = offset; break; } offset++; } while (offset < size); /* Update offset. */ state->offset = offset; return 1; } json_weak int json_skip_c_style_comments(struct json_parse_state_s *state); int json_skip_c_style_comments(struct json_parse_state_s *state) { /* do we have a comment?. */ if ('/' == state->src[state->offset]) { /* skip '/'. */ state->offset++; if ('/' == state->src[state->offset]) { /* we had a comment of the form //. */ /* skip second '/'. */ state->offset++; while (state->offset < state->size) { switch (state->src[state->offset]) { default: /* skip the character in the comment. */ state->offset++; break; case '\n': /* if we have a newline, our comment has ended! Skip the newline. */ state->offset++; /* we entered a newline, so move our line info forward. */ state->line_no++; state->line_offset = state->offset; return 1; } } /* we reached the end of the JSON file! */ return 1; } else if ('*' == state->src[state->offset]) { /* we had a comment in the C-style long form. */ /* skip '*'. */ state->offset++; while (state->offset + 1 < state->size) { if (('*' == state->src[state->offset]) && ('/' == state->src[state->offset + 1])) { /* we reached the end of our comment! */ state->offset += 2; return 1; } else if ('\n' == state->src[state->offset]) { /* we entered a newline, so move our line info forward. */ state->line_no++; state->line_offset = state->offset; } /* skip character within comment. */ state->offset++; } /* Comment wasn't ended correctly which is a failure. */ return 1; } } /* we didn't have any comment, which is ok too! */ return 0; } json_weak int json_skip_all_skippables(struct json_parse_state_s *state); int json_skip_all_skippables(struct json_parse_state_s *state) { /* skip all whitespace and other skippables until there are none left. note * that the previous version suffered from read past errors should. the * stream end on json_skip_c_style_comments eg. '{"a" ' with comments flag. */ int did_consume = 0; const size_t size = state->size; if (json_parse_flags_allow_c_style_comments & state->flags_bitset) { do { if (state->offset == size) { state->error = json_parse_error_premature_end_of_buffer; return 1; } did_consume = json_skip_whitespace(state); /* This should really be checked on access, not in front of every call. */ if (state->offset == size) { state->error = json_parse_error_premature_end_of_buffer; return 1; } did_consume |= json_skip_c_style_comments(state); } while (0 != did_consume); } else { do { if (state->offset == size) { state->error = json_parse_error_premature_end_of_buffer; return 1; } did_consume = json_skip_whitespace(state); } while (0 != did_consume); } if (state->offset == size) { state->error = json_parse_error_premature_end_of_buffer; return 1; } return 0; } json_weak int json_get_value_size(struct json_parse_state_s *state, int is_global_object); json_weak int json_get_string_size(struct json_parse_state_s *state, size_t is_key); int json_get_string_size(struct json_parse_state_s *state, size_t is_key) { size_t offset = state->offset; const size_t size = state->size; size_t data_size = 0; const char *const src = state->src; const int is_single_quote = '\'' == src[offset]; const char quote_to_use = is_single_quote ? '\'' : '"'; const size_t flags_bitset = state->flags_bitset; unsigned long codepoint; unsigned long high_surrogate = 0; if ((json_parse_flags_allow_location_information & flags_bitset) != 0 && is_key != 0) { state->dom_size += sizeof(struct json_string_ex_s); } else { state->dom_size += sizeof(struct json_string_s); } if ('"' != src[offset]) { /* if we are allowed single quoted strings check for that too. */ if (!((json_parse_flags_allow_single_quoted_strings & flags_bitset) && is_single_quote)) { state->error = json_parse_error_expected_opening_quote; state->offset = offset; return 1; } } /* skip leading '"' or '\''. */ offset++; while ((offset < size) && (quote_to_use != src[offset])) { /* add space for the character. */ data_size++; switch (src[offset]) { default: break; case '\0': case '\t': state->error = json_parse_error_invalid_string; state->offset = offset; return 1; } if ('\\' == src[offset]) { /* skip reverse solidus character. */ offset++; if (offset == size) { state->error = json_parse_error_premature_end_of_buffer; state->offset = offset; return 1; } switch (src[offset]) { default: state->error = json_parse_error_invalid_string_escape_sequence; state->offset = offset; return 1; case '"': case '\\': case '/': case 'b': case 'f': case 'n': case 'r': case 't': /* all valid characters! */ offset++; break; case 'u': if (!(offset + 5 < size)) { /* invalid escaped unicode sequence! */ state->error = json_parse_error_invalid_string_escape_sequence; state->offset = offset; return 1; } codepoint = 0; if (!json_hexadecimal_value(&src[offset + 1], 4, &codepoint)) { /* escaped unicode sequences must contain 4 hexadecimal digits! */ state->error = json_parse_error_invalid_string_escape_sequence; state->offset = offset; return 1; } /* Valid sequence! * see: https://en.wikipedia.org/wiki/UTF-8#Invalid_code_points. * 1 7 U + 0000 U + 007F 0xxxxxxx. * 2 11 U + 0080 U + 07FF 110xxxxx * 10xxxxxx. * 3 16 U + 0800 U + FFFF 1110xxxx * 10xxxxxx 10xxxxxx. * 4 21 U + 10000 U + 10FFFF 11110xxx * 10xxxxxx 10xxxxxx 10xxxxxx. * Note: the high and low surrogate halves used by UTF-16 (U+D800 * through U+DFFF) and code points not encodable by UTF-16 (those after * U+10FFFF) are not legal Unicode values, and their UTF-8 encoding must * be treated as an invalid byte sequence. */ if (high_surrogate != 0) { /* we previously read the high half of the \uxxxx\uxxxx pair, so now * we expect the low half. */ if (codepoint >= 0xdc00 && codepoint <= 0xdfff) { /* low surrogate range. */ data_size += 3; high_surrogate = 0; } else { state->error = json_parse_error_invalid_string_escape_sequence; state->offset = offset; return 1; } } else if (codepoint <= 0x7f) { data_size += 0; } else if (codepoint <= 0x7ff) { data_size += 1; } else if (codepoint >= 0xd800 && codepoint <= 0xdbff) { /* high surrogate range. */ /* The codepoint is the first half of a "utf-16 surrogate pair". so we * need the other half for it to be valid: \uHHHH\uLLLL. */ if (offset + 11 > size || '\\' != src[offset + 5] || 'u' != src[offset + 6]) { state->error = json_parse_error_invalid_string_escape_sequence; state->offset = offset; return 1; } high_surrogate = codepoint; } else if (codepoint >= 0xd800 && codepoint <= 0xdfff) { /* low surrogate range. */ /* we did not read the other half before. */ state->error = json_parse_error_invalid_string_escape_sequence; state->offset = offset; return 1; } else { data_size += 2; } /* escaped codepoints after 0xffff are supported in json through utf-16 * surrogate pairs: \uD83D\uDD25 for U+1F525. */ offset += 5; break; } } else if (('\r' == src[offset]) || ('\n' == src[offset])) { if (!(json_parse_flags_allow_multi_line_strings & flags_bitset)) { /* invalid escaped unicode sequence! */ state->error = json_parse_error_invalid_string_escape_sequence; state->offset = offset; return 1; } offset++; } else { /* skip character (valid part of sequence). */ offset++; } } /* If the offset is equal to the size, we had a non-terminated string! */ if (offset == size) { state->error = json_parse_error_premature_end_of_buffer; state->offset = offset - 1; return 1; } /* skip trailing '"' or '\''. */ offset++; /* add enough space to store the string. */ state->data_size += data_size; /* one more byte for null terminator ending the string! */ state->data_size++; /* update offset. */ state->offset = offset; return 0; } json_weak int is_valid_unquoted_key_char(const char c); int is_valid_unquoted_key_char(const char c) { return (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('_' == c)); } json_weak int json_get_key_size(struct json_parse_state_s *state); int json_get_key_size(struct json_parse_state_s *state) { const size_t flags_bitset = state->flags_bitset; if (json_parse_flags_allow_unquoted_keys & flags_bitset) { size_t offset = state->offset; const size_t size = state->size; const char *const src = state->src; size_t data_size = state->data_size; /* if we are allowing unquoted keys, first grok for a quote... */ if ('"' == src[offset]) { /* ... if we got a comma, just parse the key as a string as normal. */ return json_get_string_size(state, 1); } else if ((json_parse_flags_allow_single_quoted_strings & flags_bitset) && ('\'' == src[offset])) { /* ... if we got a comma, just parse the key as a string as normal. */ return json_get_string_size(state, 1); } else { while ((offset < size) && is_valid_unquoted_key_char(src[offset])) { offset++; data_size++; } /* one more byte for null terminator ending the string! */ data_size++; if (json_parse_flags_allow_location_information & flags_bitset) { state->dom_size += sizeof(struct json_string_ex_s); } else { state->dom_size += sizeof(struct json_string_s); } /* update offset. */ state->offset = offset; /* update data_size. */ state->data_size = data_size; return 0; } } else { /* we are only allowed to have quoted keys, so just parse a string! */ return json_get_string_size(state, 1); } } json_weak int json_get_object_size(struct json_parse_state_s *state, int is_global_object); int json_get_object_size(struct json_parse_state_s *state, int is_global_object) { const size_t flags_bitset = state->flags_bitset; const char *const src = state->src; const size_t size = state->size; size_t elements = 0; int allow_comma = 0; int found_closing_brace = 0; if (is_global_object) { /* if we found an opening '{' of an object, we actually have a normal JSON * object at the root of the DOM... */ if (!json_skip_all_skippables(state) && '{' == state->src[state->offset]) { /* . and we don't actually have a global object after all! */ is_global_object = 0; } } if (!is_global_object) { if ('{' != src[state->offset]) { state->error = json_parse_error_unknown; return 1; } /* skip leading '{'. */ state->offset++; } state->dom_size += sizeof(struct json_object_s); if ((state->offset == size) && !is_global_object) { state->error = json_parse_error_premature_end_of_buffer; return 1; } do { if (!is_global_object) { if (json_skip_all_skippables(state)) { state->error = json_parse_error_premature_end_of_buffer; return 1; } if ('}' == src[state->offset]) { /* skip trailing '}'. */ state->offset++; found_closing_brace = 1; /* finished the object! */ break; } } else { /* we don't require brackets, so that means the object ends when the input * stream ends! */ if (json_skip_all_skippables(state)) { break; } } /* if we parsed at least once element previously, grok for a comma. */ if (allow_comma) { if (',' == src[state->offset]) { /* skip comma. */ state->offset++; allow_comma = 0; } else if (json_parse_flags_allow_no_commas & flags_bitset) { /* we don't require a comma, and we didn't find one, which is ok! */ allow_comma = 0; } else { /* otherwise we are required to have a comma, and we found none. */ state->error = json_parse_error_expected_comma_or_closing_bracket; return 1; } if (json_parse_flags_allow_trailing_comma & flags_bitset) { continue; } else { if (json_skip_all_skippables(state)) { state->error = json_parse_error_premature_end_of_buffer; return 1; } } } if (json_get_key_size(state)) { /* key parsing failed! */ state->error = json_parse_error_invalid_string; return 1; } if (json_skip_all_skippables(state)) { state->error = json_parse_error_premature_end_of_buffer; return 1; } if (json_parse_flags_allow_equals_in_object & flags_bitset) { const char current = src[state->offset]; if ((':' != current) && ('=' != current)) { state->error = json_parse_error_expected_colon; return 1; } } else { if (':' != src[state->offset]) { state->error = json_parse_error_expected_colon; return 1; } } /* skip colon. */ state->offset++; if (json_skip_all_skippables(state)) { state->error = json_parse_error_premature_end_of_buffer; return 1; } if (json_get_value_size(state, /* is_global_object = */ 0)) { /* value parsing failed! */ return 1; } /* successfully parsed a name/value pair! */ elements++; allow_comma = 1; } while (state->offset < size); if ((state->offset == size) && !is_global_object && !found_closing_brace) { state->error = json_parse_error_premature_end_of_buffer; return 1; } state->dom_size += sizeof(struct json_object_element_s) * elements; return 0; } json_weak int json_get_array_size(struct json_parse_state_s *state); int json_get_array_size(struct json_parse_state_s *state) { const size_t flags_bitset = state->flags_bitset; size_t elements = 0; int allow_comma = 0; const char *const src = state->src; const size_t size = state->size; if ('[' != src[state->offset]) { /* expected array to begin with leading '['. */ state->error = json_parse_error_unknown; return 1; } /* skip leading '['. */ state->offset++; state->dom_size += sizeof(struct json_array_s); while (state->offset < size) { if (json_skip_all_skippables(state)) { state->error = json_parse_error_premature_end_of_buffer; return 1; } if (']' == src[state->offset]) { /* skip trailing ']'. */ state->offset++; state->dom_size += sizeof(struct json_array_element_s) * elements; /* finished the object! */ return 0; } /* if we parsed at least once element previously, grok for a comma. */ if (allow_comma) { if (',' == src[state->offset]) { /* skip comma. */ state->offset++; allow_comma = 0; } else if (!(json_parse_flags_allow_no_commas & flags_bitset)) { state->error = json_parse_error_expected_comma_or_closing_bracket; return 1; } if (json_parse_flags_allow_trailing_comma & flags_bitset) { allow_comma = 0; continue; } else { if (json_skip_all_skippables(state)) { state->error = json_parse_error_premature_end_of_buffer; return 1; } } } if (json_get_value_size(state, /* is_global_object = */ 0)) { /* value parsing failed! */ return 1; } /* successfully parsed an array element! */ elements++; allow_comma = 1; } /* we consumed the entire input before finding the closing ']' of the array! */ state->error = json_parse_error_premature_end_of_buffer; return 1; } json_weak int json_get_number_size(struct json_parse_state_s *state); int json_get_number_size(struct json_parse_state_s *state) { const size_t flags_bitset = state->flags_bitset; size_t offset = state->offset; const size_t size = state->size; int had_leading_digits = 0; const char *const src = state->src; state->dom_size += sizeof(struct json_number_s); if ((json_parse_flags_allow_hexadecimal_numbers & flags_bitset) && (offset + 1 < size) && ('0' == src[offset]) && (('x' == src[offset + 1]) || ('X' == src[offset + 1]))) { /* skip the leading 0x that identifies a hexadecimal number. */ offset += 2; /* consume hexadecimal digits. */ while ((offset < size) && (('0' <= src[offset] && src[offset] <= '9') || ('a' <= src[offset] && src[offset] <= 'f') || ('A' <= src[offset] && src[offset] <= 'F'))) { offset++; } } else { int found_sign = 0; int inf_or_nan = 0; if ((offset < size) && (('-' == src[offset]) || ((json_parse_flags_allow_leading_plus_sign & flags_bitset) && ('+' == src[offset])))) { /* skip valid leading '-' or '+'. */ offset++; found_sign = 1; } if (json_parse_flags_allow_inf_and_nan & flags_bitset) { const char inf[9] = "Infinity"; const size_t inf_strlen = sizeof(inf) - 1; const char nan[4] = "NaN"; const size_t nan_strlen = sizeof(nan) - 1; if (offset + inf_strlen < size) { int found = 1; size_t i; for (i = 0; i < inf_strlen; i++) { if (inf[i] != src[offset + i]) { found = 0; break; } } if (found) { /* We found our special 'Infinity' keyword! */ offset += inf_strlen; inf_or_nan = 1; } } if (offset + nan_strlen < size) { int found = 1; size_t i; for (i = 0; i < nan_strlen; i++) { if (nan[i] != src[offset + i]) { found = 0; break; } } if (found) { /* We found our special 'NaN' keyword! */ offset += nan_strlen; inf_or_nan = 1; } } } if (found_sign && !inf_or_nan && (offset < size) && !('0' <= src[offset] && src[offset] <= '9')) { /* check if we are allowing leading '.'. */ if (!(json_parse_flags_allow_leading_or_trailing_decimal_point & flags_bitset) || ('.' != src[offset])) { /* a leading '-' must be immediately followed by any digit! */ state->error = json_parse_error_invalid_number_format; state->offset = offset; return 1; } } if ((offset < size) && ('0' == src[offset])) { /* skip valid '0'. */ offset++; /* we need to record whether we had any leading digits for checks later. */ had_leading_digits = 1; if ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) { /* a leading '0' must not be immediately followed by any digit! */ state->error = json_parse_error_invalid_number_format; state->offset = offset; return 1; } } /* the main digits of our number next. */ while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) { offset++; /* we need to record whether we had any leading digits for checks later. */ had_leading_digits = 1; } if ((offset < size) && ('.' == src[offset])) { offset++; if (!('0' <= src[offset] && src[offset] <= '9')) { if (!(json_parse_flags_allow_leading_or_trailing_decimal_point & flags_bitset) || !had_leading_digits) { /* a decimal point must be followed by at least one digit. */ state->error = json_parse_error_invalid_number_format; state->offset = offset; return 1; } } /* a decimal point can be followed by more digits of course! */ while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) { offset++; } } if ((offset < size) && ('e' == src[offset] || 'E' == src[offset])) { /* our number has an exponent! Skip 'e' or 'E'. */ offset++; if ((offset < size) && ('-' == src[offset] || '+' == src[offset])) { /* skip optional '-' or '+'. */ offset++; } if ((offset < size) && !('0' <= src[offset] && src[offset] <= '9')) { /* an exponent must have at least one digit! */ state->error = json_parse_error_invalid_number_format; state->offset = offset; return 1; } /* consume exponent digits. */ do { offset++; } while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')); } } if (offset < size) { switch (src[offset]) { case ' ': case '\t': case '\r': case '\n': case '}': case ',': case ']': /* all of the above are ok. */ break; case '=': if (json_parse_flags_allow_equals_in_object & flags_bitset) { break; } state->error = json_parse_error_invalid_number_format; state->offset = offset; return 1; default: state->error = json_parse_error_invalid_number_format; state->offset = offset; return 1; } } state->data_size += offset - state->offset; /* one more byte for null terminator ending the number string! */ state->data_size++; /* update offset. */ state->offset = offset; return 0; } json_weak int json_get_value_size(struct json_parse_state_s *state, int is_global_object); int json_get_value_size(struct json_parse_state_s *state, int is_global_object) { const size_t flags_bitset = state->flags_bitset; const char *const src = state->src; size_t offset; const size_t size = state->size; if (json_parse_flags_allow_location_information & flags_bitset) { state->dom_size += sizeof(struct json_value_ex_s); } else { state->dom_size += sizeof(struct json_value_s); } if (is_global_object) { return json_get_object_size(state, /* is_global_object = */ 1); } else { if (json_skip_all_skippables(state)) { state->error = json_parse_error_premature_end_of_buffer; return 1; } /* can cache offset now. */ offset = state->offset; switch (src[offset]) { case '"': return json_get_string_size(state, 0); case '\'': if (json_parse_flags_allow_single_quoted_strings & flags_bitset) { return json_get_string_size(state, 0); } else { /* invalid value! */ state->error = json_parse_error_invalid_value; return 1; } case '{': return json_get_object_size(state, /* is_global_object = */ 0); case '[': return json_get_array_size(state); case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return json_get_number_size(state); case '+': if (json_parse_flags_allow_leading_plus_sign & flags_bitset) { return json_get_number_size(state); } else { /* invalid value! */ state->error = json_parse_error_invalid_number_format; return 1; } case '.': if (json_parse_flags_allow_leading_or_trailing_decimal_point & flags_bitset) { return json_get_number_size(state); } else { /* invalid value! */ state->error = json_parse_error_invalid_number_format; return 1; } default: if ((offset + 4) <= size && 't' == src[offset + 0] && 'r' == src[offset + 1] && 'u' == src[offset + 2] && 'e' == src[offset + 3]) { state->offset += 4; return 0; } else if ((offset + 5) <= size && 'f' == src[offset + 0] && 'a' == src[offset + 1] && 'l' == src[offset + 2] && 's' == src[offset + 3] && 'e' == src[offset + 4]) { state->offset += 5; return 0; } else if ((offset + 4) <= size && 'n' == state->src[offset + 0] && 'u' == state->src[offset + 1] && 'l' == state->src[offset + 2] && 'l' == state->src[offset + 3]) { state->offset += 4; return 0; } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && (offset + 3) <= size && 'N' == src[offset + 0] && 'a' == src[offset + 1] && 'N' == src[offset + 2]) { return json_get_number_size(state); } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && (offset + 8) <= size && 'I' == src[offset + 0] && 'n' == src[offset + 1] && 'f' == src[offset + 2] && 'i' == src[offset + 3] && 'n' == src[offset + 4] && 'i' == src[offset + 5] && 't' == src[offset + 6] && 'y' == src[offset + 7]) { return json_get_number_size(state); } /* invalid value! */ state->error = json_parse_error_invalid_value; return 1; } } } json_weak void json_parse_value(struct json_parse_state_s *state, int is_global_object, struct json_value_s *value); json_weak void json_parse_string(struct json_parse_state_s *state, struct json_string_s *string); void json_parse_string(struct json_parse_state_s *state, struct json_string_s *string) { size_t offset = state->offset; size_t bytes_written = 0; const char *const src = state->src; const char quote_to_use = '\'' == src[offset] ? '\'' : '"'; char *data = state->data; unsigned long high_surrogate = 0; unsigned long codepoint; string->string = data; /* skip leading '"' or '\''. */ offset++; while (quote_to_use != src[offset]) { if ('\\' == src[offset]) { /* skip the reverse solidus. */ offset++; switch (src[offset++]) { default: return; /* we cannot ever reach here. */ case 'u': { codepoint = 0; if (!json_hexadecimal_value(&src[offset], 4, &codepoint)) { return; /* this shouldn't happen as the value was already validated. */ } offset += 4; if (codepoint <= 0x7fu) { data[bytes_written++] = (char)codepoint; /* 0xxxxxxx. */ } else if (codepoint <= 0x7ffu) { data[bytes_written++] = (char)(0xc0u | (codepoint >> 6)); /* 110xxxxx. */ data[bytes_written++] = (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */ } else if (codepoint >= 0xd800 && codepoint <= 0xdbff) { /* high surrogate. */ high_surrogate = codepoint; continue; /* we need the low half to form a complete codepoint. */ } else if (codepoint >= 0xdc00 && codepoint <= 0xdfff) { /* low surrogate. */ /* combine with the previously read half to obtain the complete * codepoint. */ const unsigned long surrogate_offset = 0x10000u - (0xD800u << 10) - 0xDC00u; codepoint = (high_surrogate << 10) + codepoint + surrogate_offset; high_surrogate = 0; data[bytes_written++] = (char)(0xF0u | (codepoint >> 18)); /* 11110xxx. */ data[bytes_written++] = (char)(0x80u | ((codepoint >> 12) & 0x3fu)); /* 10xxxxxx. */ data[bytes_written++] = (char)(0x80u | ((codepoint >> 6) & 0x3fu)); /* 10xxxxxx. */ data[bytes_written++] = (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */ } else { /* we assume the value was validated and thus is within the valid * range. */ data[bytes_written++] = (char)(0xe0u | (codepoint >> 12)); /* 1110xxxx. */ data[bytes_written++] = (char)(0x80u | ((codepoint >> 6) & 0x3fu)); /* 10xxxxxx. */ data[bytes_written++] = (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */ } } break; case '"': data[bytes_written++] = '"'; break; case '\\': data[bytes_written++] = '\\'; break; case '/': data[bytes_written++] = '/'; break; case 'b': data[bytes_written++] = '\b'; break; case 'f': data[bytes_written++] = '\f'; break; case 'n': data[bytes_written++] = '\n'; break; case 'r': data[bytes_written++] = '\r'; break; case 't': data[bytes_written++] = '\t'; break; case '\r': data[bytes_written++] = '\r'; /* check if we have a "\r\n" sequence. */ if ('\n' == src[offset]) { data[bytes_written++] = '\n'; offset++; } break; case '\n': data[bytes_written++] = '\n'; break; } } else { /* copy the character. */ data[bytes_written++] = src[offset++]; } } /* skip trailing '"' or '\''. */ offset++; /* record the size of the string. */ string->string_size = bytes_written; /* add null terminator to string. */ data[bytes_written++] = '\0'; /* move data along. */ state->data += bytes_written; /* update offset. */ state->offset = offset; } json_weak void json_parse_key(struct json_parse_state_s *state, struct json_string_s *string); void json_parse_key(struct json_parse_state_s *state, struct json_string_s *string) { if (json_parse_flags_allow_unquoted_keys & state->flags_bitset) { const char *const src = state->src; char *const data = state->data; size_t offset = state->offset; /* if we are allowing unquoted keys, check for quoted anyway... */ if (('"' == src[offset]) || ('\'' == src[offset])) { /* ... if we got a quote, just parse the key as a string as normal. */ json_parse_string(state, string); } else { size_t size = 0; string->string = state->data; while (is_valid_unquoted_key_char(src[offset])) { data[size++] = src[offset++]; } /* add null terminator to string. */ data[size] = '\0'; /* record the size of the string. */ string->string_size = size++; /* move data along. */ state->data += size; /* update offset. */ state->offset = offset; } } else { /* we are only allowed to have quoted keys, so just parse a string! */ json_parse_string(state, string); } } json_weak void json_parse_object(struct json_parse_state_s *state, int is_global_object, struct json_object_s *object); void json_parse_object(struct json_parse_state_s *state, int is_global_object, struct json_object_s *object) { const size_t flags_bitset = state->flags_bitset; const size_t size = state->size; const char *const src = state->src; size_t elements = 0; int allow_comma = 0; struct json_object_element_s *previous = json_null; if (is_global_object) { /* if we skipped some whitespace, and then found an opening '{' of an. */ /* object, we actually have a normal JSON object at the root of the DOM... */ if ('{' == src[state->offset]) { /* . and we don't actually have a global object after all! */ is_global_object = 0; } } if (!is_global_object) { /* skip leading '{'. */ state->offset++; } (void)json_skip_all_skippables(state); /* reset elements. */ elements = 0; while (state->offset < size) { struct json_object_element_s *element = json_null; struct json_string_s *string = json_null; struct json_value_s *value = json_null; if (!is_global_object) { (void)json_skip_all_skippables(state); if ('}' == src[state->offset]) { /* skip trailing '}'. */ state->offset++; /* finished the object! */ break; } } else { if (json_skip_all_skippables(state)) { /* global object ends when the file ends! */ break; } } /* if we parsed at least one element previously, grok for a comma. */ if (allow_comma) { if (',' == src[state->offset]) { /* skip comma. */ state->offset++; allow_comma = 0; continue; } } element = (struct json_object_element_s *)state->dom; state->dom += sizeof(struct json_object_element_s); if (json_null == previous) { /* this is our first element, so record it in our object. */ object->start = element; } else { previous->next = element; } previous = element; if (json_parse_flags_allow_location_information & flags_bitset) { struct json_string_ex_s *string_ex = (struct json_string_ex_s *)state->dom; state->dom += sizeof(struct json_string_ex_s); string_ex->offset = state->offset; string_ex->line_no = state->line_no; string_ex->row_no = state->offset - state->line_offset; string = &(string_ex->string); } else { string = (struct json_string_s *)state->dom; state->dom += sizeof(struct json_string_s); } element->name = string; (void)json_parse_key(state, string); (void)json_skip_all_skippables(state); /* skip colon or equals. */ state->offset++; (void)json_skip_all_skippables(state); if (json_parse_flags_allow_location_information & flags_bitset) { struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom; state->dom += sizeof(struct json_value_ex_s); value_ex->offset = state->offset; value_ex->line_no = state->line_no; value_ex->row_no = state->offset - state->line_offset; value = &(value_ex->value); } else { value = (struct json_value_s *)state->dom; state->dom += sizeof(struct json_value_s); } element->value = value; json_parse_value(state, /* is_global_object = */ 0, value); /* successfully parsed a name/value pair! */ elements++; allow_comma = 1; } /* if we had at least one element, end the linked list. */ if (previous) { previous->next = json_null; } if (0 == elements) { object->start = json_null; } object->length = elements; } json_weak void json_parse_array(struct json_parse_state_s *state, struct json_array_s *array); void json_parse_array(struct json_parse_state_s *state, struct json_array_s *array) { const char *const src = state->src; const size_t size = state->size; size_t elements = 0; int allow_comma = 0; struct json_array_element_s *previous = json_null; /* skip leading '['. */ state->offset++; (void)json_skip_all_skippables(state); /* reset elements. */ elements = 0; do { struct json_array_element_s *element = json_null; struct json_value_s *value = json_null; (void)json_skip_all_skippables(state); if (']' == src[state->offset]) { /* skip trailing ']'. */ state->offset++; /* finished the array! */ break; } /* if we parsed at least one element previously, grok for a comma. */ if (allow_comma) { if (',' == src[state->offset]) { /* skip comma. */ state->offset++; allow_comma = 0; continue; } } element = (struct json_array_element_s *)state->dom; state->dom += sizeof(struct json_array_element_s); if (json_null == previous) { /* this is our first element, so record it in our array. */ array->start = element; } else { previous->next = element; } previous = element; if (json_parse_flags_allow_location_information & state->flags_bitset) { struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom; state->dom += sizeof(struct json_value_ex_s); value_ex->offset = state->offset; value_ex->line_no = state->line_no; value_ex->row_no = state->offset - state->line_offset; value = &(value_ex->value); } else { value = (struct json_value_s *)state->dom; state->dom += sizeof(struct json_value_s); } element->value = value; json_parse_value(state, /* is_global_object = */ 0, value); /* successfully parsed an array element! */ elements++; allow_comma = 1; } while (state->offset < size); /* end the linked list. */ if (previous) { previous->next = json_null; } if (0 == elements) { array->start = json_null; } array->length = elements; } json_weak void json_parse_number(struct json_parse_state_s *state, struct json_number_s *number); void json_parse_number(struct json_parse_state_s *state, struct json_number_s *number) { const size_t flags_bitset = state->flags_bitset; size_t offset = state->offset; const size_t size = state->size; size_t bytes_written = 0; const char *const src = state->src; char *data = state->data; number->number = data; if (json_parse_flags_allow_hexadecimal_numbers & flags_bitset) { if (('0' == src[offset]) && (('x' == src[offset + 1]) || ('X' == src[offset + 1]))) { /* consume hexadecimal digits. */ while ((offset < size) && (('0' <= src[offset] && src[offset] <= '9') || ('a' <= src[offset] && src[offset] <= 'f') || ('A' <= src[offset] && src[offset] <= 'F') || ('x' == src[offset]) || ('X' == src[offset]))) { data[bytes_written++] = src[offset++]; } } } while (offset < size) { int end = 0; switch (src[offset]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': case 'e': case 'E': case '+': case '-': data[bytes_written++] = src[offset++]; break; default: end = 1; break; } if (0 != end) { break; } } if (json_parse_flags_allow_inf_and_nan & flags_bitset) { const size_t inf_strlen = 8; /* = strlen("Infinity");. */ const size_t nan_strlen = 3; /* = strlen("NaN");. */ if (offset + inf_strlen < size) { if ('I' == src[offset]) { size_t i; /* We found our special 'Infinity' keyword! */ for (i = 0; i < inf_strlen; i++) { data[bytes_written++] = src[offset++]; } } } if (offset + nan_strlen < size) { if ('N' == src[offset]) { size_t i; /* We found our special 'NaN' keyword! */ for (i = 0; i < nan_strlen; i++) { data[bytes_written++] = src[offset++]; } } } } /* record the size of the number. */ number->number_size = bytes_written; /* add null terminator to number string. */ data[bytes_written++] = '\0'; /* move data along. */ state->data += bytes_written; /* update offset. */ state->offset = offset; } json_weak void json_parse_value(struct json_parse_state_s *state, int is_global_object, struct json_value_s *value); void json_parse_value(struct json_parse_state_s *state, int is_global_object, struct json_value_s *value) { const size_t flags_bitset = state->flags_bitset; const char *const src = state->src; const size_t size = state->size; size_t offset; (void)json_skip_all_skippables(state); /* cache offset now. */ offset = state->offset; if (is_global_object) { value->type = json_type_object; value->payload = state->dom; state->dom += sizeof(struct json_object_s); json_parse_object(state, /* is_global_object = */ 1, (struct json_object_s *)value->payload); } else { switch (src[offset]) { case '"': case '\'': value->type = json_type_string; value->payload = state->dom; state->dom += sizeof(struct json_string_s); json_parse_string(state, (struct json_string_s *)value->payload); break; case '{': value->type = json_type_object; value->payload = state->dom; state->dom += sizeof(struct json_object_s); json_parse_object(state, /* is_global_object = */ 0, (struct json_object_s *)value->payload); break; case '[': value->type = json_type_array; value->payload = state->dom; state->dom += sizeof(struct json_array_s); json_parse_array(state, (struct json_array_s *)value->payload); break; case '-': case '+': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': value->type = json_type_number; value->payload = state->dom; state->dom += sizeof(struct json_number_s); json_parse_number(state, (struct json_number_s *)value->payload); break; default: if ((offset + 4) <= size && 't' == src[offset + 0] && 'r' == src[offset + 1] && 'u' == src[offset + 2] && 'e' == src[offset + 3]) { value->type = json_type_true; value->payload = json_null; state->offset += 4; } else if ((offset + 5) <= size && 'f' == src[offset + 0] && 'a' == src[offset + 1] && 'l' == src[offset + 2] && 's' == src[offset + 3] && 'e' == src[offset + 4]) { value->type = json_type_false; value->payload = json_null; state->offset += 5; } else if ((offset + 4) <= size && 'n' == src[offset + 0] && 'u' == src[offset + 1] && 'l' == src[offset + 2] && 'l' == src[offset + 3]) { value->type = json_type_null; value->payload = json_null; state->offset += 4; } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && (offset + 3) <= size && 'N' == src[offset + 0] && 'a' == src[offset + 1] && 'N' == src[offset + 2]) { value->type = json_type_number; value->payload = state->dom; state->dom += sizeof(struct json_number_s); json_parse_number(state, (struct json_number_s *)value->payload); } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && (offset + 8) <= size && 'I' == src[offset + 0] && 'n' == src[offset + 1] && 'f' == src[offset + 2] && 'i' == src[offset + 3] && 'n' == src[offset + 4] && 'i' == src[offset + 5] && 't' == src[offset + 6] && 'y' == src[offset + 7]) { value->type = json_type_number; value->payload = state->dom; state->dom += sizeof(struct json_number_s); json_parse_number(state, (struct json_number_s *)value->payload); } break; } } } struct json_value_s * json_parse_ex(const void *src, size_t src_size, size_t flags_bitset, void *(*alloc_func_ptr)(void *user_data, size_t size), void *user_data, struct json_parse_result_s *result) { struct json_parse_state_s state; void *allocation; struct json_value_s *value; size_t total_size; int input_error; if (result) { result->error = json_parse_error_none; result->error_offset = 0; result->error_line_no = 0; result->error_row_no = 0; } if (json_null == src) { /* invalid src pointer was null! */ return json_null; } state.src = (const char *)src; state.size = src_size; state.offset = 0; state.line_no = 1; state.line_offset = 0; state.error = json_parse_error_none; state.dom_size = 0; state.data_size = 0; state.flags_bitset = flags_bitset; input_error = json_get_value_size( &state, (int)(json_parse_flags_allow_global_object & state.flags_bitset)); if (0 == input_error) { json_skip_all_skippables(&state); if (state.offset != state.size) { /* our parsing didn't have an error, but there are characters remaining in * the input that weren't part of the JSON! */ state.error = json_parse_error_unexpected_trailing_characters; input_error = 1; } } if (input_error) { /* parsing value's size failed (most likely an invalid JSON DOM!). */ if (result) { result->error = state.error; result->error_offset = state.offset; result->error_line_no = state.line_no; result->error_row_no = state.offset - state.line_offset; } return json_null; } /* our total allocation is the combination of the dom and data sizes (we. */ /* first encode the structure of the JSON, and then the data referenced by. */ /* the JSON values). */ total_size = state.dom_size + state.data_size; if (json_null == alloc_func_ptr) { allocation = malloc(total_size); } else { allocation = alloc_func_ptr(user_data, total_size); } if (json_null == allocation) { /* malloc failed! */ if (result) { result->error = json_parse_error_allocator_failed; result->error_offset = 0; result->error_line_no = 0; result->error_row_no = 0; } return json_null; } /* reset offset so we can reuse it. */ state.offset = 0; /* reset the line information so we can reuse it. */ state.line_no = 1; state.line_offset = 0; state.dom = (char *)allocation; state.data = state.dom + state.dom_size; if (json_parse_flags_allow_location_information & state.flags_bitset) { struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state.dom; state.dom += sizeof(struct json_value_ex_s); value_ex->offset = state.offset; value_ex->line_no = state.line_no; value_ex->row_no = state.offset - state.line_offset; value = &(value_ex->value); } else { value = (struct json_value_s *)state.dom; state.dom += sizeof(struct json_value_s); } json_parse_value( &state, (int)(json_parse_flags_allow_global_object & state.flags_bitset), value); return (struct json_value_s *)allocation; } struct json_value_s *json_parse(const void *src, size_t src_size) { return json_parse_ex(src, src_size, json_parse_flags_default, json_null, json_null, json_null); } struct json_extract_result_s { size_t dom_size; size_t data_size; }; struct json_value_s *json_extract_value(const struct json_value_s *value) { return json_extract_value_ex(value, json_null, json_null); } json_weak struct json_extract_result_s json_extract_get_number_size(const struct json_number_s *const number); json_weak struct json_extract_result_s json_extract_get_string_size(const struct json_string_s *const string); json_weak struct json_extract_result_s json_extract_get_object_size(const struct json_object_s *const object); json_weak struct json_extract_result_s json_extract_get_array_size(const struct json_array_s *const array); json_weak struct json_extract_result_s json_extract_get_value_size(const struct json_value_s *const value); struct json_extract_result_s json_extract_get_number_size(const struct json_number_s *const number) { struct json_extract_result_s result; result.dom_size = sizeof(struct json_number_s); result.data_size = number->number_size; return result; } struct json_extract_result_s json_extract_get_string_size(const struct json_string_s *const string) { struct json_extract_result_s result; result.dom_size = sizeof(struct json_string_s); result.data_size = string->string_size + 1; return result; } struct json_extract_result_s json_extract_get_object_size(const struct json_object_s *const object) { struct json_extract_result_s result; size_t i; const struct json_object_element_s *element = object->start; result.dom_size = sizeof(struct json_object_s) + (sizeof(struct json_object_element_s) * object->length); result.data_size = 0; for (i = 0; i < object->length; i++) { const struct json_extract_result_s string_result = json_extract_get_string_size(element->name); const struct json_extract_result_s value_result = json_extract_get_value_size(element->value); result.dom_size += string_result.dom_size; result.data_size += string_result.data_size; result.dom_size += value_result.dom_size; result.data_size += value_result.data_size; element = element->next; } return result; } struct json_extract_result_s json_extract_get_array_size(const struct json_array_s *const array) { struct json_extract_result_s result; size_t i; const struct json_array_element_s *element = array->start; result.dom_size = sizeof(struct json_array_s) + (sizeof(struct json_array_element_s) * array->length); result.data_size = 0; for (i = 0; i < array->length; i++) { const struct json_extract_result_s value_result = json_extract_get_value_size(element->value); result.dom_size += value_result.dom_size; result.data_size += value_result.data_size; element = element->next; } return result; } struct json_extract_result_s json_extract_get_value_size(const struct json_value_s *const value) { struct json_extract_result_s result = {0, 0}; switch (value->type) { default: break; case json_type_object: result = json_extract_get_object_size( (const struct json_object_s *)value->payload); break; case json_type_array: result = json_extract_get_array_size( (const struct json_array_s *)value->payload); break; case json_type_number: result = json_extract_get_number_size( (const struct json_number_s *)value->payload); break; case json_type_string: result = json_extract_get_string_size( (const struct json_string_s *)value->payload); break; } result.dom_size += sizeof(struct json_value_s); return result; } struct json_extract_state_s { char *dom; char *data; }; json_weak void json_extract_copy_value(struct json_extract_state_s *const state, const struct json_value_s *const value); void json_extract_copy_value(struct json_extract_state_s *const state, const struct json_value_s *const value) { struct json_string_s *string; struct json_number_s *number; struct json_object_s *object; struct json_array_s *array; struct json_value_s *new_value; memcpy(state->dom, value, sizeof(struct json_value_s)); new_value = (struct json_value_s *)state->dom; state->dom += sizeof(struct json_value_s); new_value->payload = state->dom; if (json_type_string == value->type) { memcpy(state->dom, value->payload, sizeof(struct json_string_s)); string = (struct json_string_s *)state->dom; state->dom += sizeof(struct json_string_s); memcpy(state->data, string->string, string->string_size + 1); string->string = state->data; state->data += string->string_size + 1; } else if (json_type_number == value->type) { memcpy(state->dom, value->payload, sizeof(struct json_number_s)); number = (struct json_number_s *)state->dom; state->dom += sizeof(struct json_number_s); memcpy(state->data, number->number, number->number_size); number->number = state->data; state->data += number->number_size; } else if (json_type_object == value->type) { struct json_object_element_s *element; size_t i; memcpy(state->dom, value->payload, sizeof(struct json_object_s)); object = (struct json_object_s *)state->dom; state->dom += sizeof(struct json_object_s); element = object->start; object->start = (struct json_object_element_s *)state->dom; for (i = 0; i < object->length; i++) { struct json_value_s *previous_value; struct json_object_element_s *previous_element; memcpy(state->dom, element, sizeof(struct json_object_element_s)); element = (struct json_object_element_s *)state->dom; state->dom += sizeof(struct json_object_element_s); string = element->name; memcpy(state->dom, string, sizeof(struct json_string_s)); string = (struct json_string_s *)state->dom; state->dom += sizeof(struct json_string_s); element->name = string; memcpy(state->data, string->string, string->string_size + 1); string->string = state->data; state->data += string->string_size + 1; previous_value = element->value; element->value = (struct json_value_s *)state->dom; json_extract_copy_value(state, previous_value); previous_element = element; element = element->next; if (element) { previous_element->next = (struct json_object_element_s *)state->dom; } } } else if (json_type_array == value->type) { struct json_array_element_s *element; size_t i; memcpy(state->dom, value->payload, sizeof(struct json_array_s)); array = (struct json_array_s *)state->dom; state->dom += sizeof(struct json_array_s); element = array->start; array->start = (struct json_array_element_s *)state->dom; for (i = 0; i < array->length; i++) { struct json_value_s *previous_value; struct json_array_element_s *previous_element; memcpy(state->dom, element, sizeof(struct json_array_element_s)); element = (struct json_array_element_s *)state->dom; state->dom += sizeof(struct json_array_element_s); previous_value = element->value; element->value = (struct json_value_s *)state->dom; json_extract_copy_value(state, previous_value); previous_element = element; element = element->next; if (element) { previous_element->next = (struct json_array_element_s *)state->dom; } } } } struct json_value_s *json_extract_value_ex(const struct json_value_s *value, void *(*alloc_func_ptr)(void *, size_t), void *user_data) { void *allocation; struct json_extract_result_s result; struct json_extract_state_s state; size_t total_size; if (json_null == value) { /* invalid value was null! */ return json_null; } result = json_extract_get_value_size(value); total_size = result.dom_size + result.data_size; if (json_null == alloc_func_ptr) { allocation = malloc(total_size); } else { allocation = alloc_func_ptr(user_data, total_size); } state.dom = (char *)allocation; state.data = state.dom + result.dom_size; json_extract_copy_value(&state, value); return (struct json_value_s *)allocation; } struct json_string_s *json_value_as_string(struct json_value_s *const value) { if (value->type != json_type_string) { return json_null; } return (struct json_string_s *)value->payload; } struct json_number_s *json_value_as_number(struct json_value_s *const value) { if (value->type != json_type_number) { return json_null; } return (struct json_number_s *)value->payload; } struct json_object_s *json_value_as_object(struct json_value_s *const value) { if (value->type != json_type_object) { return json_null; } return (struct json_object_s *)value->payload; } struct json_array_s *json_value_as_array(struct json_value_s *const value) { if (value->type != json_type_array) { return json_null; } return (struct json_array_s *)value->payload; } int json_value_is_true(const struct json_value_s *const value) { return value->type == json_type_true; } int json_value_is_false(const struct json_value_s *const value) { return value->type == json_type_false; } int json_value_is_null(const struct json_value_s *const value) { return value->type == json_type_null; } json_weak int json_write_minified_get_value_size(const struct json_value_s *value, size_t *size); json_weak int json_write_get_number_size(const struct json_number_s *number, size_t *size); int json_write_get_number_size(const struct json_number_s *number, size_t *size) { json_uintmax_t parsed_number; size_t i; if (number->number_size >= 2) { switch (number->number[1]) { default: break; case 'x': case 'X': /* the number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal * so we have to do extra work to convert it to a non-hexadecimal for JSON * output. */ parsed_number = json_strtoumax(number->number, json_null, 0); i = 0; while (0 != parsed_number) { parsed_number /= 10; i++; } *size += i; return 0; } } /* check to see if the number has leading/trailing decimal point. */ i = 0; /* skip any leading '+' or '-'. */ if ((i < number->number_size) && (('+' == number->number[i]) || ('-' == number->number[i]))) { i++; } /* check if we have infinity. */ if ((i < number->number_size) && ('I' == number->number[i])) { const char *inf = "Infinity"; size_t k; for (k = i; k < number->number_size; k++) { const char c = *inf++; /* Check if we found the Infinity string! */ if ('\0' == c) { break; } else if (c != number->number[k]) { break; } } if ('\0' == *inf) { /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */ *size += 22; /* if we had a leading '-' we need to record it in the JSON output. */ if ('-' == number->number[0]) { *size += 1; } } return 0; } /* check if we have nan. */ if ((i < number->number_size) && ('N' == number->number[i])) { const char *nan = "NaN"; size_t k; for (k = i; k < number->number_size; k++) { const char c = *nan++; /* Check if we found the NaN string! */ if ('\0' == c) { break; } else if (c != number->number[k]) { break; } } if ('\0' == *nan) { /* NaN becomes 1 because JSON can't support it. */ *size += 1; return 0; } } /* if we had a leading decimal point. */ if ((i < number->number_size) && ('.' == number->number[i])) { /* 1 + because we had a leading decimal point. */ *size += 1; goto cleanup; } for (; i < number->number_size; i++) { const char c = number->number[i]; if (!('0' <= c && c <= '9')) { break; } } /* if we had a trailing decimal point. */ if ((i + 1 == number->number_size) && ('.' == number->number[i])) { /* 1 + because we had a trailing decimal point. */ *size += 1; goto cleanup; } cleanup: *size += number->number_size; /* the actual string of the number. */ /* if we had a leading '+' we don't record it in the JSON output. */ if ('+' == number->number[0]) { *size -= 1; } return 0; } json_weak int json_write_get_string_size(const struct json_string_s *string, size_t *size); int json_write_get_string_size(const struct json_string_s *string, size_t *size) { size_t i; for (i = 0; i < string->string_size; i++) { switch (string->string[i]) { case '"': case '\\': case '\b': case '\f': case '\n': case '\r': case '\t': *size += 2; break; default: *size += 1; break; } } *size += 2; /* need to encode the surrounding '"' characters. */ return 0; } json_weak int json_write_minified_get_array_size(const struct json_array_s *array, size_t *size); int json_write_minified_get_array_size(const struct json_array_s *array, size_t *size) { struct json_array_element_s *element; *size += 2; /* '[' and ']'. */ if (1 < array->length) { *size += array->length - 1; /* ','s seperate each element. */ } for (element = array->start; json_null != element; element = element->next) { if (json_write_minified_get_value_size(element->value, size)) { /* value was malformed! */ return 1; } } return 0; } json_weak int json_write_minified_get_object_size(const struct json_object_s *object, size_t *size); int json_write_minified_get_object_size(const struct json_object_s *object, size_t *size) { struct json_object_element_s *element; *size += 2; /* '{' and '}'. */ *size += object->length; /* ':'s seperate each name/value pair. */ if (1 < object->length) { *size += object->length - 1; /* ','s seperate each element. */ } for (element = object->start; json_null != element; element = element->next) { if (json_write_get_string_size(element->name, size)) { /* string was malformed! */ return 1; } if (json_write_minified_get_value_size(element->value, size)) { /* value was malformed! */ return 1; } } return 0; } json_weak int json_write_minified_get_value_size(const struct json_value_s *value, size_t *size); int json_write_minified_get_value_size(const struct json_value_s *value, size_t *size) { switch (value->type) { default: /* unknown value type found! */ return 1; case json_type_number: return json_write_get_number_size((struct json_number_s *)value->payload, size); case json_type_string: return json_write_get_string_size((struct json_string_s *)value->payload, size); case json_type_array: return json_write_minified_get_array_size( (struct json_array_s *)value->payload, size); case json_type_object: return json_write_minified_get_object_size( (struct json_object_s *)value->payload, size); case json_type_true: *size += 4; /* the string "true". */ return 0; case json_type_false: *size += 5; /* the string "false". */ return 0; case json_type_null: *size += 4; /* the string "null". */ return 0; } } json_weak char *json_write_minified_value(const struct json_value_s *value, char *data); json_weak char *json_write_number(const struct json_number_s *number, char *data); char *json_write_number(const struct json_number_s *number, char *data) { json_uintmax_t parsed_number, backup; size_t i; if (number->number_size >= 2) { switch (number->number[1]) { default: break; case 'x': case 'X': /* The number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal * so we have to do extra work to convert it to a non-hexadecimal for JSON * output. */ parsed_number = json_strtoumax(number->number, json_null, 0); /* We need a copy of parsed number twice, so take a backup of it. */ backup = parsed_number; i = 0; while (0 != parsed_number) { parsed_number /= 10; i++; } /* Restore parsed_number to its original value stored in the backup. */ parsed_number = backup; /* Now use backup to take a copy of i, or the length of the string. */ backup = i; do { *(data + i - 1) = '0' + (char)(parsed_number % 10); parsed_number /= 10; i--; } while (0 != parsed_number); data += backup; return data; } } /* check to see if the number has leading/trailing decimal point. */ i = 0; /* skip any leading '-'. */ if ((i < number->number_size) && (('+' == number->number[i]) || ('-' == number->number[i]))) { i++; } /* check if we have infinity. */ if ((i < number->number_size) && ('I' == number->number[i])) { const char *inf = "Infinity"; size_t k; for (k = i; k < number->number_size; k++) { const char c = *inf++; /* Check if we found the Infinity string! */ if ('\0' == c) { break; } else if (c != number->number[k]) { break; } } if ('\0' == *inf++) { const char *dbl_max; /* if we had a leading '-' we need to record it in the JSON output. */ if ('-' == number->number[0]) { *data++ = '-'; } /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */ for (dbl_max = "1.7976931348623158e308"; '\0' != *dbl_max; dbl_max++) { *data++ = *dbl_max; } return data; } } /* check if we have nan. */ if ((i < number->number_size) && ('N' == number->number[i])) { const char *nan = "NaN"; size_t k; for (k = i; k < number->number_size; k++) { const char c = *nan++; /* Check if we found the NaN string! */ if ('\0' == c) { break; } else if (c != number->number[k]) { break; } } if ('\0' == *nan++) { /* NaN becomes 0 because JSON can't support it. */ *data++ = '0'; return data; } } /* if we had a leading decimal point. */ if ((i < number->number_size) && ('.' == number->number[i])) { i = 0; /* skip any leading '+'. */ if ('+' == number->number[i]) { i++; } /* output the leading '-' if we had one. */ if ('-' == number->number[i]) { *data++ = '-'; i++; } /* insert a '0' to fix the leading decimal point for JSON output. */ *data++ = '0'; /* and output the rest of the number as normal. */ for (; i < number->number_size; i++) { *data++ = number->number[i]; } return data; } for (; i < number->number_size; i++) { const char c = number->number[i]; if (!('0' <= c && c <= '9')) { break; } } /* if we had a trailing decimal point. */ if ((i + 1 == number->number_size) && ('.' == number->number[i])) { i = 0; /* skip any leading '+'. */ if ('+' == number->number[i]) { i++; } /* output the leading '-' if we had one. */ if ('-' == number->number[i]) { *data++ = '-'; i++; } /* and output the rest of the number as normal. */ for (; i < number->number_size; i++) { *data++ = number->number[i]; } /* insert a '0' to fix the trailing decimal point for JSON output. */ *data++ = '0'; return data; } i = 0; /* skip any leading '+'. */ if ('+' == number->number[i]) { i++; } for (; i < number->number_size; i++) { *data++ = number->number[i]; } return data; } json_weak char *json_write_string(const struct json_string_s *string, char *data); char *json_write_string(const struct json_string_s *string, char *data) { size_t i; *data++ = '"'; /* open the string. */ for (i = 0; i < string->string_size; i++) { switch (string->string[i]) { case '"': *data++ = '\\'; /* escape the control character. */ *data++ = '"'; break; case '\\': *data++ = '\\'; /* escape the control character. */ *data++ = '\\'; break; case '\b': *data++ = '\\'; /* escape the control character. */ *data++ = 'b'; break; case '\f': *data++ = '\\'; /* escape the control character. */ *data++ = 'f'; break; case '\n': *data++ = '\\'; /* escape the control character. */ *data++ = 'n'; break; case '\r': *data++ = '\\'; /* escape the control character. */ *data++ = 'r'; break; case '\t': *data++ = '\\'; /* escape the control character. */ *data++ = 't'; break; default: *data++ = string->string[i]; break; } } *data++ = '"'; /* close the string. */ return data; } json_weak char *json_write_minified_array(const struct json_array_s *array, char *data); char *json_write_minified_array(const struct json_array_s *array, char *data) { struct json_array_element_s *element = json_null; *data++ = '['; /* open the array. */ for (element = array->start; json_null != element; element = element->next) { if (element != array->start) { *data++ = ','; /* ','s seperate each element. */ } data = json_write_minified_value(element->value, data); if (json_null == data) { /* value was malformed! */ return json_null; } } *data++ = ']'; /* close the array. */ return data; } json_weak char *json_write_minified_object(const struct json_object_s *object, char *data); char *json_write_minified_object(const struct json_object_s *object, char *data) { struct json_object_element_s *element = json_null; *data++ = '{'; /* open the object. */ for (element = object->start; json_null != element; element = element->next) { if (element != object->start) { *data++ = ','; /* ','s seperate each element. */ } data = json_write_string(element->name, data); if (json_null == data) { /* string was malformed! */ return json_null; } *data++ = ':'; /* ':'s seperate each name/value pair. */ data = json_write_minified_value(element->value, data); if (json_null == data) { /* value was malformed! */ return json_null; } } *data++ = '}'; /* close the object. */ return data; } json_weak char *json_write_minified_value(const struct json_value_s *value, char *data); char *json_write_minified_value(const struct json_value_s *value, char *data) { switch (value->type) { default: /* unknown value type found! */ return json_null; case json_type_number: return json_write_number((struct json_number_s *)value->payload, data); case json_type_string: return json_write_string((struct json_string_s *)value->payload, data); case json_type_array: return json_write_minified_array((struct json_array_s *)value->payload, data); case json_type_object: return json_write_minified_object((struct json_object_s *)value->payload, data); case json_type_true: data[0] = 't'; data[1] = 'r'; data[2] = 'u'; data[3] = 'e'; return data + 4; case json_type_false: data[0] = 'f'; data[1] = 'a'; data[2] = 'l'; data[3] = 's'; data[4] = 'e'; return data + 5; case json_type_null: data[0] = 'n'; data[1] = 'u'; data[2] = 'l'; data[3] = 'l'; return data + 4; } } void *json_write_minified(const struct json_value_s *value, size_t *out_size) { size_t size = 0; char *data = json_null; char *data_end = json_null; if (json_null == value) { return json_null; } if (json_write_minified_get_value_size(value, &size)) { /* value was malformed! */ return json_null; } size += 1; /* for the '\0' null terminating character. */ data = (char *)malloc(size); if (json_null == data) { /* malloc failed! */ return json_null; } data_end = json_write_minified_value(value, data); if (json_null == data_end) { /* bad chi occurred! */ free(data); return json_null; } /* null terminated the string. */ *data_end = '\0'; if (json_null != out_size) { *out_size = size; } return data; } json_weak int json_write_pretty_get_value_size(const struct json_value_s *value, size_t depth, size_t indent_size, size_t newline_size, size_t *size); json_weak int json_write_pretty_get_array_size(const struct json_array_s *array, size_t depth, size_t indent_size, size_t newline_size, size_t *size); int json_write_pretty_get_array_size(const struct json_array_s *array, size_t depth, size_t indent_size, size_t newline_size, size_t *size) { struct json_array_element_s *element; *size += 1; /* '['. */ if (0 < array->length) { /* if we have any elements we need to add a newline after our '['. */ *size += newline_size; *size += array->length - 1; /* ','s seperate each element. */ for (element = array->start; json_null != element; element = element->next) { /* each element gets an indent. */ *size += (depth + 1) * indent_size; if (json_write_pretty_get_value_size(element->value, depth + 1, indent_size, newline_size, size)) { /* value was malformed! */ return 1; } /* each element gets a newline too. */ *size += newline_size; } /* since we wrote out some elements, need to add a newline and indentation. */ /* to the trailing ']'. */ *size += depth * indent_size; } *size += 1; /* ']'. */ return 0; } json_weak int json_write_pretty_get_object_size(const struct json_object_s *object, size_t depth, size_t indent_size, size_t newline_size, size_t *size); int json_write_pretty_get_object_size(const struct json_object_s *object, size_t depth, size_t indent_size, size_t newline_size, size_t *size) { struct json_object_element_s *element; *size += 1; /* '{'. */ if (0 < object->length) { *size += newline_size; /* need a newline next. */ *size += object->length - 1; /* ','s seperate each element. */ for (element = object->start; json_null != element; element = element->next) { /* each element gets an indent and newline. */ *size += (depth + 1) * indent_size; *size += newline_size; if (json_write_get_string_size(element->name, size)) { /* string was malformed! */ return 1; } *size += 3; /* seperate each name/value pair with " : ". */ if (json_write_pretty_get_value_size(element->value, depth + 1, indent_size, newline_size, size)) { /* value was malformed! */ return 1; } } *size += depth * indent_size; } *size += 1; /* '}'. */ return 0; } json_weak int json_write_pretty_get_value_size(const struct json_value_s *value, size_t depth, size_t indent_size, size_t newline_size, size_t *size); int json_write_pretty_get_value_size(const struct json_value_s *value, size_t depth, size_t indent_size, size_t newline_size, size_t *size) { switch (value->type) { default: /* unknown value type found! */ return 1; case json_type_number: return json_write_get_number_size((struct json_number_s *)value->payload, size); case json_type_string: return json_write_get_string_size((struct json_string_s *)value->payload, size); case json_type_array: return json_write_pretty_get_array_size( (struct json_array_s *)value->payload, depth, indent_size, newline_size, size); case json_type_object: return json_write_pretty_get_object_size( (struct json_object_s *)value->payload, depth, indent_size, newline_size, size); case json_type_true: *size += 4; /* the string "true". */ return 0; case json_type_false: *size += 5; /* the string "false". */ return 0; case json_type_null: *size += 4; /* the string "null". */ return 0; } } json_weak char *json_write_pretty_value(const struct json_value_s *value, size_t depth, const char *indent, const char *newline, char *data); json_weak char *json_write_pretty_array(const struct json_array_s *array, size_t depth, const char *indent, const char *newline, char *data); char *json_write_pretty_array(const struct json_array_s *array, size_t depth, const char *indent, const char *newline, char *data) { size_t k, m; struct json_array_element_s *element; *data++ = '['; /* open the array. */ if (0 < array->length) { for (k = 0; '\0' != newline[k]; k++) { *data++ = newline[k]; } for (element = array->start; json_null != element; element = element->next) { if (element != array->start) { *data++ = ','; /* ','s seperate each element. */ for (k = 0; '\0' != newline[k]; k++) { *data++ = newline[k]; } } for (k = 0; k < depth + 1; k++) { for (m = 0; '\0' != indent[m]; m++) { *data++ = indent[m]; } } data = json_write_pretty_value(element->value, depth + 1, indent, newline, data); if (json_null == data) { /* value was malformed! */ return json_null; } } for (k = 0; '\0' != newline[k]; k++) { *data++ = newline[k]; } for (k = 0; k < depth; k++) { for (m = 0; '\0' != indent[m]; m++) { *data++ = indent[m]; } } } *data++ = ']'; /* close the array. */ return data; } json_weak char *json_write_pretty_object(const struct json_object_s *object, size_t depth, const char *indent, const char *newline, char *data); char *json_write_pretty_object(const struct json_object_s *object, size_t depth, const char *indent, const char *newline, char *data) { size_t k, m; struct json_object_element_s *element; *data++ = '{'; /* open the object. */ if (0 < object->length) { for (k = 0; '\0' != newline[k]; k++) { *data++ = newline[k]; } for (element = object->start; json_null != element; element = element->next) { if (element != object->start) { *data++ = ','; /* ','s seperate each element. */ for (k = 0; '\0' != newline[k]; k++) { *data++ = newline[k]; } } for (k = 0; k < depth + 1; k++) { for (m = 0; '\0' != indent[m]; m++) { *data++ = indent[m]; } } data = json_write_string(element->name, data); if (json_null == data) { /* string was malformed! */ return json_null; } /* " : "s seperate each name/value pair. */ *data++ = ' '; *data++ = ':'; *data++ = ' '; data = json_write_pretty_value(element->value, depth + 1, indent, newline, data); if (json_null == data) { /* value was malformed! */ return json_null; } } for (k = 0; '\0' != newline[k]; k++) { *data++ = newline[k]; } for (k = 0; k < depth; k++) { for (m = 0; '\0' != indent[m]; m++) { *data++ = indent[m]; } } } *data++ = '}'; /* close the object. */ return data; } json_weak char *json_write_pretty_value(const struct json_value_s *value, size_t depth, const char *indent, const char *newline, char *data); char *json_write_pretty_value(const struct json_value_s *value, size_t depth, const char *indent, const char *newline, char *data) { switch (value->type) { default: /* unknown value type found! */ return json_null; case json_type_number: return json_write_number((struct json_number_s *)value->payload, data); case json_type_string: return json_write_string((struct json_string_s *)value->payload, data); case json_type_array: return json_write_pretty_array((struct json_array_s *)value->payload, depth, indent, newline, data); case json_type_object: return json_write_pretty_object((struct json_object_s *)value->payload, depth, indent, newline, data); case json_type_true: data[0] = 't'; data[1] = 'r'; data[2] = 'u'; data[3] = 'e'; return data + 4; case json_type_false: data[0] = 'f'; data[1] = 'a'; data[2] = 'l'; data[3] = 's'; data[4] = 'e'; return data + 5; case json_type_null: data[0] = 'n'; data[1] = 'u'; data[2] = 'l'; data[3] = 'l'; return data + 4; } } void *json_write_pretty(const struct json_value_s *value, const char *indent, const char *newline, size_t *out_size) { size_t size = 0; size_t indent_size = 0; size_t newline_size = 0; char *data = json_null; char *data_end = json_null; if (json_null == value) { return json_null; } if (json_null == indent) { indent = " "; /* default to two spaces. */ } if (json_null == newline) { newline = "\n"; /* default to linux newlines. */ } while ('\0' != indent[indent_size]) { ++indent_size; /* skip non-null terminating characters. */ } while ('\0' != newline[newline_size]) { ++newline_size; /* skip non-null terminating characters. */ } if (json_write_pretty_get_value_size(value, 0, indent_size, newline_size, &size)) { /* value was malformed! */ return json_null; } size += 1; /* for the '\0' null terminating character. */ data = (char *)malloc(size); if (json_null == data) { /* malloc failed! */ return json_null; } data_end = json_write_pretty_value(value, 0, indent, newline, data); if (json_null == data_end) { /* bad chi occurred! */ free(data); return json_null; } /* null terminated the string. */ *data_end = '\0'; if (json_null != out_size) { *out_size = size; } return data; } #if defined(__clang__) #pragma clang diagnostic pop #elif defined(_MSC_VER) #pragma warning(pop) #endif #endif /* SHEREDOM_JSON_H_INCLUDED. */ qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/PaxHeaders/moduleinfo.h0000644000000000000000000000013215124701711025026 xustar0030 mtime=1767080905.282785169 30 atime=1767080905.282785169 30 ctime=1767080905.282785169 qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/moduleinfo.h0000644000175000001440000000436415124701711025025 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // // Category : moduleinfo // Filename : public.sdk/source/vst/moduleinfo/moduleinfo.h // Created by : Steinberg, 12/2021 // Description : // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include #include #include //------------------------------------------------------------------------ namespace Steinberg { //------------------------------------------------------------------------ struct ModuleInfo { //------------------------------------------------------------------------ struct FactoryInfo { std::string vendor; std::string url; std::string email; int32_t flags {0}; }; //------------------------------------------------------------------------ struct Snapshot { double scaleFactor {1.}; std::string path; }; using SnapshotList = std::vector; //------------------------------------------------------------------------ struct ClassInfo { std::string cid; std::string category; std::string name; std::string vendor; std::string version; std::string sdkVersion; std::vector subCategories; SnapshotList snapshots; int32_t cardinality {0x7FFFFFFF}; uint32_t flags {0}; }; //------------------------------------------------------------------------ struct Compatibility { std::string newCID; std::vector oldCID; }; using ClassList = std::vector; using CompatibilityList = std::vector; std::string name; std::string version; FactoryInfo factoryInfo; ClassList classes; CompatibilityList compatibility; }; //------------------------------------------------------------------------ } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/PaxHeaders/jsoncxx.h0000644000000000000000000000013215124701711024361 xustar0030 mtime=1767080905.282785169 30 atime=1767080905.282785169 30 ctime=1767080905.282785169 qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/jsoncxx.h0000644000175000001440000003001115124701711024344 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // // Category : // Filename : public.sdk/source/vst/moduleinfo/jsoncxx.h // Created by : Steinberg, 12/2021 // Description : // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "json.h" #include #include #include #include #include #include #if defined(_MSC_VER) || __has_include() #include #define SMTG_HAS_CHARCONV #endif //------------------------------------------------------------------------ namespace JSON { namespace Detail { //------------------------------------------------------------------------ template struct Base { explicit Base (JsonT* o) : object_ (o) {} explicit Base (const Base& o) : object_ (o.object_) {} Base& operator= (const Base& o) = default; operator JsonT* () const { return object_; } JsonT* jsonValue () const { return object_; } protected: Base () : object_ (nullptr) {} JsonT* object_; }; //------------------------------------------------------------------------ template struct Iterator { explicit Iterator (JsonElement el) : el (el) {} bool operator== (const Iterator& other) const { return other.el == el; } bool operator!= (const Iterator& other) const { return other.el != el; } const JsonElement& operator* () const { return el; } const JsonElement& operator-> () const { return el; } Iterator& operator++ () { if (el) el = el.next (); return *this; } Iterator operator++ (int) { auto it = Iterator (el); operator++ (); return it; } private: JsonElement el; }; //------------------------------------------------------------------------ } // Detail struct Object; struct Array; struct String; struct Number; struct Boolean; //------------------------------------------------------------------------ enum class Type { Object, Array, String, Number, True, False, Null, }; //------------------------------------------------------------------------ struct SourceLocation { size_t offset; size_t line; size_t row; }; //------------------------------------------------------------------------ struct Value : Detail::Base { using Detail::Base::Base; using VariantT = std::variant; std::optional asObject () const; std::optional asArray () const; std::optional asString () const; std::optional asNumber () const; std::optional asBoolean () const; std::optional asNull () const; VariantT asVariant () const; Type type () const; SourceLocation getSourceLocation () const; }; //------------------------------------------------------------------------ struct Boolean { Boolean (size_t type) : value (type == json_type_true) {} operator bool () const { return value; } private: bool value; }; //------------------------------------------------------------------------ struct String : Detail::Base { using Detail::Base::Base; std::string_view text () const { return {jsonValue ()->string, jsonValue ()->string_size}; } SourceLocation getSourceLocation () const; }; //------------------------------------------------------------------------ struct Number : Detail::Base { using Detail::Base::Base; std::string_view text () const { return {jsonValue ()->number, jsonValue ()->number_size}; } std::optional getInteger () const; std::optional getDouble () const; }; //------------------------------------------------------------------------ struct ObjectElement : Detail::Base { using Detail::Base::Base; String name () const { return String (jsonValue ()->name); } Value value () const { return Value (jsonValue ()->value); } ObjectElement next () const { return ObjectElement (jsonValue ()->next); } }; //------------------------------------------------------------------------ struct Object : Detail::Base { using Detail::Base::Base; using Iterator = Detail::Iterator; size_t size () const { return jsonValue ()->length; } Iterator begin () const { return Iterator (ObjectElement (jsonValue ()->start)); } Iterator end () const { return Iterator (ObjectElement (nullptr)); } }; //------------------------------------------------------------------------ struct ArrayElement : Detail::Base { using Detail::Base::Base; Value value () const { return Value (jsonValue ()->value); } ArrayElement next () const { return ArrayElement (jsonValue ()->next); } }; //------------------------------------------------------------------------ struct Array : Detail::Base { using Detail::Base::Base; using Iterator = Detail::Iterator; size_t size () const { return jsonValue ()->length; } Iterator begin () const { return Iterator (ArrayElement (jsonValue ()->start)); } Iterator end () const { return Iterator (ArrayElement (nullptr)); } }; //------------------------------------------------------------------------ struct Document : Value { static std::variant parse (std::string_view data) { auto allocate = [] (void*, size_t allocSize) { return std::malloc (allocSize); }; json_parse_result_s parse_result {}; auto value = json_parse_ex (data.data (), data.size (), json_parse_flags_allow_json5 | json_parse_flags_allow_location_information, allocate, nullptr, &parse_result); if (value) return Document (value); return parse_result; } ~Document () noexcept { if (object_) std::free (object_); } Document (Document&& doc) noexcept { *this = std::move (doc); } Document& operator= (Document&& doc) noexcept { std::swap (object_, doc.object_); return *this; } private: using Value::Value; }; //------------------------------------------------------------------------ inline std::optional Value::asObject () const { if (type () != Type::Object) return {}; return Object (json_value_as_object (jsonValue ())); } //------------------------------------------------------------------------ inline std::optional Value::asArray () const { if (type () != Type::Array) return {}; return Array (json_value_as_array (jsonValue ())); } //------------------------------------------------------------------------ inline std::optional Value::asString () const { if (type () != Type::String) return {}; return String (json_value_as_string (jsonValue ())); } //------------------------------------------------------------------------ inline std::optional Value::asNumber () const { if (type () != Type::Number) return {}; return Number (json_value_as_number (jsonValue ())); } //------------------------------------------------------------------------ inline std::optional Value::asBoolean () const { if (type () == Type::True || type () == Type::False) return Boolean (jsonValue ()->type); return {}; } //------------------------------------------------------------------------ inline std::optional Value::asNull () const { if (type () != Type::Null) return {}; return nullptr; } //------------------------------------------------------------------------ inline Type Value::type () const { switch (jsonValue ()->type) { case json_type_string: return Type::String; case json_type_number: return Type::Number; case json_type_object: return Type::Object; case json_type_array: return Type::Array; case json_type_true: return Type::True; case json_type_false: return Type::False; case json_type_null: return Type::Null; } assert (false); return Type::Null; } //------------------------------------------------------------------------ inline Value::VariantT Value::asVariant () const { switch (type ()) { case Type::String: return *asString (); case Type::Number: return *asNumber (); case Type::Object: return *asObject (); case Type::Array: return *asArray (); case Type::True: return *asBoolean (); case Type::False: return *asBoolean (); case Type::Null: return *asNull (); } assert (false); return nullptr; } //------------------------------------------------------------------------ inline SourceLocation Value::getSourceLocation () const { auto exValue = reinterpret_cast (jsonValue ()); return {exValue->offset, exValue->line_no, exValue->row_no}; } //------------------------------------------------------------------------ inline SourceLocation String::getSourceLocation () const { auto exValue = reinterpret_cast (jsonValue ()); return {exValue->offset, exValue->line_no, exValue->row_no}; } //------------------------------------------------------------------------ inline std::optional Number::getInteger () const { #if defined(SMTG_HAS_CHARCONV) int64_t result {0}; auto res = std::from_chars (jsonValue ()->number, jsonValue ()->number + jsonValue ()->number_size, result); if (res.ec == std::errc ()) return result; return {}; #else int64_t result {0}; std::string str (jsonValue ()->number, jsonValue ()->number + jsonValue ()->number_size); if (std::sscanf (str.data (), "%lld", &result) != 1) return {}; return result; #endif } //------------------------------------------------------------------------ inline std::optional Number::getDouble () const { #if 1 // clang still has no floting point from_chars version size_t ctrl {0}; auto result = std::stod (std::string (jsonValue ()->number, jsonValue ()->number_size), &ctrl); if (ctrl > 0) return result; #else double result {0.}; auto res = std::from_chars (jsonValue ()->number, jsonValue ()->number + jsonValue ()->number_size, result); if (res.ec == std::errc ()) return result; #endif return {}; } //------------------------------------------------------------------------ inline std::string_view errorToString (json_parse_error_e error) { switch (error) { case json_parse_error_e::json_parse_error_none: return {}; case json_parse_error_e::json_parse_error_expected_comma_or_closing_bracket: return "json_parse_error_expected_comma_or_closing_bracket"; case json_parse_error_e::json_parse_error_expected_colon: return "json_parse_error_expected_colon"; case json_parse_error_e::json_parse_error_expected_opening_quote: return "json_parse_error_expected_opening_quote"; case json_parse_error_e::json_parse_error_invalid_string_escape_sequence: return "json_parse_error_invalid_string_escape_sequence"; case json_parse_error_e::json_parse_error_invalid_number_format: return "json_parse_error_invalid_number_format"; case json_parse_error_e::json_parse_error_invalid_value: return "json_parse_error_invalid_value"; case json_parse_error_e::json_parse_error_premature_end_of_buffer: return "json_parse_error_premature_end_of_buffer"; case json_parse_error_e::json_parse_error_invalid_string: return "json_parse_error_invalid_string"; case json_parse_error_e::json_parse_error_allocator_failed: return "json_parse_error_allocator_failed"; case json_parse_error_e::json_parse_error_unexpected_trailing_characters: return "json_parse_error_unexpected_trailing_characters"; case json_parse_error_e::json_parse_error_unknown: return "json_parse_error_unknown"; } return {}; } //------------------------------------------------------------------------ } // JSON qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/PaxHeaders/moduleinfocreator.h0000644000000000000000000000013215124701711026406 xustar0030 mtime=1767080905.282785169 30 atime=1767080905.282785169 30 ctime=1767080905.282785169 qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/moduleinfocreator.h0000644000175000001440000000363215124701711026402 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // // Category : moduleinfo // Filename : public.sdk/source/vst/moduleinfo/moduleinfocreator.h // Created by : Steinberg, 12/2021 // Description : utility functions to create moduleinfo json files // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "moduleinfo.h" #include "public.sdk/source/vst/hosting/module.h" #include #include #include //------------------------------------------------------------------------ namespace Steinberg::ModuleInfoLib { //------------------------------------------------------------------------ /** create a ModuleInfo from a module * * @param module module to create the module info from * @param includeDiscardableClasses if true adds the current available classes to the module info * @return a ModuleInfo struct with the classes and factory info of the module */ ModuleInfo createModuleInfo (const VST3::Hosting::Module& module, bool includeDiscardableClasses); //------------------------------------------------------------------------ /** output the ModuleInfo as json to the stream * * @param info module info * @param output output stream */ void outputJson (const ModuleInfo& info, std::ostream& output); //------------------------------------------------------------------------ } // Steinberg::ModuelInfoLib qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/PaxHeaders/moduleinfocreator.cpp0000644000000000000000000000013215124701711026741 xustar0030 mtime=1767080905.282785169 30 atime=1767080905.282785169 30 ctime=1767080905.282785169 qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp0000644000175000001440000001773115124701711026742 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // // Category : moduleinfo // Filename : public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp // Created by : Steinberg, 12/2021 // Description : utility functions to create moduleinfo json files // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "moduleinfocreator.h" #include "jsoncxx.h" #include #include #include //------------------------------------------------------------------------ namespace Steinberg::ModuleInfoLib { using namespace VST3; namespace { //------------------------------------------------------------------------ struct JSON5Writer { private: std::ostream& stream; bool beautify; bool lastIsComma {false}; int32_t intend {0}; void doBeautify () { if (beautify) { stream << '\n'; for (int i = 0; i < intend; ++i) stream << " "; } } void writeComma () { if (lastIsComma) return; stream << ","; lastIsComma = true; } void startObject () { stream << "{"; ++intend; lastIsComma = false; } void endObject () { --intend; doBeautify (); stream << "}"; lastIsComma = false; } void startArray () { stream << "["; ++intend; lastIsComma = false; } void endArray () { --intend; doBeautify (); stream << "]"; lastIsComma = false; } public: JSON5Writer (std::ostream& stream, bool beautify = true) : stream (stream), beautify (beautify) { } void string (std::string_view str) { stream << "\"" << str << "\""; lastIsComma = false; } void boolean (bool val) { stream << (val ? "true" : "false"); lastIsComma = false; } template void value (ValueT val) { stream << val; lastIsComma = false; } template void object (Proc proc) { startObject (); proc (); endObject (); } template void array (Iterator begin, Iterator end, Proc proc) { startArray (); while (begin != end) { doBeautify (); proc (begin); ++begin; writeComma (); } endArray (); } template void keyValue (std::string_view key, Proc proc) { doBeautify (); string (key); stream << ": "; proc (); writeComma (); } }; //------------------------------------------------------------------------ void writeSnapshots (const ModuleInfo::SnapshotList& snapshots, JSON5Writer& w) { w.keyValue ("Snapshots", [&] () { w.array (snapshots.begin (), snapshots.end (), [&] (const auto& el) { w.object ([&] () { w.keyValue ("Scale Factor", [&] () { w.value (el->scaleFactor); }); w.keyValue ("Path", [&] () { w.string (el->path); }); }); }); }); } //------------------------------------------------------------------------ void writeClassInfo (const ModuleInfo::ClassInfo& cls, JSON5Writer& w) { w.keyValue ("CID", [&] () { w.string (cls.cid); }); w.keyValue ("Category", [&] () { w.string (cls.category); }); w.keyValue ("Name", [&] () { w.string (cls.name); }); w.keyValue ("Vendor", [&] () { w.string (cls.vendor); }); w.keyValue ("Version", [&] () { w.string (cls.version); }); w.keyValue ("SDKVersion", [&] () { w.string (cls.sdkVersion); }); const auto& sc = cls.subCategories; if (!sc.empty ()) { w.keyValue ("Sub Categories", [&] () { w.array (sc.begin (), sc.end (), [&] (const auto& cat) { w.string (*cat); }); }); } w.keyValue ("Class Flags", [&] () { w.value (cls.flags); }); w.keyValue ("Cardinality", [&] () { w.value (cls.cardinality); }); writeSnapshots (cls.snapshots, w); } //------------------------------------------------------------------------ void writePluginCompatibility (const ModuleInfo::CompatibilityList& compat, JSON5Writer& w) { if (compat.empty ()) return; w.keyValue ("Compatibility", [&] () { w.array (compat.begin (), compat.end (), [&] (auto& el) { w.object ([&] () { w.keyValue ("New", [&] () { w.string (el->newCID); }); w.keyValue ("Old", [&] () { w.array (el->oldCID.begin (), el->oldCID.end (), [&] (auto& oldEl) { w.string (*oldEl); }); }); }); }); }); } //------------------------------------------------------------------------ void writeFactoryInfo (const ModuleInfo::FactoryInfo& fi, JSON5Writer& w) { w.keyValue ("Factory Info", [&] () { w.object ([&] () { w.keyValue ("Vendor", [&] () { w.string (fi.vendor); }); w.keyValue ("URL", [&] () { w.string (fi.url); }); w.keyValue ("E-Mail", [&] () { w.string (fi.email); }); w.keyValue ("Flags", [&] () { w.object ([&] () { w.keyValue ("Unicode", [&] () { w.boolean (fi.flags & PFactoryInfo::kUnicode); }); w.keyValue ("Classes Discardable", [&] () { w.boolean (fi.flags & PFactoryInfo::kClassesDiscardable); }); w.keyValue ("Component Non Discardable", [&] () { w.boolean (fi.flags & PFactoryInfo::kComponentNonDiscardable); }); }); }); }); }); } //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ ModuleInfo createModuleInfo (const VST3::Hosting::Module& module, bool includeDiscardableClasses) { ModuleInfo info; const auto& factory = module.getFactory (); auto factoryInfo = factory.info (); info.name = module.getName (); auto pos = info.name.find_last_of ('.'); if (pos != std::string::npos) info.name.erase (pos); info.factoryInfo.vendor = factoryInfo.vendor (); info.factoryInfo.url = factoryInfo.url (); info.factoryInfo.email = factoryInfo.email (); info.factoryInfo.flags = factoryInfo.flags (); if (factoryInfo.classesDiscardable () == false || (factoryInfo.classesDiscardable () && includeDiscardableClasses)) { auto snapshots = VST3::Hosting::Module::getSnapshots (module.getPath ()); for (const auto& ci : factory.classInfos ()) { ModuleInfo::ClassInfo classInfo; classInfo.cid = ci.ID ().toString (); classInfo.category = ci.category (); classInfo.name = ci.name (); classInfo.vendor = ci.vendor (); classInfo.version = ci.version (); classInfo.sdkVersion = ci.sdkVersion (); classInfo.subCategories = ci.subCategories (); classInfo.cardinality = ci.cardinality (); classInfo.flags = ci.classFlags (); auto snapshotIt = std::find_if (snapshots.begin (), snapshots.end (), [&] (const auto& el) { return el.uid == ci.ID (); }); if (snapshotIt != snapshots.end ()) { for (auto& s : snapshotIt->images) { std::string_view path (s.path); if (path.find (module.getPath ()) == 0) path.remove_prefix (module.getPath ().size () + 1); classInfo.snapshots.emplace_back ( ModuleInfo::Snapshot {s.scaleFactor, {path.data (), path.size ()}}); } snapshots.erase (snapshotIt); } info.classes.emplace_back (std::move (classInfo)); } } return info; } //------------------------------------------------------------------------ void outputJson (const ModuleInfo& info, std::ostream& output) { JSON5Writer w (output); w.object ([&] () { w.keyValue ("Name", [&] () { w.string (info.name); }); w.keyValue ("Version", [&] () { w.string (info.version); }); writeFactoryInfo (info.factoryInfo, w); writePluginCompatibility (info.compatibility, w); w.keyValue ("Classes", [&] () { w.array (info.classes.begin (), info.classes.end (), [&] (const auto& cls) { w.object ([&] () { writeClassInfo (*cls, w); }); }); }); }); } //------------------------------------------------------------------------ } // Steinberg::ModuleInfoLib qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/PaxHeaders/moduleinfoparser.cpp0000644000000000000000000000013215124701711026576 xustar0030 mtime=1767080905.282785169 30 atime=1767080905.282785169 30 ctime=1767080905.282785169 qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp0000644000175000001440000003741015124701711026573 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // // Category : moduleinfo // Filename : public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp // Created by : Steinberg, 01/2022 // Description : utility functions to parse moduleinfo json files // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "moduleinfoparser.h" #include "jsoncxx.h" #include "pluginterfaces/base/ipluginbase.h" #include #include //------------------------------------------------------------------------ namespace Steinberg::ModuleInfoLib { namespace { //------------------------------------------------------------------------ void printJsonParseError (json_parse_result_s& parseResult, std::ostream& errorOut) { errorOut << "error : " << JSON::errorToString (static_cast (parseResult.error)) << '\n'; errorOut << "offset : " << parseResult.error_offset << '\n'; errorOut << "line no: " << parseResult.error_line_no << '\n'; errorOut << "row no : " << parseResult.error_row_no << '\n'; } //------------------------------------------------------------------------ struct parse_error : std::exception { parse_error (const std::string& str, const JSON::Value& value) : str (str), location (value.getSourceLocation ()) { addLocation (location); } parse_error (const std::string& str, const JSON::String& value) : str (str), location (value.getSourceLocation ()) { addLocation (location); } const char* what () const noexcept override { return str.data (); } private: void addLocation (const JSON::SourceLocation& loc) { str += '\n'; str += "offset:"; str += std::to_string (loc.offset); str += '\n'; str += "line:"; str += std::to_string (loc.line); str += '\n'; str += "row:"; str += std::to_string (loc.row); str += '\n'; } std::string str; JSON::SourceLocation location; }; //------------------------------------------------------------------------ struct ModuleInfoJsonParser { ModuleInfoJsonParser () = default; std::string_view getText (const JSON::Value& value) const { if (auto str = value.asString ()) return str->text (); throw parse_error ("Expect a String here", value); } template T getInteger (const JSON::Value& value) const { if (auto number = value.asNumber ()) { if (auto result = number->getInteger ()) { if (result > static_cast (std::numeric_limits::max ()) || result < static_cast (std::numeric_limits::min ())) throw parse_error ("Value is out of range here", value); return static_cast (*result); } throw parse_error ("Expect an Integer here", value); } throw parse_error ("Expect a Number here", value); } double getDouble (const JSON::Value& value) const { if (auto number = value.asNumber ()) { if (auto result = number->getDouble ()) return *result; throw parse_error ("Expect a Double here", value); } throw parse_error ("Expect a Number here", value); } void parseFactoryInfo (const JSON::Value& value) { enum ParsedBits { Vendor = 1 << 0, URL = 1 << 1, EMail = 1 << 2, Flags = 1 << 3, }; uint32_t parsed {0}; if (auto obj = value.asObject ()) { for (const auto& el : *obj) { auto elementName = el.name ().text (); if (elementName == "Vendor") { if (parsed & ParsedBits::Vendor) throw parse_error ("Only one 'Vendor' key allowed", el.name ()); parsed |= ParsedBits::Vendor; info.factoryInfo.vendor = getText (el.value ()); } else if (elementName == "URL") { if (parsed & ParsedBits::URL) throw parse_error ("Only one 'URL' key allowed", el.name ()); parsed |= ParsedBits::URL; info.factoryInfo.url = getText (el.value ()); } else if (elementName == "E-Mail") { if (parsed & ParsedBits::EMail) throw parse_error ("Only one 'E-Mail' key allowed", el.name ()); parsed |= ParsedBits::EMail; info.factoryInfo.email = getText (el.value ()); } else if (elementName == "Flags") { if (parsed & ParsedBits::Flags) throw parse_error ("Only one 'Flags' key allowed", el.name ()); auto flags = el.value ().asObject (); if (!flags) throw parse_error ("Expect 'Flags' to be a JSON Object", el.name ()); for (const auto& flag : *flags) { auto flagName = flag.name ().text (); auto flagValue = flag.value ().asBoolean (); if (!flagValue) throw parse_error ("Flag must be a boolean", flag.value ()); if (flagName == "Classes Discardable") { if (*flagValue) info.factoryInfo.flags |= PFactoryInfo::kClassesDiscardable; } else if (flagName == "Component Non Discardable") { if (*flagValue) info.factoryInfo.flags |= PFactoryInfo::kComponentNonDiscardable; } else if (flagName == "Unicode") { if (*flagValue) info.factoryInfo.flags |= PFactoryInfo::kUnicode; } else throw parse_error ("Unknown flag", flag.name ()); } parsed |= ParsedBits::Flags; } } } if (!(parsed & ParsedBits::Vendor)) throw std::logic_error ("Missing 'Vendor' in Factory Info"); if (!(parsed & ParsedBits::URL)) throw std::logic_error ("Missing 'URL' in Factory Info"); if (!(parsed & ParsedBits::EMail)) throw std::logic_error ("Missing 'EMail' in Factory Info"); if (!(parsed & ParsedBits::Flags)) throw std::logic_error ("Missing 'Flags' in Factory Info"); } void parseClasses (const JSON::Value& value) { enum ParsedBits { CID = 1 << 0, Category = 1 << 1, Name = 1 << 2, Vendor = 1 << 3, Version = 1 << 4, SDKVersion = 1 << 5, SubCategories = 1 << 6, ClassFlags = 1 << 7, Snapshots = 1 << 8, Cardinality = 1 << 9, }; auto array = value.asArray (); if (!array) throw parse_error ("Expect Classes Array", value); for (const auto& classInfoEl : *array) { auto classInfo = classInfoEl.value ().asObject (); if (!classInfo) throw parse_error ("Expect Class Object", classInfoEl.value ()); ModuleInfo::ClassInfo ci {}; uint32_t parsed {0}; for (const auto& el : *classInfo) { auto elementName = el.name ().text (); if (elementName == "CID") { if (parsed & ParsedBits::CID) throw parse_error ("Only one 'CID' key allowed", el.name ()); ci.cid = getText (el.value ()); parsed |= ParsedBits::CID; } else if (elementName == "Category") { if (parsed & ParsedBits::Category) throw parse_error ("Only one 'Category' key allowed", el.name ()); ci.category = getText (el.value ()); parsed |= ParsedBits::Category; } else if (elementName == "Name") { if (parsed & ParsedBits::Name) throw parse_error ("Only one 'Name' key allowed", el.name ()); ci.name = getText (el.value ()); parsed |= ParsedBits::Name; } else if (elementName == "Vendor") { if (parsed & ParsedBits::Vendor) throw parse_error ("Only one 'Vendor' key allowed", el.name ()); ci.vendor = getText (el.value ()); parsed |= ParsedBits::Vendor; } else if (elementName == "Version") { if (parsed & ParsedBits::Version) throw parse_error ("Only one 'Version' key allowed", el.name ()); ci.version = getText (el.value ()); parsed |= ParsedBits::Version; } else if (elementName == "SDKVersion") { if (parsed & ParsedBits::SDKVersion) throw parse_error ("Only one 'SDKVersion' key allowed", el.name ()); ci.sdkVersion = getText (el.value ()); parsed |= ParsedBits::SDKVersion; } else if (elementName == "Sub Categories") { if (parsed & ParsedBits::SubCategories) throw parse_error ("Only one 'Sub Categories' key allowed", el.name ()); auto subCatArr = el.value ().asArray (); if (!subCatArr) throw parse_error ("Expect Array here", el.value ()); for (const auto& catEl : *subCatArr) { auto cat = getText (catEl.value ()); ci.subCategories.emplace_back (cat); } parsed |= ParsedBits::SubCategories; } else if (elementName == "Class Flags") { if (parsed & ParsedBits::ClassFlags) throw parse_error ("Only one 'Class Flags' key allowed", el.name ()); ci.flags = getInteger (el.value ()); parsed |= ParsedBits::ClassFlags; } else if (elementName == "Cardinality") { if (parsed & ParsedBits::Cardinality) throw parse_error ("Only one 'Cardinality' key allowed", el.name ()); ci.cardinality = getInteger (el.value ()); parsed |= ParsedBits::Cardinality; } else if (elementName == "Snapshots") { if (parsed & ParsedBits::Snapshots) throw parse_error ("Only one 'Snapshots' key allowed", el.name ()); auto snapArr = el.value ().asArray (); if (!snapArr) throw parse_error ("Expect Array here", el.value ()); for (const auto& snapEl : *snapArr) { auto snap = snapEl.value ().asObject (); if (!snap) throw parse_error ("Expect Object here", snapEl.value ()); ModuleInfo::Snapshot snapshot; for (const auto& spEl : *snap) { auto spElName = spEl.name ().text (); if (spElName == "Path") snapshot.path = getText (spEl.value ()); else if (spElName == "Scale Factor") snapshot.scaleFactor = getDouble (spEl.value ()); else throw parse_error ("Unexpected key", spEl.name ()); } if (snapshot.scaleFactor == 0. || snapshot.path.empty ()) throw parse_error ("Missing Snapshot keys", snapEl.value ()); ci.snapshots.emplace_back (std::move (snapshot)); } parsed |= ParsedBits::Snapshots; } else throw parse_error ("Unexpected key", el.name ()); } if (!(parsed & ParsedBits::CID)) throw parse_error ("'CID' key missing", classInfoEl.value ()); if (!(parsed & ParsedBits::Category)) throw parse_error ("'Category' key missing", classInfoEl.value ()); if (!(parsed & ParsedBits::Name)) throw parse_error ("'Name' key missing", classInfoEl.value ()); if (!(parsed & ParsedBits::Vendor)) throw parse_error ("'Vendor' key missing", classInfoEl.value ()); if (!(parsed & ParsedBits::Version)) throw parse_error ("'Version' key missing", classInfoEl.value ()); if (!(parsed & ParsedBits::SDKVersion)) throw parse_error ("'SDK Version' key missing", classInfoEl.value ()); if (!(parsed & ParsedBits::ClassFlags)) throw parse_error ("'Class Flags' key missing", classInfoEl.value ()); if (!(parsed & ParsedBits::Cardinality)) throw parse_error ("'Cardinality' key missing", classInfoEl.value ()); info.classes.emplace_back (std::move (ci)); } } void parseCompatibility (const JSON::Value& value) { auto arr = value.asArray (); if (!arr) throw parse_error ("Expect Array here", value); for (const auto& el : *arr) { auto obj = el.value ().asObject (); if (!obj) throw parse_error ("Expect Object here", el.value ()); ModuleInfo::Compatibility compat; for (const auto& objEl : *obj) { auto elementName = objEl.name ().text (); if (elementName == "New") compat.newCID = getText (objEl.value ()); else if (elementName == "Old") { auto oldElArr = objEl.value ().asArray (); if (!oldElArr) throw parse_error ("Expect Array here", objEl.value ()); for (const auto& old : *oldElArr) { compat.oldCID.emplace_back (getText (old.value ())); } } } if (compat.newCID.empty ()) throw parse_error ("Expect New CID here", el.value ()); if (compat.oldCID.empty ()) throw parse_error ("Expect Old CID here", el.value ()); info.compatibility.emplace_back (std::move (compat)); } } void parse (const JSON::Document& doc) { auto docObj = doc.asObject (); if (!docObj) throw parse_error ("Unexpected", doc); enum ParsedBits { Name = 1 << 0, Version = 1 << 1, FactoryInfo = 1 << 2, Compatibility = 1 << 3, Classes = 1 << 4, }; uint32_t parsed {0}; for (const auto& el : *docObj) { auto elementName = el.name ().text (); if (elementName == "Name") { if (parsed & ParsedBits::Name) throw parse_error ("Only one 'Name' key allowed", el.name ()); parsed |= ParsedBits::Name; info.name = getText (el.value ()); } else if (elementName == "Version") { if (parsed & ParsedBits::Version) throw parse_error ("Only one 'Version' key allowed", el.name ()); parsed |= ParsedBits::Version; info.version = getText (el.value ()); } else if (elementName == "Factory Info") { if (parsed & ParsedBits::FactoryInfo) throw parse_error ("Only one 'Factory Info' key allowed", el.name ()); parseFactoryInfo (el.value ()); parsed |= ParsedBits::FactoryInfo; } else if (elementName == "Compatibility") { if (parsed & ParsedBits::Compatibility) throw parse_error ("Only one 'Compatibility' key allowed", el.name ()); parseCompatibility (el.value ()); parsed |= ParsedBits::Compatibility; } else if (elementName == "Classes") { if (parsed & ParsedBits::Classes) throw parse_error ("Only one 'Classes' key allowed", el.name ()); parseClasses (el.value ()); parsed |= ParsedBits::Classes; } else { throw parse_error ("Unexpected JSON Token", el.name ()); } } if (!(parsed & ParsedBits::Name)) throw std::logic_error ("'Name' key missing"); if (!(parsed & ParsedBits::Version)) throw std::logic_error ("'Version' key missing"); if (!(parsed & ParsedBits::FactoryInfo)) throw std::logic_error ("'Factory Info' key missing"); if (!(parsed & ParsedBits::Classes)) throw std::logic_error ("'Classes' key missing"); } ModuleInfo&& takeInfo () { return std::move (info); } private: ModuleInfo info; }; //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ std::optional parseJson (std::string_view jsonData, std::ostream* optErrorOutput) { auto docVar = JSON::Document::parse (jsonData); if (auto res = std::get_if (&docVar)) { if (optErrorOutput) printJsonParseError (*res, *optErrorOutput); return {}; } auto doc = std::get_if (&docVar); assert (doc); try { ModuleInfoJsonParser parser; parser.parse (*doc); return parser.takeInfo (); } catch (std::exception& error) { if (optErrorOutput) *optErrorOutput << error.what () << '\n'; return {}; } // unreachable } //------------------------------------------------------------------------ std::optional parseCompatibilityJson (std::string_view jsonData, std::ostream* optErrorOutput) { auto docVar = JSON::Document::parse (jsonData); if (auto res = std::get_if (&docVar)) { if (optErrorOutput) printJsonParseError (*res, *optErrorOutput); return {}; } auto doc = std::get_if (&docVar); assert (doc); try { ModuleInfoJsonParser parser; parser.parseCompatibility (*doc); return parser.takeInfo ().compatibility; } catch (std::exception& error) { if (optErrorOutput) *optErrorOutput << error.what () << '\n'; return {}; } // unreachable } //------------------------------------------------------------------------ } // Steinberg::ModuelInfoLib qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/PaxHeaders/ReadMe.md0000644000000000000000000000013215124701711024173 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282210861 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/moduleinfo/ReadMe.md0000644000175000001440000000254115124701711024165 0ustar00rncbcusers # ModuleInfoLib This is a c++17 library to parse and create the Steinberg moduleinfo.json files. ## Parsing To parse a moduleinfo.json file you need to include the following files to your project: * moduleinfoparser.cpp * moduleinfoparser.h * moduleinfo.h * json.h * jsoncxx.h And add a header search path to the root folder of the VST SDK. Now to parse a moduleinfo.json file in code you need to read the moduleinfo.json into a memory buffer and call ``` c++ auto moduleInfo = ModuleInfoLib::parseCompatibilityJson (std::string_view (buffer, bufferSize), &std::cerr); ``` Afterwards if parsing succeeded the moduleInfo optional has a value containing the ModuleInfo. ## Creating The VST3 SDK contains the moduleinfotool utility that can create moduleinfo.json files from VST3 modules. To add this capability to your own project you need to link to the sdk_hosting library from the SDK and include the following files to your project: * moduleinfocreator.cpp * moduleinfocreator.h * moduleinfo.h Additionally you need to add the module platform implementation from the hosting directory (module_win32.cpp, module_mac.mm or module_linux.cpp). Now you can use the two methods in moduleinfocreator.h to create a moduleinfo.json file: ``` c++ auto moduleInfo = ModuleInfoLib::createModuleInfo (module, false); ModuleInfoLib::outputJson (moduleInfo, std::cout); ``` qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstgui_linux_runloop_support.cpp0000644000000000000000000000013215124701711027171 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstgui_linux_runloop_support.cpp0000644000175000001440000001135215124701711027163 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstgui_linux_runloop_support.cpp // Created by : Steinberg, 10/2025 // Description : VSTGUI Linux Runloop Support // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/vstgui_linux_runloop_support.h" #include "vstgui/lib/platform/linux/linuxfactory.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/gui/iplugview.h" //----------------------------------------------------------------------------- namespace Steinberg::Linux { namespace { //----------------------------------------------------------------------------- class RunLoop : public VSTGUI::IRunLoop, public VSTGUI::AtomicReferenceCounted { public: struct EventHandler : U::Implements> { VSTGUI::IEventHandler* handler {nullptr}; void PLUGIN_API onFDIsSet (FileDescriptor) override { if (handler) handler->onEvent (); } }; struct TimerHandler : U::Implements> { VSTGUI::ITimerHandler* handler {nullptr}; void PLUGIN_API onTimer () final { if (handler) handler->onTimer (); } }; bool registerEventHandler (int fd, VSTGUI::IEventHandler* handler) final; bool unregisterEventHandler (VSTGUI::IEventHandler* handler) final; bool registerTimer (uint64_t interval, VSTGUI::ITimerHandler* handler) final; bool unregisterTimer (VSTGUI::ITimerHandler* handler) final; RunLoop (Steinberg::FUnknown* runLoop); private: using EventHandlers = std::vector>; using TimerHandlers = std::vector>; EventHandlers eventHandlers; TimerHandlers timerHandlers; FUnknownPtr runLoop; }; //----------------------------------------------------------------------------- bool RunLoop::registerEventHandler (int fd, VSTGUI::IEventHandler* handler) { if (!runLoop) return false; auto smtgHandler = Steinberg::owned (new EventHandler ()); smtgHandler->handler = handler; if (runLoop->registerEventHandler (smtgHandler, fd) == Steinberg::kResultTrue) { eventHandlers.push_back (smtgHandler); return true; } return false; } //----------------------------------------------------------------------------- bool RunLoop::unregisterEventHandler (VSTGUI::IEventHandler* handler) { if (!runLoop) return false; for (auto it = eventHandlers.begin (), end = eventHandlers.end (); it != end; ++it) { if ((*it)->handler == handler) { runLoop->unregisterEventHandler ((*it)); eventHandlers.erase (it); return true; } } return false; } //----------------------------------------------------------------------------- bool RunLoop::registerTimer (uint64_t interval, VSTGUI::ITimerHandler* handler) { if (!runLoop) return false; auto smtgHandler = Steinberg::owned (new TimerHandler ()); smtgHandler->handler = handler; if (runLoop->registerTimer (smtgHandler, interval) == Steinberg::kResultTrue) { timerHandlers.push_back (smtgHandler); return true; } return false; } //----------------------------------------------------------------------------- bool RunLoop::unregisterTimer (VSTGUI::ITimerHandler* handler) { if (!runLoop) return false; for (auto it = timerHandlers.begin (), end = timerHandlers.end (); it != end; ++it) { if ((*it)->handler == handler) { runLoop->unregisterTimer ((*it)); timerHandlers.erase (it); return true; } } return false; } //----------------------------------------------------------------------------- RunLoop::RunLoop (Steinberg::FUnknown* runLoop) : runLoop (runLoop) { } //----------------------------------------------------------------------------- } // anonymous //----------------------------------------------------------------------------- bool setupVSTGUIRunloop (FUnknown* hostContext) { if (auto linuxFactory = VSTGUI::getPlatformFactory ().asLinuxFactory ()) { if (U::cast (hostContext)) { linuxFactory->setRunLoop (VSTGUI::makeOwned (hostContext)); return true; } } return false; } //----------------------------------------------------------------------------- } // Steinberg::Linux qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstcomponent.cpp0000644000000000000000000000013215124701711023616 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstcomponent.cpp0000644000175000001440000001354315124701711023614 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstcomponent.cpp // Created by : Steinberg, 04/2005 // Description : Basic VST Plug-in Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstcomponent.h" namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // Component Implementation //------------------------------------------------------------------------ Component::Component () : audioInputs (kAudio, kInput) , audioOutputs (kAudio, kOutput) , eventInputs (kEvent, kInput) , eventOutputs (kEvent, kOutput) { } //------------------------------------------------------------------------ tresult PLUGIN_API Component::initialize (FUnknown* context) { return ComponentBase::initialize (context); } //------------------------------------------------------------------------ tresult PLUGIN_API Component::terminate () { // remove all busses removeAllBusses (); return ComponentBase::terminate (); } //------------------------------------------------------------------------ BusList* Component::getBusList (MediaType type, BusDirection dir) { if (type == kAudio) return dir == kInput ? &audioInputs : &audioOutputs; if (type == kEvent) return dir == kInput ? &eventInputs : &eventOutputs; return nullptr; } //------------------------------------------------------------------------ tresult Component::removeAudioBusses () { audioInputs.clear (); audioOutputs.clear (); return kResultOk; } //------------------------------------------------------------------------ tresult Component::removeEventBusses () { eventInputs.clear (); eventOutputs.clear (); return kResultOk; } //------------------------------------------------------------------------ tresult Component::removeAllBusses () { removeAudioBusses (); removeEventBusses (); return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API Component::getControllerClassId (TUID classID) { if (controllerClass.isValid ()) { controllerClass.toTUID (classID); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API Component::setIoMode (IoMode /*mode*/) { return kNotImplemented; } //------------------------------------------------------------------------ int32 PLUGIN_API Component::getBusCount (MediaType type, BusDirection dir) { BusList* busList = getBusList (type, dir); return busList ? static_cast (busList->size ()) : 0; } //------------------------------------------------------------------------ tresult PLUGIN_API Component::getBusInfo (MediaType type, BusDirection dir, int32 index, BusInfo& info) { if (index < 0) return kInvalidArgument; BusList* busList = getBusList (type, dir); if (busList == nullptr) return kInvalidArgument; if (index >= static_cast (busList->size ())) return kInvalidArgument; Bus* bus = busList->at (index); info.mediaType = type; info.direction = dir; if (bus->getInfo (info)) return kResultTrue; return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API Component::getRoutingInfo (RoutingInfo& /*inInfo*/, RoutingInfo& /*outInfo*/) { return kNotImplemented; } //------------------------------------------------------------------------ tresult PLUGIN_API Component::activateBus (MediaType type, BusDirection dir, int32 index, TBool state) { if (index < 0) return kInvalidArgument; BusList* busList = getBusList (type, dir); if (busList == nullptr) return kInvalidArgument; if (index >= static_cast (busList->size ())) return kInvalidArgument; Bus* bus = busList->at (index); bus->setActive (state); return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API Component::setActive (TBool /*state*/) { return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API Component::setState (IBStream* /*state*/) { return kNotImplemented; } //------------------------------------------------------------------------ tresult PLUGIN_API Component::getState (IBStream* /*state*/) { return kNotImplemented; } //------------------------------------------------------------------------ tresult Component::renameBus (MediaType type, BusDirection dir, int32 index, const String128 newName) { if (index < 0) return kInvalidArgument; BusList* busList = getBusList (type, dir); if (busList == nullptr) return kInvalidArgument; if (index >= static_cast (busList->size ())) return kInvalidArgument; Bus* bus = busList->at (index); bus->setName (newName); return kResultTrue; } //------------------------------------------------------------------------ // Helpers Implementation //------------------------------------------------------------------------ tresult getSpeakerChannelIndex (SpeakerArrangement arrangement, uint64 speaker, int32& channel) { channel = SpeakerArr::getSpeakerIndex (speaker, arrangement); return (channel < 0) == true ? kResultFalse : kResultTrue; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/utility0000644000000000000000000000013215124701711022001 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.285285125 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/0000755000175000001440000000000015124701711022046 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/mpeprocessor.h0000644000000000000000000000012715124701711024754 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/mpeprocessor.h0000644000175000001440000001343715124701711024750 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/mpeprocessor.h // Created by : Steinberg, 07/2017 // Description : VST 3 MIDI-MPE decomposer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace MPE { using NoteID = int32_t; using Pitch = uint32_t; using Channel = uint32_t; using Velocity = float; using NormalizedValue = double; //------------------------------------------------------------------------ /** MPE per note controller enumeration */ enum class Controller : uint32_t { /** Pressure MPE controller */ Pressure, /** X / horizontal MPE controller */ X, /** Y / vertical MPE controller */ Y, /** no MPE controller */ None }; //------------------------------------------------------------------------ struct Handler { /** Generate a new noteID * * called by the processor for a new NoteID. The handler has to make sure that the noteID is * not used again until the releaseNoteID method is called. * * @param outNoteID on return contains the new noteID if this call succeed * @return true if outNoteID was filled with a new noteID */ virtual bool generateNewNoteID (NoteID& outNoteID) = 0; /** Release a noteID * * called by the processor when the NoteID is no longer used. * * @param noteID the noteID not longer in use */ virtual void releaseNoteID (NoteID noteID) = 0; /** A note on was transmitted * * @param noteID unique note identifier * @param pitch note pitch * @param velocity note on velocity */ virtual void onMPENoteOn (NoteID noteID, Pitch pitch, Velocity velocity) = 0; /** A note off was transmitted * * @param noteID unique note identifier * @param pitch note pitch * @param velocity note off velocity */ virtual void onMPENoteOff (NoteID noteID, Pitch pitch, Velocity velocity) = 0; /** A new per note controller change was transmitted * * @param noteID unique note identifier * @param cc the MIDI controller which changed * @param value the value of the change in the range [0..1] */ virtual void onMPEControllerChange (NoteID noteID, Controller cc, NormalizedValue value) = 0; /** Non MPE MIDI input data was transmitted * * @param data MIDI data buffer * @param dataSize size of the MIDI data buffer in bytes */ virtual void onOtherInput (const uint8_t* data, size_t dataSize) = 0; /** Sysex MIDI data was transmitted * * @param data Sysex data buffer * @param dataSize size of sysex data buffer in bytes */ virtual void onSysexInput (const uint8_t* data, size_t dataSize) = 0; // error handling /** called when the handler did not return a new note ID */ virtual void errorNoteDroppedBecauseNoNoteID (Pitch pitch) = 0; /** the internal note stack for this channel is full, happens on too many note ons per channel */ virtual void errorNoteDroppedBecauseNoteStackFull (Channel channel, Pitch pitch) = 0; /** called when the internal data has no reference to this note off */ virtual void errorNoteForNoteOffNotFound (Channel channel, Pitch pitch) = 0; /** called when a program change was received inside the MPE zone which is a protocol violation */ virtual void errorProgramChangeReceivedInMPEZone () = 0; }; //------------------------------------------------------------------------ /** Input MIDI Message enumeration */ enum InputMIDIMessage : uint32_t { MIDICC_0 = 0, MIDICC_127 = 127, ChannelPressure = 128, PitchBend = 129, Aftertouch = 130, }; //------------------------------------------------------------------------ /** MPE setup structure */ struct Setup { Channel masterChannel {0}; Channel memberChannelBegin {1}; Channel memberChannelEnd {14}; InputMIDIMessage pressure {ChannelPressure}; InputMIDIMessage x {PitchBend}; InputMIDIMessage y {static_cast (74)}; }; //------------------------------------------------------------------------ /** MPE Decompose Processor * * decomposes MPE MIDI messages * */ class Processor { public: Processor (Handler* delegate, size_t maxNotesPerChannel = 16); ~Processor () noexcept; const Setup& getSetup () const; /** change the MPE setup * * make sure that MIDI processing is stopped while this is called. * * @param setup new setup */ void changeSetup (const Setup& setup); /** reset all notes * * All playing notes will be stopped and note identifiers are released. * */ void reset (); /** feed new native MIDI data * * @param data MIDI data buffer * @param dataSize data buffer size in bytes */ void processMIDIInput (const uint8_t* data, size_t dataSize); private: int32_t onNoteOn (const uint8_t* data, size_t dataSize); int32_t onNoteOff (const uint8_t* data, size_t dataSize); int32_t onAftertouch (const uint8_t* data, size_t dataSize); int32_t onController (const uint8_t* data, size_t dataSize); int32_t onProgramChange (const uint8_t* data, size_t dataSize); int32_t onChannelPressure (const uint8_t* data, size_t dataSize); int32_t onPitchWheel (const uint8_t* data, size_t dataSize); struct Impl; std::unique_ptr impl; }; //------------------------------------------------------------------------ } // MPE } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/optional.h0000644000000000000000000000012715124701711024060 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/optional.h0000644000175000001440000000470415124701711024051 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/optional.h // Created by : Steinberg, 08/2016 // Description : optional helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include #include #include //------------------------------------------------------------------------ namespace VST3 { //------------------------------------------------------------------------ template struct Optional { Optional () noexcept : valid (false) {} explicit Optional (const T& v) noexcept : _value (v), valid (true) {} Optional (T&& v) noexcept : _value (std::move (v)), valid (true) {} Optional (Optional&& other) noexcept { *this = std::move (other); } Optional& operator= (Optional&& other) noexcept { valid = other.valid; _value = std::move (other._value); return *this; } explicit operator bool () const noexcept { setValidationChecked (); return valid; } const T& operator* () const noexcept { checkValid (); return _value; } const T* operator-> () const noexcept { checkValid (); return &_value; } T& operator* () noexcept { checkValid (); return _value; } T* operator-> () noexcept { checkValid (); return &_value; } T&& value () noexcept { checkValid (); return std::move (_value); } const T& value () const noexcept { checkValid (); return _value; } void swap (T& other) noexcept { checkValid (); auto tmp = std::move (other); other = std::move (_value); _value = std::move (tmp); } private: T _value {}; bool valid; #if !defined(NDEBUG) mutable bool validationChecked {false}; #endif void setValidationChecked () const { #if !defined(NDEBUG) validationChecked = true; #endif } void checkValid () const { #if !defined(NDEBUG) assert (validationChecked); #endif } }; //------------------------------------------------------------------------ } qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/audiobuffers.h0000644000000000000000000000013015124701711024703 xustar0029 mtime=1767080905.28554689 30 atime=1767080905.285285125 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/audiobuffers.h0000644000175000001440000000354715124701711024706 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/audiobuffers.h // Created by : Steinberg, 04/2021 // Description : Audio Buffer utilities // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstaudioprocessor.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** get channel buffers from audio bus buffers 32 bit variant */ template ::type* = nullptr> inline Sample32** getChannelBuffers (AudioBusBuffers& buffer) { return buffer.channelBuffers32; } //------------------------------------------------------------------------ /** get channel buffers from audio bus buffers 64 bit variant */ template ::type* = nullptr> inline Sample64** getChannelBuffers (AudioBusBuffers& buffer) { return buffer.channelBuffers64; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/processcontextrequirements.h0000644000000000000000000000012715124701711027762 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/processcontextrequirements.h0000644000175000001440000001022215124701711027743 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/processcontextrequirements.h // Created by : Steinberg, 12/2019 // Description : Helper class to work with IProcessContextRequirements // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstaudioprocessor.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ class ProcessContextRequirements { private: using Self = ProcessContextRequirements; public: ProcessContextRequirements (uint32 inFlags = 0) : flags (inFlags) {} bool wantsNone () const { return flags == 0; } bool wantsSystemTime () const { return (flags & IProcessContextRequirements::kNeedSystemTime) != 0; } bool wantsContinousTimeSamples () const { return (flags & IProcessContextRequirements::kNeedContinousTimeSamples) != 0; } bool wantsProjectTimeMusic () const { return (flags & IProcessContextRequirements::kNeedProjectTimeMusic) != 0; } bool wantsBarPositionMusic () const { return (flags & IProcessContextRequirements::kNeedBarPositionMusic) != 0; } bool wantsCycleMusic () const { return (flags & IProcessContextRequirements::kNeedCycleMusic) != 0; } bool wantsSamplesToNextClock () const { return (flags & IProcessContextRequirements::kNeedSamplesToNextClock) != 0; } bool wantsTempo () const { return (flags & IProcessContextRequirements::kNeedTempo) != 0; } bool wantsTimeSignature () const { return (flags & IProcessContextRequirements::kNeedTimeSignature) != 0; } bool wantsChord () const { return (flags & IProcessContextRequirements::kNeedChord) != 0; } bool wantsFrameRate () const { return (flags & IProcessContextRequirements::kNeedFrameRate) != 0; } bool wantsTransportState () const { return (flags & IProcessContextRequirements::kNeedTransportState) != 0; } /** set SystemTime as requested */ Self& needSystemTime () { flags |= IProcessContextRequirements::kNeedSystemTime; return *this; } /** set ContinousTimeSamples as requested */ Self& needContinousTimeSamples () { flags |= IProcessContextRequirements::kNeedContinousTimeSamples; return *this; } /** set ProjectTimeMusic as requested */ Self& needProjectTimeMusic () { flags |= IProcessContextRequirements::kNeedProjectTimeMusic; return *this; } /** set BarPositionMusic as needed */ Self& needBarPositionMusic () { flags |= IProcessContextRequirements::kNeedBarPositionMusic; return *this; } /** set CycleMusic as needed */ Self& needCycleMusic () { flags |= IProcessContextRequirements::kNeedCycleMusic; return *this; } /** set SamplesToNextClock as needed */ Self& needSamplesToNextClock () { flags |= IProcessContextRequirements::kNeedSamplesToNextClock; return *this; } /** set Tempo as needed */ Self& needTempo () { flags |= IProcessContextRequirements::kNeedTempo; return *this; } /** set TimeSignature as needed */ Self& needTimeSignature () { flags |= IProcessContextRequirements::kNeedTimeSignature; return *this; } /** set Chord as needed */ Self& needChord () { flags |= IProcessContextRequirements::kNeedChord; return *this; } /** set FrameRate as needed */ Self& needFrameRate () { flags |= IProcessContextRequirements::kNeedFrameRate; return *this; } /** set TransportState as needed */ Self& needTransportState () { flags |= IProcessContextRequirements::kNeedTransportState; return *this; } uint32 flags {0}; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/stringconvert.cpp0000644000000000000000000000012715124701711025475 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/stringconvert.cpp0000644000175000001440000000750215124701711025465 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/stringconvert.cpp // Created by : Steinberg, 11/2014 // Description : c++11 unicode string convert functions // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginterfaces/base/fplatform.h" #if SMTG_OS_WINDOWS #ifndef _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING #define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING #endif #endif // SMTG_OS_WINDOWS #include "public.sdk/source/vst/utility/stringconvert.h" #include "public.sdk/source/common/commonstringconvert.h" #include #include #include namespace Steinberg { namespace Vst { namespace StringConvert { //------------------------------------------------------------------------ namespace { #if defined(_MSC_VER) && _MSC_VER >= 1900 #define USE_WCHAR_AS_UTF16TYPE using UTF16Type = wchar_t; #else using UTF16Type = char16_t; #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #elif defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4996) #endif using Converter = std::wstring_convert, UTF16Type>; //------------------------------------------------------------------------ Converter& converter () { static Converter conv; return conv; } #ifdef __clang__ #pragma clang diagnostic pop #elif defined(_MSC_VER) #pragma warning(pop) #endif //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ std::u16string convert (const std::string& utf8Str) { return Steinberg::StringConvert::convert (utf8Str); } //------------------------------------------------------------------------ bool convert (const std::string& utf8Str, Steinberg::Vst::String128 str) { return convert (utf8Str, str, 128); } //------------------------------------------------------------------------ bool convert (const std::string& utf8Str, Steinberg::Vst::TChar* str, uint32_t maxCharacters) { auto ucs2 = convert (utf8Str); if (ucs2.length () < maxCharacters) { ucs2.copy (reinterpret_cast (str), ucs2.length ()); str[ucs2.length ()] = 0; return true; } return false; } //------------------------------------------------------------------------ std::string convert (const Steinberg::Vst::TChar* str) { return converter ().to_bytes (reinterpret_cast (str)); } //------------------------------------------------------------------------ std::string convert (const Steinberg::Vst::TChar* str, uint32_t max) { std::string result; if (str) { Steinberg::Vst::TChar tmp[2] {}; for (uint32_t i = 0; i < max; ++i, ++str) { tmp[0] = *str; if (tmp[0] == 0) break; result += convert (tmp); } } return result; } //------------------------------------------------------------------------ std::string convert (const std::u16string& str) { return Steinberg::StringConvert::convert (str); } //------------------------------------------------------------------------ std::string convert (const char* str, uint32_t max) { return Steinberg::StringConvert::convert (str, max); } //------------------------------------------------------------------------ } // StringConvert } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/dataexchange.h0000644000000000000000000000012715124701711024647 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/dataexchange.h0000644000175000001440000001344515124701711024642 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // Category : Helpers // Filename : public.sdk/source/vst/utility/dataexchange.h // Created by : Steinberg, 06/2023 // Description : VST Data Exchange API Helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/vstaudioeffect.h" #include "pluginterfaces/vst/ivstdataexchange.h" #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Helper class to provide a single API for plug-ins to transfer data from the realtime audio * process to the edit controller either via the backwards compatible message handling protocol * (see IMessage) or the new IDataExchangeHandler/IDataExchangeReceiver API. * * To use this, make an instance of DataExchangeHandler a member of your IAudioProcessor class and * call onConnect, onDisconnect, onActivate and onDeactivate when the processor is (dis-)connected * and (de)activated. In your IAudioProcessor::process method you call getCurrentOrNewBlock () to * get a block fill it with the data you want to send and then call sendCurrentBlock. * See DataExchangeReceiverHandler on how to receive that data. */ class DataExchangeHandler { public: struct Config { /** the size of one block in bytes */ uint32 blockSize; /** the number of blocks to request */ uint32 numBlocks; /** the alignment of the buffer */ uint32 alignment {32}; /** a user defined context ID */ DataExchangeUserContextID userContextID {0}; }; /** the callback will be called on setup processing to get the required configuration for the * data exchange */ using ConfigCallback = std::function; DataExchangeHandler (IAudioProcessor* processor, ConfigCallback&& callback); DataExchangeHandler (IAudioProcessor* processor, const ConfigCallback& callback); ~DataExchangeHandler () noexcept; /** call this in AudioEffect::connect * * provide the hostContext you get via AudioEffect::initiailze to this method */ void onConnect (IConnectionPoint* other, FUnknown* hostContext); /** call this in AudioEffect::disconnect */ void onDisconnect (IConnectionPoint* other); /** call this in AudioEffect::setActive(true) */ void onActivate (const Vst::ProcessSetup& setup, bool forceUseMessageHandling = false); /** call this in AudioEffect::setActive(false) */ void onDeactivate (); //--- --------------------------------------------------------------------- /** Get the current or a new block * * On the first call this will always return a new block, only after sendCurrentBlock or * discardCurrentBlock is called a new block will be acquired. * This may return an invalid DataExchangeBlock (check the blockID for * InvalidDataExchangeBlockID) when the queue is full. * * [call only in process call] */ DataExchangeBlock getCurrentOrNewBlock (); /** Send the current block to the receiver * * [call only in process call] */ bool sendCurrentBlock (); /** Discard the current block * * [call only in process call] */ bool discardCurrentBlock (); /** Enable or disable the acquiring of new blocks (per default it is enabled) * * If you disable this then the getCurrentOrNewBlock will always return an invalid block. * * [call only in process call] */ void enable (bool state); /** Ask if enabled * * [call only in process call] */ bool isEnabled () const; //------------------------------------------------------------------------ private: DataExchangeHandler (IAudioProcessor* processor); struct Impl; std::unique_ptr impl; }; //------------------------------------------------------------------------ /** Helper class to provide a single API for plug-ins to transfer data from the realtime audio * process to the edit controller either via the message handling protocol (see IMessage) or the * new IDataExchangeHandler/IDataExchangeReceiver API. * * This is the other side of the DataExchangeHandler on the edit controller side. Make this a * member of your edit controller and call onMessage for every IMessage you get via * IConnectionPoint::notify. Your edit controller must implement the IDataExchangeReceiver * interface. */ class DataExchangeReceiverHandler { public: DataExchangeReceiverHandler (IDataExchangeReceiver* receiver); ~DataExchangeReceiverHandler () noexcept; /** call this for every message you receive via IConnectionPoint::notify * * @return true if the message was handled */ bool onMessage (IMessage* msg); private: struct Impl; std::unique_ptr impl; }; //------------------------------------------------------------------------ inline bool operator!= (const DataExchangeBlock& lhs, const DataExchangeBlock& rhs) { return lhs.data != rhs.data || lhs.size != rhs.size || lhs.blockID != rhs.blockID; } //------------------------------------------------------------------------ inline bool operator== (const DataExchangeBlock& lhs, const DataExchangeBlock& rhs) { return lhs.data == rhs.data && lhs.size == rhs.size && lhs.blockID == rhs.blockID; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/vst2persistence.cpp0000644000000000000000000000013215124701711025725 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/vst2persistence.cpp0000644000175000001440000004411515124701711025722 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : VST3 SDK // Filename : public.sdk/source/vst/utility/vst2persistence.cpp // Created by : Steinberg, 12/2019 // Description : vst2 persistence helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/utility/vst2persistence.h" #include "pluginterfaces/base/fplatform.h" #include //------------------------------------------------------------------------ namespace VST3 { namespace { namespace IO { //------------------------------------------------------------------------ enum class Error { NoError, Unknown, EndOfFile, BufferToBig, NotAllowed, InvalidArgument, }; //------------------------------------------------------------------------ enum class SeekMode { Set, End, Current }; //------------------------------------------------------------------------ struct Result { Error error {Error::Unknown}; uint64_t bytes {0u}; Result () noexcept = default; Result (Error error, uint64_t bytes = 0) noexcept : error (error), bytes (bytes) {} operator bool () const noexcept { return error == Error::NoError; } }; //------------------------------------------------------------------------ struct ReadBufferDesc { const uint64_t bytes; void* ptr; }; //------------------------------------------------------------------------ struct WriteBufferDesc { const uint64_t bytes; const void* ptr; }; //------------------------------------------------------------------------ template class ByteOrderStream { public: //------------------------------------------------------------------------ ByteOrderStream (Steinberg::IBStream& stream) noexcept : stream (stream) {} ByteOrderStream (ByteOrderStream&&) noexcept = delete; ByteOrderStream& operator= (ByteOrderStream&&) noexcept = delete; ByteOrderStream (const ByteOrderStream&) noexcept = delete; ByteOrderStream& operator= (const ByteOrderStream&) noexcept = delete; inline Result operator<< (const std::string& input) noexcept; inline Result operator>> (std::string& output) noexcept; template inline Result operator<< (const T& input) noexcept; template inline Result operator>> (T& output) const noexcept; inline Result read (const ReadBufferDesc& buffer) const noexcept; inline Result write (const WriteBufferDesc& buffer) noexcept; inline Result seek (SeekMode mode, int64_t bytes) const noexcept; inline Result tell () const noexcept; //------------------------------------------------------------------------ private: template inline Result swapAndWrite (const uint8_t* buffer) noexcept; inline void swap (uint8_t* buffer, uint64_t size) const noexcept; Steinberg::IBStream& stream; }; //------------------------------------------------------------------------ using LittleEndianStream = ByteOrderStream; using BigEndianStream = ByteOrderStream; using NativeEndianStream = ByteOrderStream; //------------------------------------------------------------------------ template inline Result ByteOrderStream::read (const ReadBufferDesc& buffer) const noexcept { if (buffer.bytes > static_cast (std::numeric_limits::max ())) return Result (Error::BufferToBig); Steinberg::int32 readBytes = 0; auto tres = stream.read (buffer.ptr, static_cast (buffer.bytes), &readBytes); if (tres != Steinberg::kResultTrue) return Result (Error::Unknown); assert (readBytes >= 0); return Result {Error::NoError, static_cast (readBytes)}; } //------------------------------------------------------------------------ template inline Result ByteOrderStream::write (const WriteBufferDesc& buffer) noexcept { if (buffer.bytes > static_cast (std::numeric_limits::max ())) return Result (Error::BufferToBig); Steinberg::int32 writtenBytes = 0; auto tres = stream.write (const_cast (buffer.ptr), static_cast (buffer.bytes), &writtenBytes); if (tres != Steinberg::kResultTrue) return Result (Error::Unknown); assert (writtenBytes >= 0); return Result {Error::NoError, static_cast (writtenBytes)}; } //------------------------------------------------------------------------ template inline Result ByteOrderStream::seek (SeekMode mode, int64_t bytes) const noexcept { Steinberg::int32 seekMode = 0; switch (mode) { case SeekMode::Set: seekMode = Steinberg::IBStream::kIBSeekSet; break; case SeekMode::Current: seekMode = Steinberg::IBStream::kIBSeekCur; break; case SeekMode::End: seekMode = Steinberg::IBStream::kIBSeekEnd; break; } Steinberg::int64 seekRes = 0; auto tres = stream.seek (static_cast (bytes), seekMode, &seekRes); if (tres != Steinberg::kResultTrue || seekRes < 0) return Result {Error::Unknown}; return Result (Error::NoError, static_cast (seekRes)); } //------------------------------------------------------------------------ template inline Result ByteOrderStream::tell () const noexcept { Steinberg::int64 tellRes = 0; auto tres = stream.tell (&tellRes); if (tres != Steinberg::kResultTrue || tellRes < 0) return Result {Error::Unknown}; return Result {Error::NoError, static_cast (tellRes)}; } //------------------------------------------------------------------------ template inline Result ByteOrderStream::operator<< (const std::string& input) noexcept { auto res = *this << static_cast (input.length ()); if (!res) return res; res = stream.write (const_cast (static_cast (input.data ())), static_cast (input.length ())); res.bytes += sizeof (uint64_t); return res; } //------------------------------------------------------------------------ template inline Result ByteOrderStream::operator>> (std::string& output) noexcept { uint64_t length; auto res = *this >> length; if (!res) return res; output.resize (length); if (length > 0) { res = stream.read (&output.front (), static_cast (length)); res.bytes += sizeof (uint64_t); } return res; } //------------------------------------------------------------------------ template template inline Result ByteOrderStream::operator<< (const T& input) noexcept { static_assert (std::is_standard_layout::value, "Only standard layout types allowed"); // with C++17: if constexpr (StreamByteOrder == BYTEORDER) if (constexpr bool tmp = (StreamByteOrder == BYTEORDER)) return write (WriteBufferDesc {sizeof (T), static_cast (&input)}); return swapAndWrite (reinterpret_cast (&input)); } //------------------------------------------------------------------------ template template inline Result ByteOrderStream::operator>> (T& output) const noexcept { static_assert (std::is_standard_layout::value, "Only standard layout types allowed"); auto res = read (ReadBufferDesc {sizeof (T), &output}); // with C++17: if constexpr (StreamByteOrder == BYTEORDER) if (constexpr bool tmp = (StreamByteOrder == BYTEORDER)) return res; swap (reinterpret_cast (&output), res.bytes); return res; } //------------------------------------------------------------------------ template template inline Result ByteOrderStream::swapAndWrite (const uint8_t* buffer) noexcept { // with C++17: if constexpr (_size > 1) if (constexpr bool tmp2 = (_size > 1)) { int8_t tmp[_size]; constexpr auto halfSize = _size / 2; auto size = _size; auto low = buffer; auto high = buffer + size - 1; while (size > halfSize) { tmp[size - 2] = buffer[(_size - size) + 1]; tmp[(_size - size) + 1] = buffer[size - 2]; tmp[_size - size] = *high; tmp[size - 1] = *low; low += 2; high -= 2; size -= 2; } return write (WriteBufferDesc {_size, tmp}); } return write (WriteBufferDesc {1, buffer}); } //------------------------------------------------------------------------ template inline void ByteOrderStream::swap (uint8_t* buffer, uint64_t size) const noexcept { if (size < 2) return; auto low = buffer; auto high = buffer + size - 1; while (size >= 2) { auto tmp = *low; *low = *high; *high = tmp; low += 2; high -= 2; size -= 2; } } //------------------------------------------------------------------------ } // IO //------------------------------------------------------------------------ constexpr int32_t cMagic = 'CcnK'; constexpr int32_t bankMagic = 'FxBk'; constexpr int32_t privateChunkID = 'VstW'; constexpr int32_t chunkBankMagic = 'FBCh'; constexpr int32_t programMagic = 'FxCk'; constexpr int32_t chunkProgramMagic = 'FPCh'; //------------------------------------------------------------------------ Optional loadProgram (const IO::BigEndianStream& state, const Optional& vst2xUniqueID) { Vst2xProgram program; int32_t id; if (!(state >> id)) return {}; if (id != cMagic) return {}; int32_t bankSize; if (!(state >> bankSize)) return {}; int32_t fxMagic; if (!(state >> fxMagic)) return {}; if (!(fxMagic == programMagic || fxMagic == chunkProgramMagic)) return {}; int32_t formatVersion; if (!(state >> formatVersion)) return {}; int32_t fxId; if (!(state >> fxId)) return {}; if (vst2xUniqueID && fxId != *vst2xUniqueID) return {}; int32_t fxVersion; if (!(state >> fxVersion)) return {}; int32_t numParams; if (!(state >> numParams)) return {}; if (numParams < 0) return {}; char name[29]; if (!state.read ({28, name})) return {}; name[28] = 0; program.name = name; program.fxUniqueID = fxId; program.fxVersion = fxVersion; if (fxMagic == chunkProgramMagic) { uint32_t chunkSize; if (!(state >> chunkSize)) return {}; program.chunk.resize (chunkSize); if (!state.read ({chunkSize, program.chunk.data ()})) return {}; } else { program.values.resize (numParams); float paramValue; for (int32_t i = 0; i < numParams; ++i) { if (!(state >> paramValue)) return {}; program.values[i] = paramValue; } } return {std::move (program)}; } //------------------------------------------------------------------------ bool loadPrograms (Steinberg::IBStream& stream, Vst2xState::Programs& programs, const Optional& vst2xUniqueID) { IO::BigEndianStream state (stream); for (auto& program : programs) { if (auto prg = loadProgram (state, vst2xUniqueID)) std::swap (program, *prg); else return false; } return true; } //------------------------------------------------------------------------ template IO::Error streamSizeWriter (StreamT& stream, Proc proc) { auto startPos = stream.tell (); if (startPos.error != IO::Error::NoError) return startPos.error; auto res = stream << static_cast (0); // placeholder if (!res) return res.error; auto procRes = proc (); if (procRes != IO::Error::NoError) return procRes; auto endPos = stream.tell (); if (endPos.error != IO::Error::NoError) return endPos.error; auto size = (endPos.bytes - startPos.bytes) - 4; auto typeSize = static_cast (size); if (size != static_cast (typeSize)) return IO::Error::Unknown; res = stream.seek (IO::SeekMode::Set, startPos.bytes); if (!res) return res.error; res = (stream << typeSize); if (!res) return res.error; res = stream.seek (IO::SeekMode::Set, endPos.bytes); return res.error; } //------------------------------------------------------------------------ template IO::Error writePrograms (StreamT& stream, const Vst2xState::Programs& programs) { for (const auto& program : programs) { auto res = stream << cMagic; if (!res) return res.error; res = streamSizeWriter (stream, [&] () { bool writeChunk = !program.chunk.empty (); if (!(res = stream << (writeChunk ? chunkProgramMagic : programMagic))) return res.error; int32_t version = 1; if (!(res = stream << version)) return res.error; if (!(res = stream << program.fxUniqueID)) return res.error; int32_t fxVersion = program.fxVersion; if (!(res = stream << fxVersion)) return res.error; uint32_t numParams = static_cast (program.values.size ()); if (!(res = stream << numParams)) return res.error; auto programName = program.name; programName.resize (28); for (auto c : programName) { if (!(res = stream << c)) return res.error; } if (writeChunk) { if (!(res = stream << static_cast (program.chunk.size ()))) return res.error; if (!(res = stream.write ({program.chunk.size (), program.chunk.data ()}))) return res.error; } else { for (auto value : program.values) { if (!(res = stream << value)) return res.error; } } return IO::Error::NoError; }); if (res.error != IO::Error::NoError) return res.error; } return IO::Error::NoError; } //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ Optional tryVst2StateLoad (Steinberg::IBStream& stream, Optional vst2xUniqueID) noexcept { Vst2xState result; IO::BigEndianStream state (stream); int32_t version; int32_t size; int32_t id; if (!(state >> id)) return {}; if (id == privateChunkID) { if (!(state >> size)) return {}; if (!(state >> version)) return {}; int32_t bypass; if (!(state >> bypass)) return {}; result.isBypassed = bypass ? true : false; if (!(state >> id)) return {}; } if (id != cMagic) return {}; int32_t bankSize; if (!(state >> bankSize)) return {}; int32_t fxMagic; if (!(state >> fxMagic)) return {}; if (!(fxMagic == bankMagic || fxMagic == chunkBankMagic)) return {}; int32_t bankVersion; if (!(state >> bankVersion)) return {}; int32_t fxId; if (!(state >> fxId)) return {}; if (vst2xUniqueID && fxId != *vst2xUniqueID) return {}; result.fxUniqueID = fxId; int32_t fxVersion; if (!(state >> fxVersion)) return {}; result.fxVersion = fxVersion; int32_t numPrograms; if (!(state >> numPrograms)) return {}; if (numPrograms < 1 && fxMagic == bankMagic) return {}; int32_t currentProgram = 0; if (bankVersion >= 1) { if (!(state >> currentProgram)) return {}; state.seek (IO::SeekMode::Current, 124); // future } result.currentProgram = currentProgram; if (fxMagic == bankMagic) { result.programs.resize (numPrograms); if (!loadPrograms (stream, result.programs, vst2xUniqueID)) return {}; assert (static_cast (result.programs.size ()) > currentProgram); } else { uint32_t chunkSize; if (!(state >> chunkSize)) return {}; if (chunkSize == 0) return {}; result.chunk.resize (chunkSize); if (!state.read ({chunkSize, result.chunk.data ()})) return {}; } return {std::move (result)}; } //------------------------------------------------------------------------ bool writeVst2State (const Vst2xState& state, Steinberg::IBStream& _stream, bool writeBypassState) noexcept { IO::BigEndianStream stream (_stream); if (writeBypassState) { if (!(stream << privateChunkID)) return false; if (streamSizeWriter (stream, [&] () { uint32_t version = 1; auto res = (stream << version); if (!res) return res.error; int32_t bypass = state.isBypassed ? 1 : 0; return (stream << bypass).error; }) != IO::Error::NoError) { return false; } } if (!(stream << cMagic)) { return false; } if (streamSizeWriter (stream, [&] () { bool writeChunk = !state.chunk.empty (); IO::Result res; if (!(res = (stream << (writeChunk ? chunkBankMagic : bankMagic)))) return res.error; int32_t bankVersion = 2; if (!(res = (stream << bankVersion))) return res.error; if (!(res = (stream << state.fxUniqueID))) return res.error; if (!(res = (stream << state.fxVersion))) return res.error; int32_t numPrograms = writeChunk ? 1 : static_cast (state.programs.size ()); if (!(res = (stream << numPrograms))) return res.error; if (bankVersion > 1) { if (!(res = (stream << state.currentProgram))) return res.error; // write 124 zero bytes uint8_t byte = 0; for (uint32_t i = 0; i < 124; ++i) if (!(res = (stream << byte))) return res.error; } if (writeChunk) { auto chunkSize = static_cast (state.chunk.size ()); if (!(res = (stream << chunkSize))) return res.error; stream.write ({state.chunk.size (), state.chunk.data ()}); } else { writePrograms (stream, state.programs); } return IO::Error::NoError; }) != IO::Error::NoError) { return false; } return true; } //------------------------------------------------------------------------ Optional tryVst2ProgramLoad (Steinberg::IBStream& stream, Optional vst2xUniqueID) noexcept { IO::BigEndianStream state (stream); return loadProgram (state, vst2xUniqueID); } //------------------------------------------------------------------------ } // VST3 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/stringconvert.h0000644000000000000000000000012715124701711025142 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/stringconvert.h0000644000175000001440000001454515124701711025137 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/stringconvert.h // Created by : Steinberg, 11/2014 // Description : c++11 unicode string convert functions // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/vsttypes.h" #include namespace Steinberg { namespace Vst { namespace StringConvert { //------------------------------------------------------------------------ /** * Forward to Steinberg::StringConvert::convert (...) */ std::u16string convert (const std::string& utf8Str); //------------------------------------------------------------------------ /** * Forward to Steinberg::StringConvert::convert (...) */ std::string convert (const std::u16string& str); //------------------------------------------------------------------------ /** * Forward to Steinberg::StringConvert::convert (...) */ std::string convert (const char* str, uint32_t max); //------------------------------------------------------------------------ /** * convert an UTF-8 string to an UTF-16 string buffer with max 127 characters * * @param utf8Str UTF-8 string * @param str UTF-16 string buffer * * @return true on success */ bool convert (const std::string& utf8Str, Steinberg::Vst::String128 str); //------------------------------------------------------------------------ /** * convert an UTF-8 string to an UTF-16 string buffer * * @param utf8Str UTF-8 string * @param str UTF-16 string buffer * @param maxCharacters max characters that fit into str * * @return true on success */ bool convert (const std::string& utf8Str, Steinberg::Vst::TChar* str, uint32_t maxCharacters); //------------------------------------------------------------------------ /** * convert an UTF-16 string buffer to an UTF-8 string * * @param str UTF-16 string buffer * * @return UTF-8 string */ std::string convert (const Steinberg::Vst::TChar* str); //------------------------------------------------------------------------ /** * convert an UTF-16 string buffer to an UTF-8 string * * @param str UTF-16 string buffer * @param max maximum characters in str * * @return UTF-8 string */ std::string convert (const Steinberg::Vst::TChar* str, uint32_t max); //------------------------------------------------------------------------ } // StringConvert //------------------------------------------------------------------------ inline const Steinberg::Vst::TChar* toTChar (const std::u16string& str) { return reinterpret_cast (str.data ()); } //------------------------------------------------------------------------ /** * convert a number to an UTF-16 string * * @param value number * * @return UTF-16 string */ template std::u16string toString (NumberT value) { auto u8str = std::to_string (value); return StringConvert::convert (u8str); } //------------------------------------------------------------------------ } // Vst } // Steinberg //------------------------------------------------------------------------ // Deprecated VST3 namespace //------------------------------------------------------------------------ namespace VST3 { namespace StringConvert { //------------------------------------------------------------------------ SMTG_DEPRECATED_MSG ("Use Steinberg::Vst::StringConvert::convert (...)") inline std::u16string convert (const std::string& utf8Str) { return Steinberg::Vst::StringConvert::convert (utf8Str); } //------------------------------------------------------------------------ SMTG_DEPRECATED_MSG ("Use Steinberg::Vst::StringConvert::convert (...)") inline std::string convert (const std::u16string& str) { return Steinberg::Vst::StringConvert::convert (str); } //------------------------------------------------------------------------ SMTG_DEPRECATED_MSG ("Use Steinberg::Vst::StringConvert::convert (...)") inline std::string convert (const char* str, uint32_t max) { return Steinberg::Vst::StringConvert::convert (str, max); } //------------------------------------------------------------------------ SMTG_DEPRECATED_MSG ("Use Steinberg::Vst::StringConvert::convert (...)") inline bool convert (const std::string& utf8Str, Steinberg::Vst::String128 str) { return Steinberg::Vst::StringConvert::convert (utf8Str, str); } //------------------------------------------------------------------------ SMTG_DEPRECATED_MSG ("Use Steinberg::Vst::StringConvert::convert (...)") inline bool convert (const std::string& utf8Str, Steinberg::Vst::TChar* str, uint32_t maxCharacters) { return Steinberg::Vst::StringConvert::convert (utf8Str, str, maxCharacters); } //------------------------------------------------------------------------ SMTG_DEPRECATED_MSG ("Use Steinberg::Vst::StringConvert::convert (...)") inline std::string convert (const Steinberg::Vst::TChar* str) { return Steinberg::Vst::StringConvert::convert (str); } //------------------------------------------------------------------------ SMTG_DEPRECATED_MSG ("Use Steinberg::Vst::StringConvert::convert (...)") inline std::string convert (const Steinberg::Vst::TChar* str, uint32_t max) { return Steinberg::Vst::StringConvert::convert (str, max); } //------------------------------------------------------------------------ } // StringConvert //------------------------------------------------------------------------ SMTG_DEPRECATED_MSG ("Use Steinberg::Vst::toTChar (...)") inline const Steinberg::Vst::TChar* toTChar (const std::u16string& str) { return Steinberg::Vst::toTChar (str); } //------------------------------------------------------------------------ template SMTG_DEPRECATED_MSG ("Use Steinberg::Vst::toString (...)") std::u16string toString (NumberT value) { return Steinberg::Vst::toString (value); } //------------------------------------------------------------------------ } // VST3 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/ump.h0000644000000000000000000000013115124701711023027 xustar0030 mtime=1767080905.286210845 29 atime=1767080905.28618869 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/ump.h0000644000175000001440000010617715124701711023034 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // Category : Helpers // Filename : public.sdk/source/vst/utility/ump.h // Created by : Steinberg, 12/2023 // Description : a single c++17 header UMP parser implementation without other dependencies than // to std::array // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #ifndef SMTG_ALWAYS_INLINE #if _MSVC #define SMTG_ALWAYS_INLINE __forceinline #else #define SMTG_ALWAYS_INLINE __inline__ __attribute__ ((__always_inline__)) #endif #endif #include #include #include //------------------------------------------------------------------------ namespace Steinberg::Vst::UMP { struct IUniversalMidiPacketHandler; enum class ParsingAction : uint32_t; //------------------------------------------------------------------------ /** the parse sections control which messages to process of UMP * * the generated code is smaller and runtime is faster if only the needed sections are enabled. */ enum ParseSections : uint8_t { Utility = 1 << 0, System = 1 << 1, ChannelVoice1 = 1 << 2, SysEx = 1 << 3, ChannelVoice2 = 1 << 3, Data128 = 1 << 4, All = 0xff }; //------------------------------------------------------------------------ /** stateless parsing universal MIDI packets * * @tparam sections which sections to parse * @param numWords number of 32-bit words * @param words pointer to a words array * @param handler callback handler * @return number of successfully processed words */ template size_t parsePackets (const size_t numWords, const uint32_t* words, const IUniversalMidiPacketHandler& handler); //------------------------------------------------------------------------ struct IUniversalMidiPacketHandler { using Group = uint8_t; using Channel = uint8_t; using Index = uint8_t; using NoteNumber = uint8_t; using BankNumber = uint8_t; using ControllerNumber = uint8_t; using Velocity8 = uint8_t; using Velocity16 = uint16_t; using AttributeType = uint8_t; using AttributeValue = uint16_t; using OptionFlags = uint8_t; using Data8 = uint8_t; using Data32 = uint32_t; using Program = uint8_t; using BankMSB = uint8_t; using BankLSB = uint8_t; using Timestamp = uint16_t; using Timecode = uint8_t; using StreamID = uint8_t; using SysEx6ByteData = const std::array&; using SysEx13ByteData = const std::array&; using MixedData = const std::array&; // UTILITY virtual void onNoop (Group group) const = 0; virtual void onJitterClock (Group group, Timestamp time) const = 0; virtual void onJitterTimestamp (Group group, Timestamp time) const = 0; // SYSTEM COMMON virtual void onMIDITimeCode (Group group, Timecode timecode) const = 0; virtual void onSongPositionPointer (Group group, uint8_t posLSB, uint8_t posMSB) const = 0; virtual void onSongSelect (Group group, uint8_t songIndex) const = 0; virtual void onTuneRequest (Group group) const = 0; // SYSTEM REALTIME enum class SystemRealtime { TimingClock, Start, Continue, Stop, ActiveSensing, Reset }; virtual void onSystemRealtime (Group group, SystemRealtime which) const = 0; // MIDI 1.0 CHANNEL VOICE Messages virtual void onMidi1NoteOff (Group group, Channel channel, NoteNumber note, Velocity8 velocity) const = 0; virtual void onMidi1NoteOn (Group group, Channel channel, NoteNumber note, Velocity8 velocity) const = 0; virtual void onMidi1PolyPressure (Group group, Channel channel, NoteNumber note, Data8 data) const = 0; virtual void onMidi1ControlChange (Group group, Channel channel, ControllerNumber controller, Data8 value) const = 0; virtual void onMidi1ProgramChange (Group group, Channel channel, Program program) const = 0; virtual void onMidi1ChannelPressure (Group group, Channel channel, Data8 pressure) const = 0; virtual void onMidi1PitchBend (Group group, Channel channel, Data8 valueLSB, Data8 valueMSB) const = 0; // DATA 64 BIT virtual void onSysExPacket (Group group, SysEx6ByteData data) const = 0; virtual void onSysExStart (Group group, SysEx6ByteData data) const = 0; virtual void onSysExContinue (Group group, SysEx6ByteData data) const = 0; virtual void onSysExEnd (Group group, SysEx6ByteData data) const = 0; // MIDI 2.0 CHANNEL VOICE Messages virtual void onRegisteredPerNoteController (Group group, Channel channel, NoteNumber note, ControllerNumber controller, Data32 data) const = 0; virtual void onAssignablePerNoteController (Group group, Channel channel, NoteNumber note, ControllerNumber controller, Data32 data) const = 0; virtual void onRegisteredController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const = 0; virtual void onAssignableController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const = 0; virtual void onRelativeRegisteredController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const = 0; virtual void onRelativeAssignableController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const = 0; virtual void onPerNotePitchBend (Group group, Channel channel, NoteNumber note, Data32 data) const = 0; virtual void onNoteOff (Group group, Channel channel, NoteNumber note, Velocity16 velocity, AttributeType attr, AttributeValue attrValue) const = 0; virtual void onNoteOn (Group group, Channel channel, NoteNumber note, Velocity16 velocity, AttributeType attr, AttributeValue attrValue) const = 0; virtual void onPolyPressure (Group group, Channel channel, NoteNumber note, Data32 data) const = 0; virtual void onControlChange (Group group, Channel channel, ControllerNumber controller, Data32 data) const = 0; virtual void onProgramChange (Group group, Channel channel, OptionFlags options, Program program, BankMSB bankMSB, BankLSB bankLSB) const = 0; virtual void onChannelPressure (Group group, Channel channel, Data32 data) const = 0; virtual void onPitchBend (Group group, Channel channel, Data32 data) const = 0; virtual void onPerNoteManagement (Group group, Channel channel, NoteNumber note, OptionFlags options) const = 0; // DATA 128 BIT virtual void onSysEx8Packet (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const = 0; virtual void onSysEx8Start (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const = 0; virtual void onSysEx8Continue (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const = 0; virtual void onSysEx8End (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const = 0; virtual void onMixedDataSetHeader (Group group, Index mdsID, MixedData data) const = 0; virtual void onMixedDataSetPayload (Group group, Index mdsID, MixedData data) const = 0; // invalid input data virtual ParsingAction onInvalidInputData (size_t index) const = 0; // insufficient input data virtual void onInsufficentInputData (size_t index, size_t numMissingWords) const = 0; }; //------------------------------------------------------------------------ enum class ParsingAction : uint32_t { Break, Continue }; namespace Detail { //------------------------------------------------------------------------ enum class MessageType : uint8_t { Utility = 0x0, // 1 word System = 0x1, // 1 word ChannelVoice1 = 0x2, // 1 word SysEx = 0x3, // 2 words ChannelVoice2 = 0x4, // 2 words Data128 = 0x5, // 4 words Unknown6 = 0x6, // 1 word Unknown7 = 0x7, // 1 word Unknown8 = 0x8, // 2 words Unknown9 = 0x9, // 2 words UnknownA = 0xa, // 2 words UnknownB = 0xb, // 3 words UnknownC = 0xc, // 3 words UnknownD = 0xd, // 4 words UnknownE = 0xe, // 4 words UnknownF = 0xf, // 4 words }; //------------------------------------------------------------------------ struct UMPMessage { uint32_t data; SMTG_ALWAYS_INLINE constexpr MessageType type () const { auto val = (data & 0xf0000000ul) >> 28; return static_cast (val); } SMTG_ALWAYS_INLINE constexpr size_t messageWordCount () const { auto index = static_cast (type ()); return wordCounts[index]; } SMTG_ALWAYS_INLINE constexpr uint8_t group () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte3_7bits () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte4_7bits () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte1 () const { return value (data); } SMTG_ALWAYS_INLINE constexpr uint8_t byte2 () const { return value (data); } SMTG_ALWAYS_INLINE constexpr uint8_t byte3 () const { return value (data); } SMTG_ALWAYS_INLINE constexpr uint8_t byte4 () const { return value (data); } protected: static constexpr std::array wordCounts = {1u, 1u, 1u, 2u, 2u, 4u, 1u, 1u, 2u, 2u, 2u, 3u, 3u, 4u, 4u, 4u}; template SMTG_ALWAYS_INLINE constexpr T bitMask () const { return ((static_cast (1) << bits) - 1) << (((sizeof (T) * 8) - bits) - pos); } template SMTG_ALWAYS_INLINE constexpr T value (T data) const { return (data & bitMask ()) >> (((sizeof (T) * 8) - bits) - pos); } }; //------------------------------------------------------------------------ struct UMPMessage2 : UMPMessage { uint32_t data2; SMTG_ALWAYS_INLINE constexpr uint8_t byte5_7bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte6_7bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte7_7bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte8_7bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint16_t byte5_16bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint16_t byte7_16bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte5 () const { return value (data2); } SMTG_ALWAYS_INLINE constexpr uint8_t byte6 () const { return value (data2); } SMTG_ALWAYS_INLINE constexpr uint8_t byte7 () const { return value (data2); } SMTG_ALWAYS_INLINE constexpr uint8_t byte8 () const { return value (data2); } #if SMTG_OS_MACOS // TO REMOVE: We had to add a duplicate this function from UMPMessage // for older XCode than 15 to fix a linker issue SMTG_ALWAYS_INLINE constexpr uint8_t group () const { return static_cast (value (data)); } #endif }; //------------------------------------------------------------------------ struct UMPMessage4 : UMPMessage2 { uint32_t data3; uint32_t data4; SMTG_ALWAYS_INLINE constexpr uint8_t byte9 () const { return value (data3); } SMTG_ALWAYS_INLINE constexpr uint8_t byte10 () const { return value (data3); } SMTG_ALWAYS_INLINE constexpr uint8_t byte11 () const { return value (data3); } SMTG_ALWAYS_INLINE constexpr uint8_t byte12 () const { return value (data3); } SMTG_ALWAYS_INLINE constexpr uint8_t byte13 () const { return value (data4); } SMTG_ALWAYS_INLINE constexpr uint8_t byte14 () const { return value (data4); } SMTG_ALWAYS_INLINE constexpr uint8_t byte15 () const { return value (data4); } SMTG_ALWAYS_INLINE constexpr uint8_t byte16 () const { return value (data4); } }; static_assert (sizeof (UMPMessage) == sizeof (uint32_t), ""); static_assert (sizeof (UMPMessage2) == sizeof (uint32_t) * 2, ""); static_assert (sizeof (UMPMessage4) == sizeof (uint32_t) * 4, ""); //------------------------------------------------------------------------ struct UMPMessageUtility : UMPMessage { enum class Status { Noop = 0x0, JrClock = 0x1, JrTimestamp = 0x2, }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr IUniversalMidiPacketHandler::Timestamp timestamp () const { return static_cast (value (data)); } }; //------------------------------------------------------------------------ struct UMPMessageSystem : UMPMessage { enum class Status { Reserved = 0xf0, MIDITimeCode = 0xf1, SongPositionPointer = 0xf2, SongSelect = 0xf3, Reserved2 = 0xf4, Reserved3 = 0xf5, TuneRequest = 0xf6, Reserved4 = 0xf7, TimingClock = 0xf8, Reserved5 = 0xf9, Start = 0xfa, Continue = 0xfb, Stop = 0xfc, Reserved6 = 0xfd, ActiveSensing = 0xfe, Reset = 0xff, }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } }; //------------------------------------------------------------------------ struct UMPMessageChannelVoice1 : UMPMessage { enum class Status { NoteOff = 0x8, NoteOn = 0x9, PolyPressure = 0xa, ControlChange = 0xb, ProgramChange = 0xc, ChannelPressure = 0xd, PitchBend = 0xe, }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint32_t channel () const { return value (data); } }; //------------------------------------------------------------------------ struct UMPMessageSysEx : UMPMessage2 { enum class Status { Packet = 0x0, Start = 0x1, Continue = 0x2, End = 0x3, }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } }; //------------------------------------------------------------------------ struct UMPMessageChannelVoice2 : UMPMessage2 { enum class Status { RegisteredPerNoteController = 0x0, AssignablePerNoteController = 0x1, RegisteredController = 0x2, AssignableController = 0x3, RelativeRegisteredController = 0x4, RelativeAssignableController = 0x5, PerNotePitchBend = 0x6, NoteOff = 0x8, NoteOn = 0x9, PolyPressure = 0xa, ControlChange = 0xb, ProgramChange = 0xc, ChannelPressure = 0xd, PitchBend = 0xe, PerNoteManagement = 0xf }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint32_t channel () const { return value (data); } }; //------------------------------------------------------------------------ struct UMPMessageData128 : UMPMessage4 { enum class Status { Packet = 0x0, Start = 0x1, Continue = 0x2, End = 0x3, MixedHeader = 0x8, MixedPayload = 0x9 }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint8_t numBytes () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint8_t mdsId () const { return numBytes (); } }; //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onUtilityMessage (const UMPMessageUtility& msg, const IUniversalMidiPacketHandler& handler) { auto status = msg.status (); using Status = UMPMessageUtility::Status; switch (status) { case Status::Noop: { handler.onNoop (msg.group ()); break; } case Status::JrClock: { handler.onJitterClock (msg.group (), msg.timestamp ()); break; } case Status::JrTimestamp: { handler.onJitterTimestamp (msg.group (), msg.timestamp ()); break; } default: return false; } return true; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onSystemMessage (const UMPMessageSystem& msg, const IUniversalMidiPacketHandler& handler) { auto status = msg.status (); using Status = UMPMessageSystem::Status; using SystemRealtime = IUniversalMidiPacketHandler::SystemRealtime; switch (status) { case Status::MIDITimeCode: { handler.onMIDITimeCode (msg.group (), msg.byte3_7bits ()); break; } case Status::SongPositionPointer: { handler.onSongPositionPointer (msg.group (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } case Status::SongSelect: { handler.onSongSelect (msg.group (), msg.byte3_7bits ()); break; } case Status::TuneRequest: { handler.onTuneRequest (msg.group ()); break; } case Status::TimingClock: { handler.onSystemRealtime (msg.group (), SystemRealtime::TimingClock); break; } case Status::Start: { handler.onSystemRealtime (msg.group (), SystemRealtime::Start); break; } case Status::Continue: { handler.onSystemRealtime (msg.group (), SystemRealtime::Continue); break; } case Status::Stop: { handler.onSystemRealtime (msg.group (), SystemRealtime::Stop); break; } case Status::ActiveSensing: { handler.onSystemRealtime (msg.group (), SystemRealtime::ActiveSensing); break; } case Status::Reset: { handler.onSystemRealtime (msg.group (), SystemRealtime::Reset); break; } default: return false; } return true; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onChannelVoice1Message (const UMPMessageChannelVoice1& msg, const IUniversalMidiPacketHandler& handler) { using Status = UMPMessageChannelVoice1::Status; switch (msg.status ()) { case Status::NoteOff: { handler.onMidi1NoteOff (msg.group (), msg.channel (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } case Status::NoteOn: { handler.onMidi1NoteOn (msg.group (), msg.channel (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } case Status::PolyPressure: { handler.onMidi1PolyPressure (msg.group (), msg.channel (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } case Status::ControlChange: { handler.onMidi1ControlChange (msg.group (), msg.channel (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } case Status::ProgramChange: { handler.onMidi1ProgramChange (msg.group (), msg.channel (), msg.byte3_7bits ()); break; } case Status::ChannelPressure: { handler.onMidi1ChannelPressure (msg.group (), msg.channel (), msg.byte3_7bits ()); break; } case Status::PitchBend: { handler.onMidi1PitchBend (msg.group (), msg.channel (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } default: return false; } return true; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onSysExMessage (const UMPMessageSysEx& msg, const IUniversalMidiPacketHandler& handler) { using Status = UMPMessageSysEx::Status; switch (msg.status ()) { case Status::Packet: { handler.onSysExPacket (msg.group (), {msg.byte3_7bits (), msg.byte4_7bits (), msg.byte5_7bits (), msg.byte6_7bits (), msg.byte7_7bits (), msg.byte8_7bits ()}); break; } case Status::Start: { handler.onSysExStart (msg.group (), {msg.byte3_7bits (), msg.byte4_7bits (), msg.byte5_7bits (), msg.byte6_7bits (), msg.byte7_7bits (), msg.byte8_7bits ()}); break; } case Status::Continue: { handler.onSysExContinue (msg.group (), {msg.byte3_7bits (), msg.byte4_7bits (), msg.byte5_7bits (), msg.byte6_7bits (), msg.byte7_7bits (), msg.byte8_7bits ()}); break; } case Status::End: { handler.onSysExEnd (msg.group (), {msg.byte3_7bits (), msg.byte4_7bits (), msg.byte5_7bits (), msg.byte6_7bits (), msg.byte7_7bits (), msg.byte8_7bits ()}); break; } default: return false; } return true; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onChannelVoice2Message (const UMPMessageChannelVoice2& msg, const IUniversalMidiPacketHandler& handler) { auto group = msg.group (); auto status = msg.status (); auto channel = msg.channel (); using Status = UMPMessageChannelVoice2::Status; switch (status) { case Status::RegisteredPerNoteController: { handler.onRegisteredPerNoteController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::AssignablePerNoteController: { handler.onAssignablePerNoteController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::RegisteredController: { handler.onRegisteredController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::AssignableController: { handler.onAssignableController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::RelativeRegisteredController: { handler.onRelativeRegisteredController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::RelativeAssignableController: { handler.onRelativeAssignableController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::PerNotePitchBend: { handler.onPerNotePitchBend (group, channel, msg.byte3_7bits (), msg.data2); break; } case Status::NoteOn: { handler.onNoteOn (group, channel, msg.byte3_7bits (), msg.byte5_16bits (), msg.byte4_7bits (), msg.byte7_16bits ()); break; } case Status::NoteOff: { handler.onNoteOff (group, channel, msg.byte3_7bits (), msg.byte5_16bits (), msg.byte4_7bits (), msg.byte7_16bits ()); break; } case Status::PolyPressure: { handler.onPolyPressure (group, channel, msg.byte3_7bits (), msg.data2); break; } case Status::ControlChange: { handler.onControlChange (group, channel, msg.byte3_7bits (), msg.data2); break; } case Status::ProgramChange: { handler.onProgramChange (group, channel, msg.byte4_7bits (), msg.byte5_7bits (), msg.byte7_7bits (), msg.byte8_7bits ()); break; } case Status::ChannelPressure: { handler.onChannelPressure (group, channel, msg.data2); break; } case Status::PitchBend: { handler.onPitchBend (group, channel, msg.data2); break; } case Status::PerNoteManagement: { handler.onPerNoteManagement (group, channel, msg.byte3_7bits (), msg.byte4_7bits ()); break; } default: return false; } return true; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onData128Message (const UMPMessageData128& msg, const IUniversalMidiPacketHandler& handler) { using Status = UMPMessageData128::Status; switch (msg.status ()) { case Status::Packet: { handler.onSysEx8Packet (msg.group (), msg.numBytes (), msg.byte3 (), {msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } case Status::Start: { handler.onSysEx8Start (msg.group (), msg.numBytes (), msg.byte3 (), {msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } case Status::Continue: { handler.onSysEx8Continue (msg.group (), msg.numBytes (), msg.byte3 (), {msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } case Status::End: { handler.onSysEx8End (msg.group (), msg.numBytes (), msg.byte3 (), {msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } case Status::MixedHeader: { handler.onMixedDataSetHeader (msg.group (), msg.mdsId (), {msg.byte3 (), msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } case Status::MixedPayload: { handler.onMixedDataSetPayload (msg.group (), msg.mdsId (), {msg.byte3 (), msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } default: return false; } return true; } //------------------------------------------------------------------------ template SMTG_ALWAYS_INLINE size_t parse (const size_t numWords, const uint32_t* words, const IUniversalMidiPacketHandler& handler) { auto msg = reinterpret_cast (words); for (size_t index = 0; index < numWords;) { auto numMsgWords = msg->messageWordCount (); if (index + numMsgWords > numWords) { handler.onInsufficentInputData (index, (index + numMsgWords) - numWords); return index; } switch (msg->type ()) { case MessageType::Utility: { if constexpr ((Sections & ParseSections::Utility) != 0) { auto utilityMsg = reinterpret_cast (msg); if (!onUtilityMessage (*utilityMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } case MessageType::System: { if constexpr ((Sections & ParseSections::System) != 0) { auto systemMsg = reinterpret_cast (msg); if (!onSystemMessage (*systemMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } case MessageType::ChannelVoice1: { if constexpr ((Sections & ParseSections::ChannelVoice1) != 0) { auto channelMsg = reinterpret_cast (msg); if (!onChannelVoice1Message (*channelMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } case MessageType::SysEx: { if constexpr ((Sections & ParseSections::SysEx) != 0) { assert (index < numWords - 1); auto sysExMsg = reinterpret_cast (msg); if (!onSysExMessage (*sysExMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } case MessageType::ChannelVoice2: { if constexpr ((Sections & ParseSections::ChannelVoice2) != 0) { assert (index < numWords - 1); auto channelMsg = reinterpret_cast (msg); if (!onChannelVoice2Message (*channelMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } case MessageType::Data128: { if constexpr ((Sections & ParseSections::Data128) != 0) { assert (index < numWords - 3); auto dataMsg = reinterpret_cast (msg); if (!onData128Message (*dataMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } } index += numMsgWords; msg += numMsgWords; } return numWords; } //------------------------------------------------------------------------ } // Detail //------------------------------------------------------------------------ template SMTG_ALWAYS_INLINE size_t parsePackets (const size_t numWords, const uint32_t* words, const IUniversalMidiPacketHandler& handler) { return Detail::parse (numWords, words, handler); } //------------------------------------------------------------------------ struct UniversalMidiPacketHandlerAdapter : IUniversalMidiPacketHandler { void onNoop (Group group) const override {} void onJitterClock (Group group, Timestamp time) const override {} void onJitterTimestamp (Group group, Timestamp time) const override {} void onMIDITimeCode (Group group, Timecode timecode) const override {} void onSongPositionPointer (Group group, uint8_t posLSB, uint8_t posMSB) const override {} void onSongSelect (Group group, uint8_t songIndex) const override {} void onTuneRequest (Group group) const override {} void onSystemRealtime (Group group, SystemRealtime which) const override {} void onMidi1NoteOff (Group group, Channel channel, NoteNumber note, Velocity8 velocity) const override { } void onMidi1NoteOn (Group group, Channel channel, NoteNumber note, Velocity8 velocity) const override { } void onMidi1PolyPressure (Group group, Channel channel, NoteNumber note, Data8 data) const override { } void onMidi1ControlChange (Group group, Channel channel, ControllerNumber controller, Data8 value) const override { } void onMidi1ProgramChange (Group group, Channel channel, Program program) const override {} void onMidi1ChannelPressure (Group group, Channel channel, Data8 pressure) const override {} void onMidi1PitchBend (Group group, Channel channel, Data8 valueLSB, Data8 valueMSB) const override { } void onSysExPacket (Group group, SysEx6ByteData data) const override {} void onSysExStart (Group group, SysEx6ByteData data) const override {} void onSysExContinue (Group group, SysEx6ByteData data) const override {} void onSysExEnd (Group group, SysEx6ByteData data) const override {} void onRegisteredPerNoteController (Group group, Channel channel, NoteNumber note, ControllerNumber controller, Data32 data) const override { } void onAssignablePerNoteController (Group group, Channel channel, NoteNumber note, ControllerNumber controller, Data32 data) const override { } void onRegisteredController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const override { } void onAssignableController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const override { } void onRelativeRegisteredController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const override { } void onRelativeAssignableController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const override { } void onPerNotePitchBend (Group group, Channel channel, NoteNumber note, Data32 data) const override { } void onNoteOff (Group group, Channel channel, NoteNumber note, Velocity16 velocity, AttributeType attr, AttributeValue attrValue) const override { } void onNoteOn (Group group, Channel channel, NoteNumber note, Velocity16 velocity, AttributeType attr, AttributeValue attrValue) const override { } void onPolyPressure (Group group, Channel channel, NoteNumber note, Data32 data) const override { } void onControlChange (Group group, Channel channel, ControllerNumber controller, Data32 data) const override { } void onProgramChange (Group group, Channel channel, OptionFlags options, Program program, BankMSB bankMSB, BankLSB bankLSB) const override { } void onChannelPressure (Group group, Channel channel, Data32 data) const override {} void onPitchBend (Group group, Channel channel, Data32 data) const override {} void onPerNoteManagement (Group group, Channel channel, NoteNumber note, OptionFlags options) const override { } void onSysEx8Packet (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const override { } void onSysEx8Start (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const override { } void onSysEx8Continue (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const override { } void onSysEx8End (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const override { } void onMixedDataSetHeader (Group group, Index mdsID, MixedData data) const override {} void onMixedDataSetPayload (Group group, Index mdsID, MixedData data) const override {} ParsingAction onInvalidInputData (size_t index) const override { return ParsingAction::Continue; } void onInsufficentInputData (size_t index, size_t numMissingWords) const override {} }; //------------------------------------------------------------------------ } // Steinberg::Vst::EditorHost qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/ringbuffer.h0000644000000000000000000000012715124701711024364 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/ringbuffer.h0000644000175000001440000001056515124701711024357 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/ringbuffer.h // Created by : Steinberg, 01/2018 // Description : Simple RingBuffer with one reader and one writer thread // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ftypes.h" #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace OneReaderOneWriter { //------------------------------------------------------------------------ /** Ringbuffer * * A ringbuffer supporting one reader and one writer thread */ template class RingBuffer { private: using AtomicUInt32 = std::atomic; using Index = uint32; using StorageT = std::vector; StorageT buffer; Index readPosition {0u}; Index writePosition {0u}; AtomicUInt32 elementCount {0u}; public: /** Default constructor * * @param initialNumberOfItems initial ring buffer size */ RingBuffer (size_t initialNumberOfItems = 0) noexcept { if (initialNumberOfItems) resize (initialNumberOfItems); } /** size * * @return number of elements the buffer can hold */ size_t size () const noexcept { return buffer.size (); } /** resize * * note that you have to make sure that no other thread is reading or writing while calling * this method * @param newNumberOfItems resize buffer */ void resize (size_t newNumberOfItems) noexcept { buffer.resize (newNumberOfItems); } /** push a new item into the ringbuffer * * @param item to push * @return true on success or false if buffer is full */ bool push (ItemT&& item) noexcept { if (elementCount.load () == buffer.size ()) return false; // full auto pos = writePosition; buffer[pos] = std::move (item); elementCount++; ++pos; if (pos >= buffer.size ()) pos = 0u; writePosition = pos; return true; } /** push a new item into the ringbuffer * * @param item to push * @return true on success or false if buffer is full */ bool push (const ItemT& item) noexcept { if (elementCount.load () == buffer.size ()) return false; // full auto pos = writePosition; buffer[pos] = item; elementCount++; ++pos; if (pos >= buffer.size ()) pos = 0u; writePosition = pos; return true; } /** push multiple items at once into the ringbuffer * * if there are insufficient free slots in the ring buffer, no item will be pushed. * furthermore, it is guaranteed that the newly added items can only be popped from the buffer * after all items have been added. * * @param items list of items to push * @return true on success or false if there's not enough free space in the buffer */ bool push (const std::initializer_list& items) noexcept { if (items.size () == 0) return true; uint32 elementsPushed = 0u; auto freeElementCount = buffer.size () - elementCount.load (); if (freeElementCount < items.size ()) return false; auto pos = writePosition; for (const auto& el : items) { buffer[pos] = el; ++pos; if (pos >= buffer.size ()) pos = 0u; ++elementsPushed; } while (true) { uint32 expected = elementCount.load (); uint32 desired = expected + elementsPushed; if (elementCount.compare_exchange_strong (expected, desired)) break; } writePosition = pos; return elementsPushed; } /** pop an item out of the ringbuffer * * @param item * @return true on success or false if buffer is empty */ bool pop (ItemT& item) noexcept { if (elementCount.load () == 0) return false; // empty auto pos = readPosition; item = std::move (buffer[pos]); elementCount--; ++pos; if (pos >= buffer.size ()) pos = 0; readPosition = pos; return true; } }; //------------------------------------------------------------------------ } // OneReaderOneWriter } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/processdataslicer.h0000644000000000000000000000012715124701711025745 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/processdataslicer.h0000644000175000001440000000724315124701711025737 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/processdataslicer.h // Created by : Steinberg, 04/2021 // Description : Process the process data in slices // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstaudioprocessor.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Process Data Slicer * * Cuts the VST process data into slices to process * * Example: * \code{.cpp} * tresult PLUGIN_API Processor::process (ProcessData& data) * { * ProcessDataSlicer slicer (32); * slicer.process (data, [&] (ProcessData& data) { * doSlicedProcessing (data); // data.numSamples <= 32 * }); * } * \endcode */ class ProcessDataSlicer { public: /** Constructor * * @param inSliceSice slice size in samples */ ProcessDataSlicer (int32 inSliceSize = 8) : sliceSize (inSliceSize) {} //------------------------------------------------------------------------ /** Process the data * * @tparam SampleSize sample size 32 or 64 bit processing * @tparam DoProcessCallback the callback proc * @param data Process data * @param doProcessing process callback */ template void process (ProcessData& data, DoProcessCallback doProcessing) noexcept { stopIt = false; auto numSamples = data.numSamples; auto samplesLeft = data.numSamples; while (samplesLeft > 0 && !stopIt) { auto currentSliceSize = samplesLeft > sliceSize ? sliceSize : samplesLeft; data.numSamples = currentSliceSize; doProcessing (data); advanceBuffers (data.inputs, data.numInputs, currentSliceSize); advanceBuffers (data.outputs, data.numOutputs, currentSliceSize); samplesLeft -= currentSliceSize; } // revert buffer pointers (otherwise some hosts may use these wrong pointers) advanceBuffers (data.inputs, data.numInputs, -(numSamples - samplesLeft)); advanceBuffers (data.outputs, data.numOutputs, -(numSamples - samplesLeft)); data.numSamples = numSamples; } /** Stop the slice process * * If you want to break the slice processing early, you have to capture the slicer in the * DoProcessCallback and call the stop method. */ void stop () noexcept { stopIt = true; } private: template void advanceBuffers (AudioBusBuffers* buffers, int32 numBuffers, int32 numSamples) const noexcept { for (auto index = 0; index < numBuffers; ++index) { for (auto channelIndex = 0; channelIndex < buffers[index].numChannels; ++channelIndex) { if (SampleSize == SymbolicSampleSizes::kSample32) buffers[index].channelBuffers32[channelIndex] += numSamples; else buffers[index].channelBuffers64[channelIndex] += numSamples; } } } int32 sliceSize; bool stopIt {false}; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/mpeprocessor.cpp0000644000000000000000000000012715124701711025307 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/mpeprocessor.cpp0000644000175000001440000002577215124701711025310 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/mpeprocessor.cpp // Created by : Steinberg, 07/2017 // Description : VST 3 MIDI-MPE decomposer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "mpeprocessor.h" #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace MPE { //------------------------------------------------------------------------ struct Note { NoteID noteID; Pitch pitch; }; //------------------------------------------------------------------------ struct ChannelData { using NoteList = std::vector; NoteList notes; NormalizedValue pressure {0.}; NormalizedValue x {0.5}; NormalizedValue y {0.}; }; //------------------------------------------------------------------------ struct Processor::Impl { static constexpr auto NumMIDIChannels = 16u; using ChannelDataList = std::array; Handler* delegate {nullptr}; Setup setup; ChannelDataList channelData {}; size_t dataBufferUsed {0}; size_t maxNotesPerChannel {16}; bool inSysex {false}; Impl (Handler* delegate, size_t maxNotesPerChannel) : delegate (delegate), maxNotesPerChannel (maxNotesPerChannel) { for (auto& cd : channelData) cd.notes.reserve (maxNotesPerChannel); } bool inMPEZone (uint8_t channel) const { return channel >= setup.memberChannelBegin && channel <= setup.memberChannelEnd; } Controller getController (InputMIDIMessage input) const { if (setup.pressure == input) return Controller::Pressure; if (setup.x == input) return Controller::X; if (setup.y == input) return Controller::Y; return Controller::None; } NormalizedValue getControllerValue (Controller controller, const ChannelData& data) const { switch (controller) { case Controller::Pressure: return data.pressure; case Controller::X: return data.x; case Controller::Y: return data.y; case Controller::None: assert (false); break; } return 0.; } void setControllerValue (Controller controller, ChannelData& data, NormalizedValue value) { switch (controller) { case Controller::Pressure: data.pressure = value; break; case Controller::X: data.x = value; break; case Controller::Y: data.y = value; break; case Controller::None: assert (false); break; } } }; //------------------------------------------------------------------------ Processor::Processor (Handler* delegate, size_t maxNotesPerChannel) { impl = std::make_unique (delegate, maxNotesPerChannel); } //------------------------------------------------------------------------ Processor::~Processor () noexcept = default; //------------------------------------------------------------------------ const Setup& Processor::getSetup () const { return impl->setup; } //------------------------------------------------------------------------ void Processor::changeSetup (const Setup& setup) { impl->setup = setup; } //------------------------------------------------------------------------ void Processor::reset () { for (auto& cd : impl->channelData) { for (auto& note : cd.notes) { impl->delegate->onMPENoteOff (note.noteID, note.pitch, 0.f); impl->delegate->releaseNoteID (note.noteID); } cd.notes.clear (); } } //------------------------------------------------------------------------ int32_t Processor::onNoteOn (const uint8_t* data, size_t dataSize) { assert (dataSize >= 2); if (data[2] == 0) return onNoteOff (data, dataSize); auto channel = data[0] & 0x0F; if (impl->inMPEZone (channel)) { auto& channelData = impl->channelData[channel]; auto pitch = data[1]; if (channelData.notes.size () >= impl->maxNotesPerChannel) { // error note stack full impl->delegate->errorNoteDroppedBecauseNoteStackFull (channel, pitch); } else { Note note; if (impl->delegate->generateNewNoteID (note.noteID)) { note.pitch = pitch; channelData.notes.push_back (note); auto velocity = static_cast (data[2]) / 127.f; impl->delegate->onMPENoteOn (note.noteID, note.pitch, velocity); impl->delegate->onMPEControllerChange (note.noteID, Controller::Pressure, channelData.pressure); impl->delegate->onMPEControllerChange (note.noteID, Controller::X, channelData.x); impl->delegate->onMPEControllerChange (note.noteID, Controller::Y, channelData.y); } else { // error: note will be dropped impl->delegate->errorNoteDroppedBecauseNoNoteID (pitch); } } } else { impl->delegate->onOtherInput (data, 3); } return 3; } //------------------------------------------------------------------------ int32_t Processor::onNoteOff (const uint8_t* data, size_t dataSize) { assert (dataSize >= 2); auto channel = data[0] & 0x0F; if (impl->inMPEZone (channel)) { bool noteFound = false; auto& notes = impl->channelData[channel].notes; for (auto it = notes.begin (); it != notes.end (); ++it) { if (it->pitch == data[1]) { auto velocity = static_cast (data[2]) / 127.f; impl->delegate->onMPENoteOff (it->noteID, it->pitch, velocity); impl->delegate->releaseNoteID (it->noteID); notes.erase (it); noteFound = true; break; } } if (!noteFound) { // error: no note for note off found impl->delegate->errorNoteForNoteOffNotFound (channel, data[1]); } } else { impl->delegate->onOtherInput (data, 3); } return 3; } //------------------------------------------------------------------------ int32_t Processor::onAftertouch (const uint8_t* data, size_t dataSize) { assert (dataSize >= 2); auto controller = impl->getController (ChannelPressure); if (controller != Controller::None) { auto channel = data[0] & 0x0F; if (impl->inMPEZone (channel)) { auto& channelData = impl->channelData[channel]; auto value = static_cast (data[2]) / 127.; impl->setControllerValue (controller, channelData, value); for (auto& note : channelData.notes) { impl->delegate->onMPEControllerChange (note.noteID, controller, value); } return 3; } } impl->delegate->onOtherInput (data, 3); return 3; } //------------------------------------------------------------------------ int32_t Processor::onController (const uint8_t* data, size_t dataSize) { assert (dataSize >= 2); auto cc = data[1]; auto controller = impl->getController (static_cast (cc)); if (controller != Controller::None) { auto channel = data[0] & 0x0F; if (impl->inMPEZone (channel)) { auto& channelData = impl->channelData[channel]; auto value = static_cast (data[2]) / 127.; impl->setControllerValue (controller, channelData, value); for (auto& note : channelData.notes) { impl->delegate->onMPEControllerChange (note.noteID, controller, value); } return 3; } } impl->delegate->onOtherInput (data, 3); return 3; } //------------------------------------------------------------------------ int32_t Processor::onProgramChange (const uint8_t* data, size_t dataSize) { assert (dataSize >= 1); auto channel = data[0] & 0x0F; if (impl->inMPEZone (channel)) { // error: program change send in MPE zone impl->delegate->errorProgramChangeReceivedInMPEZone (); } else { impl->delegate->onOtherInput (data, 2); } return 2; } //------------------------------------------------------------------------ int32_t Processor::onChannelPressure (const uint8_t* data, size_t dataSize) { assert (dataSize >= 1); auto controller = impl->getController (ChannelPressure); if (controller != Controller::None) { auto channel = data[0] & 0x0F; if (impl->inMPEZone (channel)) { auto& channelData = impl->channelData[channel]; auto value = static_cast (data[1]) / 127.; impl->setControllerValue (controller, channelData, value); for (auto& note : channelData.notes) { impl->delegate->onMPEControllerChange (note.noteID, controller, value); } return 2; } } impl->delegate->onOtherInput (data, 2); return 2; } //------------------------------------------------------------------------ int32_t Processor::onPitchWheel (const uint8_t* data, size_t dataSize) { assert (dataSize >= 2); auto controller = impl->getController (PitchBend); if (controller != Controller::None) { auto channel = data[0] & 0x0F; if (impl->inMPEZone (channel)) { auto& channelData = impl->channelData[channel]; auto value = static_cast ((data[1] & 0x7F) + ((data[2] & 0x7F) << 7)) / 16383.; impl->setControllerValue (controller, channelData, value); for (auto& note : channelData.notes) { impl->delegate->onMPEControllerChange (note.noteID, controller, value); } return 3; } } impl->delegate->onOtherInput (data, 3); return 3; } //------------------------------------------------------------------------ void Processor::processMIDIInput (const uint8_t* data, size_t dataSize) { assert (dataSize > 0); if (impl->inSysex || data[0] == 0xf0) { for (size_t index = 0; index < dataSize; ++index) { if (data[index] == 0xf7) { ++index; impl->delegate->onSysexInput (data, index); impl->inSysex = false; auto dataLeft = dataSize - index; if (dataLeft != 0) { processMIDIInput (data + index, dataSize - index); } return; } } impl->inSysex = true; impl->delegate->onSysexInput (data, dataSize); return; } auto status = static_cast (data[0] & 0xF0); int32_t packetSize = 0; switch (status) { case 0x90: // Note On { packetSize = onNoteOn (data, dataSize); break; } case 0x80: // Note Off { packetSize = onNoteOff (data, dataSize); break; } case 0xa0: // Aftertouch { packetSize = onAftertouch (data, dataSize); break; } case 0xb0: // Controller { packetSize = onController (data, dataSize); break; } case 0xc0: // Program Change { packetSize = onProgramChange (data, dataSize); break; } case 0xd0: // Channel Pressure { packetSize = onChannelPressure (data, dataSize); break; } case 0xe0: // Pitch Wheel { packetSize = onPitchWheel (data, dataSize); break; } case 0xf0: // System Realtime Messages { impl->delegate->onOtherInput (data, 1); packetSize = 1; break; } default: { // Ehm... assert (false); return; } } if (dataSize > static_cast (packetSize)) processMIDIInput (data + packetSize, dataSize - packetSize); } //------------------------------------------------------------------------ } // MPE } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/dataexchange.cpp0000644000000000000000000000012715124701711025202 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/dataexchange.cpp0000644000175000001440000003241015124701711025166 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // Category : Helpers // Filename : public.sdk/source/vst/utility/dataexchange.cpp // Created by : Steinberg, 06/2023 // Description : VST Data Exchange API Helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "dataexchange.h" #include "public.sdk/source/vst/utility/alignedalloc.h" #include "public.sdk/source/vst/utility/ringbuffer.h" #include "base/source/timer.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/vst/ivsthostapplication.h" #include #ifdef _MSC_VER #include #endif //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { static constexpr DataExchangeBlock InvalidDataExchangeBlock = {nullptr, 0, InvalidDataExchangeBlockID}; //------------------------------------------------------------------------ bool operator== (const DataExchangeHandler::Config& c1, const DataExchangeHandler::Config& c2) { return c1.userContextID == c2.userContextID && c1.alignment == c2.alignment && c1.numBlocks == c2.numBlocks && c1.blockSize == c2.blockSize; } static constexpr auto MessageIDDataExchange = "DataExchange"; static constexpr auto MessageIDQueueOpened = "DataExchangeQueueOpened"; static constexpr auto MessageIDQueueClosed = "DataExchangeQueueClosed"; static constexpr auto MessageKeyData = "Data"; static constexpr auto MessageKeyBlockSize = "BlockSize"; static constexpr auto MessageKeyUserContextID = "UserContextID"; //------------------------------------------------------------------------ struct MessageHandler : ITimerCallback { using RingBuffer = OneReaderOneWriter::RingBuffer; IPtr timer; IPtr hostApp; IConnectionPoint* connection {nullptr}; RingBuffer realtimeBuffer; RingBuffer messageBuffer; RingBuffer rtOnlyBuffer; void* lockedRealtimeBlock {nullptr}; DataExchangeHandler::Config config {}; MessageHandler (FUnknown* hostContext, IConnectionPoint* connection) : connection (connection) { hostApp = U::cast (hostContext); } ~MessageHandler () noexcept override { if (timer) timer->stop (); timer = nullptr; } bool openQueue (const DataExchangeHandler::Config& c) { if (!hostApp || !connection) return false; config = c; timer = owned (Timer::create (this, 1)); if (!timer) return false; realtimeBuffer.resize (config.numBlocks); messageBuffer.resize (config.numBlocks); rtOnlyBuffer.resize (config.numBlocks); for (auto i = 0u; i < config.numBlocks; ++i) { auto data = aligned_alloc (config.blockSize, config.alignment); realtimeBuffer.push (data); } if (auto msg = owned (allocateMessage (hostApp))) { msg->setMessageID (MessageIDQueueOpened); if (auto attr = msg->getAttributes ()) { attr->setInt (MessageKeyUserContextID, config.userContextID); attr->setInt (MessageKeyBlockSize, config.blockSize); } connection->notify (msg); } return true; } bool closeQueue () { if (timer) timer->stop (); timer = nullptr; void* data; while (realtimeBuffer.pop (data)) { aligned_free (data, config.alignment); } while (messageBuffer.pop (data)) { aligned_free (data, config.alignment); } while (rtOnlyBuffer.pop (data)) { aligned_free (data, config.alignment); } if (auto msg = owned (allocateMessage (hostApp))) { msg->setMessageID (MessageIDQueueClosed); if (auto attr = msg->getAttributes ()) { attr->setInt (MessageKeyUserContextID, config.userContextID); } connection->notify (msg); } return true; } void* lockBlock () { if (lockedRealtimeBlock != nullptr) return nullptr; void* data; if (rtOnlyBuffer.pop (data)) { lockedRealtimeBlock = data; return lockedRealtimeBlock; } if (realtimeBuffer.pop (data)) { lockedRealtimeBlock = data; return lockedRealtimeBlock; } return nullptr; } bool freeBlock (bool send) { if (send) { if (messageBuffer.push (lockedRealtimeBlock)) { lockedRealtimeBlock = nullptr; return true; } return false; } if (rtOnlyBuffer.push (lockedRealtimeBlock)) { lockedRealtimeBlock = nullptr; return true; } return false; } void onTimer (Timer* /*_timer*/) override { void* data; while (messageBuffer.pop (data)) { if (auto msg = owned (allocateMessage (hostApp))) { msg->setMessageID (MessageIDDataExchange); if (auto attributes = msg->getAttributes ()) { attributes->setInt (MessageKeyUserContextID, config.userContextID); attributes->setBinary (MessageKeyData, data, config.blockSize); connection->notify (msg); } } realtimeBuffer.push (data); } } }; //------------------------------------------------------------------------ struct DataExchangeHandler::Impl { Config config {}; ConfigCallback configCallback; IDataExchangeHandler* exchangeHandler {nullptr}; IConnectionPoint* connectionPoint {nullptr}; FUnknown* hostContext {nullptr}; IAudioProcessor* processor {nullptr}; std::unique_ptr fallbackMessageHandler; DataExchangeQueueID queueID {InvalidDataExchangeQueueID}; DataExchangeBlock currentBlock {InvalidDataExchangeBlock}; bool enabled {true}; bool internalUseExchangeManager {true}; bool isOpen () const { return queueID != InvalidDataExchangeQueueID; } bool openQueue (bool forceUseMessageHandling) { if (exchangeHandler && !forceUseMessageHandling) { internalUseExchangeManager = true; return exchangeHandler->openQueue (processor, config.blockSize, config.numBlocks, config.alignment, config.userContextID, &queueID) == kResultTrue; } internalUseExchangeManager = false; fallbackMessageHandler = std::make_unique (hostContext, connectionPoint); if (fallbackMessageHandler->openQueue (config)) { queueID = 0; return true; } fallbackMessageHandler.reset (); return false; } void closeQueue () { if (queueID == InvalidDataExchangeQueueID) return; if (internalUseExchangeManager) exchangeHandler->closeQueue (queueID); else if (fallbackMessageHandler) { fallbackMessageHandler->closeQueue (); fallbackMessageHandler.reset (); } currentBlock = InvalidDataExchangeBlock; queueID = InvalidDataExchangeQueueID; } DataExchangeBlock lockBlock () { if (!isOpen ()) return InvalidDataExchangeBlock; else if (currentBlock.blockID != InvalidDataExchangeBlockID) return currentBlock; else if (internalUseExchangeManager) { auto res = exchangeHandler->lockBlock (queueID, ¤tBlock); if (res != kResultTrue) currentBlock = InvalidDataExchangeBlock; return currentBlock; } else if (fallbackMessageHandler) { if (auto data = fallbackMessageHandler->lockBlock ()) { currentBlock.data = data; currentBlock.size = config.blockSize; currentBlock.blockID = 0; return currentBlock; } } return InvalidDataExchangeBlock; } bool freeBlock (bool send) { if (!isOpen () || currentBlock.blockID == InvalidDataExchangeBlockID) return true; if (internalUseExchangeManager) { auto res = exchangeHandler->freeBlock (queueID, currentBlock.blockID, send); currentBlock = InvalidDataExchangeBlock; return res == kResultTrue; } else if (fallbackMessageHandler) { if (fallbackMessageHandler->freeBlock (send)) { currentBlock = InvalidDataExchangeBlock; return true; } } return false; } }; //------------------------------------------------------------------------ DataExchangeHandler::DataExchangeHandler (IAudioProcessor* processor) { impl = std::make_unique (); impl->processor = processor; } //------------------------------------------------------------------------ DataExchangeHandler::DataExchangeHandler (IAudioProcessor* processor, ConfigCallback&& callback) : DataExchangeHandler (processor) { impl->configCallback = std::move (callback); } //------------------------------------------------------------------------ DataExchangeHandler::DataExchangeHandler (IAudioProcessor* processor, const ConfigCallback& callback) : DataExchangeHandler (processor) { impl->configCallback = callback; } //------------------------------------------------------------------------ DataExchangeHandler::~DataExchangeHandler () noexcept { } //------------------------------------------------------------------------ void DataExchangeHandler::onConnect (IConnectionPoint* other, FUnknown* hostContext) { impl->connectionPoint = other; impl->hostContext = hostContext; impl->exchangeHandler = U::cast (hostContext); } //------------------------------------------------------------------------ void DataExchangeHandler::onDisconnect (IConnectionPoint* /*other*/) { impl->closeQueue (); impl->connectionPoint = nullptr; impl->hostContext = nullptr; impl->exchangeHandler = nullptr; } //------------------------------------------------------------------------ void DataExchangeHandler::onActivate (const Vst::ProcessSetup& setup, bool forceUseMessageHandling) { Config conf {}; if (impl->configCallback (conf, setup)) { if (impl->isOpen ()) { if (impl->config == conf) return; impl->closeQueue (); } impl->config = conf; impl->openQueue (forceUseMessageHandling); } } //------------------------------------------------------------------------ void DataExchangeHandler::onDeactivate () { if (impl->isOpen ()) impl->closeQueue (); } //------------------------------------------------------------------------ void DataExchangeHandler::enable (bool state) { impl->enabled = state; } //------------------------------------------------------------------------ bool DataExchangeHandler::isEnabled () const { return impl->enabled; } //------------------------------------------------------------------------ DataExchangeBlock DataExchangeHandler::getCurrentOrNewBlock () { if (!isEnabled ()) return InvalidDataExchangeBlock; return impl->lockBlock (); } //------------------------------------------------------------------------ bool DataExchangeHandler::sendCurrentBlock () { return impl->freeBlock (true); } //------------------------------------------------------------------------ bool DataExchangeHandler::discardCurrentBlock () { return impl->freeBlock (false); } //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ struct DataExchangeReceiverHandler::Impl { IDataExchangeReceiver* receiver {nullptr}; }; //------------------------------------------------------------------------ DataExchangeReceiverHandler::DataExchangeReceiverHandler (IDataExchangeReceiver* receiver) { impl = std::make_unique (); impl->receiver = receiver; } //------------------------------------------------------------------------ DataExchangeReceiverHandler::~DataExchangeReceiverHandler () noexcept = default; //------------------------------------------------------------------------ bool DataExchangeReceiverHandler::onMessage (IMessage* msg) { std::string_view msgID = msg->getMessageID (); if (msgID == MessageIDDataExchange) { if (auto attributes = msg->getAttributes ()) { const void* data; uint32 sizeInBytes; if (attributes->getBinary (MessageKeyData, data, sizeInBytes) != kResultTrue) return false; int64 userContext; if (attributes->getInt (MessageKeyUserContextID, userContext) != kResultTrue) return false; DataExchangeBlock block; block.size = sizeInBytes; block.data = const_cast (data); block.blockID = 0; impl->receiver->onDataExchangeBlocksReceived (static_cast (userContext), 1, &block, false); return true; } } else if (msgID == MessageIDQueueOpened) { if (auto attributes = msg->getAttributes ()) { int64 userContext; if (attributes->getInt (MessageKeyUserContextID, userContext) != kResultTrue) return false; int64 blockSize; if (attributes->getInt (MessageKeyBlockSize, blockSize) != kResultTrue) return false; TBool backgroundThread = false; impl->receiver->queueOpened (static_cast (userContext), static_cast (blockSize), backgroundThread); return true; } } else if (msgID == MessageIDQueueClosed) { if (auto attributes = msg->getAttributes ()) { int64 userContext; if (attributes->getInt (MessageKeyUserContextID, userContext) != kResultTrue) return false; impl->receiver->queueClosed (static_cast (userContext)); return true; } } return false; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/rttransfer.h0000644000000000000000000000012715124701711024425 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/rttransfer.h0000644000175000001440000000774715124701711024430 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/rttransfer.h // Created by : Steinberg, 04/2021 // Description : Realtime Object Transfer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Transfer objects from a non realtime thread to a realtime one * * You have to use it from two threads, the realtime context thread where you are not allowed to * block and a non realtime thread from where the object is coming. * * It's guaranteed that the function you should only call in the realtime context is wait free and * does not do any allocations or deallocations * */ template > struct RTTransferT { using ObjectType = ObjectT; using ObjectTypePtr = std::unique_ptr; RTTransferT () { assert (storage[0].is_lock_free ()); } ~RTTransferT () noexcept { clear_ui (); } /** Access the transfer object. * * If there's a new object, the proc is called with the new object. The object is only valid * inside the proc. * * To be called from the realtime context. */ template void accessTransferObject_rt (Proc proc) noexcept { ObjectType* newObject {nullptr}; ObjectType* currentObject = storage[0].load (); if (currentObject && storage[0].compare_exchange_strong (currentObject, newObject)) { proc (*currentObject); ObjectType* transitObj = storage[1].load (); if (storage[1].compare_exchange_strong (transitObj, currentObject) == false) { assert (false); } ObjectType* oldObject = storage[2].load (); if (storage[2].compare_exchange_strong (oldObject, transitObj) == false) { assert (false); } } } /** Transfer an object to the realtime context. * * The ownership of newObject is transfered to this object and the Deleter is used to free * the memory of it afterwards. * * If there's already an object in transfer the previous object will be deallocated and * replaced with the new one without passing to the realtime context. * * To be called from the non realtime context. */ void transferObject_ui (ObjectTypePtr&& newObjectPtr) { ObjectType* newObject = newObjectPtr.release (); clear_ui (); while (true) { ObjectType* currentObject = storage[0].load (); if (storage[0].compare_exchange_strong (currentObject, newObject)) { deallocate (currentObject); break; } } } /** Clear the transfer. * * To be called from the non realtime context. */ void clear_ui () { clearStorage (storage[0]); clearStorage (storage[1]); clearStorage (storage[2]); } private: using AtomicObjectPtr = std::atomic; void clearStorage (AtomicObjectPtr& atomObj) { ObjectType* newObject = nullptr; while (ObjectType* current = atomObj.load ()) { if (atomObj.compare_exchange_strong (current, newObject)) { deallocate (current); break; } } } void deallocate (ObjectType* object) { if (object) { Deleter d; d (object); } } std::array storage {}; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/test0000644000000000000000000000012715124701711022764 xustar0029 mtime=1767080905.28618869 29 atime=1767080905.28554689 29 ctime=1767080905.28618869 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/test/0000755000175000001440000000000015124701711023025 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/test/PaxHeaders/rttransfertest.cpp0000644000000000000000000000012715124701711026637 xustar0029 mtime=1767080905.28618869 29 atime=1767080905.28554689 29 ctime=1767080905.28618869 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/test/rttransfertest.cpp0000644000175000001440000001105615124701711026626 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/test/rtstatetransfertest.cpp // Created by : Steinberg, 04/2021 // Description : Realtime State Transfer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/utility/rttransfer.h" #include "public.sdk/source/vst/utility/testing.h" #include "pluginterfaces/vst/vsttypes.h" #include #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace { //------------------------------------------------------------------------ using ParameterVector = std::vector>; using RTTransfer = RTTransferT; //------------------------------------------------------------------------ struct RaceConditionTestObject { static std::atomic numDeletes; //------------------------------------------------------------------------ struct MyDeleter { void operator () (double* v) const noexcept { delete v; ++numDeletes; } }; RTTransferT transfer; std::thread thread; std::mutex m1; std::mutex m2; std::condition_variable c1; bool test (ITestResult* result) { numDeletes = 0; { auto obj1 = std::unique_ptr (new double (0.5)); auto obj2 = std::unique_ptr (new double (1.)); transfer.transferObject_ui (std::move (obj1)); m2.lock (); thread = std::thread ([&] () { transfer.accessTransferObject_rt ([&] (const double&) { c1.notify_all (); m2.lock (); m2.unlock (); }); transfer.accessTransferObject_rt ([&] (const double&) {}); }); std::unique_lock lm1 (m1); c1.wait (lm1); transfer.transferObject_ui (std::move (obj2)); m2.unlock (); thread.join (); transfer.clear_ui (); } return numDeletes == 2; } }; std::atomic RaceConditionTestObject::numDeletes {0}; //------------------------------------------------------------------------ static std::atomic CustomDeleterCallCount; struct CustomDeleter { template void operator () (T* v) const noexcept { delete v; ++CustomDeleterCallCount; } }; //------------------------------------------------------------------------ ModuleInitializer InitStateTransferTests ([] () { registerTest ("RTTransfer", STR ("Simple Transfer"), [] (ITestResult*) { RTTransfer helper; auto list = std::make_unique (); list->emplace_back (std::make_pair (0, 1.)); helper.transferObject_ui (std::move (list)); bool success = false; constexpr double one = 1.; helper.accessTransferObject_rt ([&success, one = one] (const auto& list) { if (list.size () == 1) { if (list[0].first == 0) { if (Test::equal (one, list[0].second)) { success = true; } } } }); list = std::make_unique (); list->emplace_back (std::make_pair (0, 1.)); helper.transferObject_ui (std::move (list)); helper.accessTransferObject_rt ([] (auto&) {}); list = std::make_unique (); list->emplace_back (std::make_pair (0, 1.)); helper.transferObject_ui (std::move (list)); helper.accessTransferObject_rt ([] (auto&) {}); return success; }); registerTest ("RTTransfer", STR ("CheckRaceCondition"), [] (ITestResult* r) { RaceConditionTestObject obj; return obj.test (r); }); registerTest ("RTTransfer", STR ("Custom Deleter"), [] (ITestResult* result) { CustomDeleterCallCount = 0; RTTransferT transfer; auto obj1 = std::unique_ptr (new double (1.)); transfer.transferObject_ui (std::move (obj1)); if (CustomDeleterCallCount != 0) { return false; } transfer.clear_ui (); return CustomDeleterCallCount == 1; }); }); //------------------------------------------------------------------------ } // Anonymous } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/test/PaxHeaders/versionparsertest.cpp0000644000000000000000000000012715124701711027347 xustar0029 mtime=1767080905.28618869 29 atime=1767080905.28618869 29 ctime=1767080905.28618869 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/test/versionparsertest.cpp0000644000175000001440000000707715124701711027346 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/test/versionparsertest.cpp // Created by : Steinberg, 12/2019 // Description : Test version parser // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/utility/testing.h" #include "public.sdk/source/vst/utility/versionparser.h" #include "pluginterfaces/base/fstrdefs.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ static ModuleInitializer InitVersionParserTests ([] () { registerTest ("VersionParser", STR ("Parsing 'SDK 3.7'"), [] (ITestResult* testResult) { auto version = VST3::Version::parse ("SDK 3.7"); if (version.getMajor () != 3 || version.getMinor () != 7 || version.getSub () != 0 || version.getBuildnumber () != 0) { testResult->addErrorMessage (STR ("Parsing 'SDK 3.7' failed")); return false; } return true; }); registerTest ("VersionParser", STR ("Parsing 'SDK 3.7.1.38'"), [] (ITestResult* testResult) { auto version = VST3::Version::parse ("3.7.1.38"); if (version.getMajor () != 3 || version.getMinor () != 7 || version.getSub () != 1 || version.getBuildnumber () != 38) { testResult->addErrorMessage (STR ("Parsing '3.7.1.38' failed")); return false; } return true; }); registerTest ("VersionParser", STR ("Parsing 'SDK 3.7 Prerelease'"), [] (ITestResult* testResult) { auto version = VST3::Version::parse ("SDK 3.7 Prerelease"); if (version.getMajor () != 3 || version.getMinor () != 7 || version.getSub () != 0 || version.getBuildnumber () != 0) { testResult->addErrorMessage (STR ("Parsing 'SDK 3.7 Prerelease' failed")); return false; } return true; }); registerTest ("VersionParser", STR ("Parsing 'SDK 3.7-99'"), [] (ITestResult* testResult) { auto version = VST3::Version::parse ("SDK 3.7-99"); if (version.getMajor () != 3 || version.getMinor () != 7 || version.getSub () != 0 || version.getBuildnumber () != 0) { testResult->addErrorMessage (STR ("Parsing 'SDK 3.7-99' failed")); return false; } return true; }); registerTest ("VersionParser", STR ("Parsing 'No version at all'"), [] (ITestResult* testResult) { auto version = VST3::Version::parse ("No version at all"); if (version.getMajor () != 0 || version.getMinor () != 0 || version.getSub () != 0 || version.getBuildnumber () != 0) { testResult->addErrorMessage (STR ("Parsing 'No version at all' failed")); return false; } return true; }); }); //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/test/PaxHeaders/sampleaccuratetest.cpp0000644000000000000000000000012715124701711027436 xustar0029 mtime=1767080905.28618869 29 atime=1767080905.28618869 29 ctime=1767080905.28618869 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/test/sampleaccuratetest.cpp0000644000175000001440000001210615124701711027422 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Examples // Filename : public.sdk/source/vst/utility/test/sampleaccuratetest.cpp // Created by : Steinberg, 04/2021 // Description : Tests for Sample Accurate Parameter Changes // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/hosting/parameterchanges.h" #include "public.sdk/source/vst/utility/sampleaccurate.h" #include "public.sdk/source/vst/utility/testing.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ static ModuleInitializer InitTests ([] () { registerTest ("SampleAccurate::Parameter", STR ("Single Change"), [] (ITestResult* result) { ParamID pid = 1; SampleAccurate::Parameter param (pid, 0.); ParameterValueQueue queue (pid); int32 index = 0; queue.addPoint (0, 0., index); queue.addPoint (100, 1., index); param.beginChanges (&queue); param.advance (50); if (Test::notEqual (param.getValue (), 0.5)) { result->addErrorMessage (STR ("Unexpected Value")); return false; } param.advance (50); if (Test::notEqual (param.getValue (), 1.)) { result->addErrorMessage (STR ("Unexpected Value")); return false; } param.endChanges (); return true; }); registerTest ("SampleAccurate::Parameter", STR ("Multi Change"), [] (ITestResult* result) { ParamID pid = 1; SampleAccurate::Parameter param (pid, 0.); ParameterValueQueue queue (pid); int32 index = 0; queue.addPoint (0, 0., index); queue.addPoint (100, 1., index); queue.addPoint (120, 0., index); param.beginChanges (&queue); param.advance (50); if (Test::notEqual (param.getValue (), 0.5)) { result->addErrorMessage (STR ("Unexpected Value")); return false; } param.advance (50); if (Test::notEqual (param.getValue (), 1.)) { result->addErrorMessage (STR ("Unexpected Value")); return false; } param.advance (20); if (Test::notEqual (param.getValue (), 0.)) { result->addErrorMessage (STR ("Unexpected Value")); return false; } param.endChanges (); return true; }); registerTest ("SampleAccurate::Parameter", STR ("Edge"), [] (ITestResult* result) { ParamID pid = 1; SampleAccurate::Parameter param (pid, 0.); ParameterValueQueue queue (pid); int32 index = 0; queue.addPoint (0, 0., index); queue.addPoint (1, 1., index); queue.addPoint (2, 0., index); param.beginChanges (&queue); param.advance (2); if (Test::notEqual (param.getValue (), 0.)) { result->addErrorMessage (STR ("Unexpected Value")); return false; } param.endChanges (); return true; }); registerTest ("SampleAccurate::Parameter", STR ("Flush"), [] (ITestResult* result) { ParamID pid = 1; SampleAccurate::Parameter param (pid, 0.); ParameterValueQueue queue (pid); int32 index = 0; queue.addPoint (0, 0., index); queue.addPoint (256, 1., index); queue.addPoint (258, 0.5, index); param.beginChanges (&queue); param.flushChanges (); if (Test::notEqual (param.getValue (), 0.5)) { result->addErrorMessage (STR ("Unexpected Value")); return false; } param.endChanges (); return true; }); registerTest ("SampleAccurate::Parameter", STR ("Callback"), [] (ITestResult* result) { ParamID pid = 1; SampleAccurate::Parameter param (pid, 0.); ParameterValueQueue queue (pid); int32 index = 0; queue.addPoint (0, 0., index); queue.addPoint (128, 0., index); queue.addPoint (256, 1., index); queue.addPoint (258, 0.5, index); param.beginChanges (&queue); bool failure = false; param.advance (128, [&result, &failure] (auto) { result->addErrorMessage (STR ("Unexpected Value")); failure = true; }); if (failure) return false; constexpr auto half = 0.5; param.advance (514, [&result, &failure, half = half] (auto value) { if (Test::notEqual (value, half)) { result->addErrorMessage (STR ("Unexpected Value")); failure = true; } else failure = false; }); if (failure) return false; param.endChanges (); return true; }); registerTest ("SampleAccurate::Parameter", STR ("NoChanges"), [] (ITestResult* result) { ParamID pid = 1; SampleAccurate::Parameter param (pid, 1.); param.endChanges (); if (Test::notEqual (param.getValue (), 1.)) { result->addErrorMessage (STR ("Unexpected Value")); return false; } return true; }); }); //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/test/PaxHeaders/ringbuffertest.cpp0000644000000000000000000000012715124701711026576 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/test/ringbuffertest.cpp0000644000175000001440000000557715124701711026600 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/test/ringbuffertest.cpp // Created by : Steinberg, 03/2018 // Description : Test ringbuffer // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/utility/ringbuffer.h" #include "public.sdk/source/vst/utility/testing.h" #include "pluginterfaces/base/fstrdefs.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ static ModuleInitializer InitRingbufferTests ([] () { registerTest ("RingBuffer", STR ("push until full"), [] (ITestResult*) { OneReaderOneWriter::RingBuffer rb (4); if (!rb.push (0)) return false; if (!rb.push (1)) return false; if (!rb.push (2)) return false; if (!rb.push (3)) return false; if (!rb.push (4)) return true; return false; }); registerTest ("RingBuffer", STR ("pop until empty"), [] (ITestResult*) { OneReaderOneWriter::RingBuffer rb (4); if (!rb.push (0)) return false; if (!rb.push (1)) return false; if (!rb.push (2)) return false; if (!rb.push (3)) return false; uint32 value; if (!rb.pop (value) || value != 0) return false; if (!rb.pop (value) || value != 1) return false; if (!rb.pop (value) || value != 2) return false; if (!rb.pop (value) || value != 3) return false; if (!rb.pop (value)) return true; return false; }); registerTest ("RingBuffer", STR ("roundtrip"), [] (ITestResult*) { OneReaderOneWriter::RingBuffer rb (2); uint32 value; for (auto i = 0u; i < rb.size () * 2; ++i) { if (!rb.push (i)) return false; if (!rb.pop (value) || value != i) return false; } return true; }); registerTest ("RingBuffer", STR ("push multiple"), [] (ITestResult*) { OneReaderOneWriter::RingBuffer rb (3); if (!rb.push ({32u, 64u})) return false; if (rb.push ({32u, 64u})) return false; uint32 value; if (!rb.pop (value)) return false; if (!rb.pop (value)) return false; if (rb.pop (value)) return false; return true; }); }); //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/systemtime.h0000644000000000000000000000012715124701711024436 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/systemtime.h0000644000175000001440000000470615124701711024431 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // Category : Helpers // Filename : public.sdk/source/vst/utility/systemtime.h // Created by : Steinberg, 06/2023 // Description : VST Component System Time API Helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivsteditcontroller.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** SystemTime Helper class * * Get the system time on the vst controller side. * * If supported by the host this uses the same clock as used in the * realtime audio process block. Otherwise an approximation via platform APIs is used. * * This can be used to synchronize audio and visuals. As known, the audio process block is always * called ealier as the audio which was generated passes the audio monitors or headphones. * Depending on the audio graph this can be so long that your eyes will see the visualization (if * not synchronized) earlier then your ears will hear the sound. * To synchronize you need to queue your visualization data on the controller side timestamped with * the time from the process block and dequed when it's time for the data to be visualized. */ class SystemTime { public: SystemTime (IComponentHandler* componentHandler); SystemTime (const SystemTime& st); SystemTime (SystemTime&& st) noexcept; SystemTime& operator= (const SystemTime& st); SystemTime& operator= (SystemTime&& st) noexcept; /** get the current system time */ int64 get () const { return getImpl (); } //------------------------------------------------------------------------ using GetImplFunc = std::function; private: GetImplFunc getImpl; }; //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/testing.cpp0000644000000000000000000000012715124701711024243 xustar0029 mtime=1767080905.28618869 29 atime=1767080905.28618869 29 ctime=1767080905.28618869 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/testing.cpp0000644000175000001440000001504015124701711024227 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Examples // Filename : public.sdk/source/vst/utility/testing.cpp // Created by : Steinberg, 04/2021 // Description : Utility classes for custom testing in the vst validator // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/utility/testing.h" #include #include #include #include //------------------------------------------------------------------------ namespace Steinberg { DEF_CLASS_IID (ITest) DEF_CLASS_IID (ITestSuite) DEF_CLASS_IID (ITestFactory) //------------------------------------------------------------------------ namespace Vst { namespace { //------------------------------------------------------------------------ struct TestRegistry { struct TestWithContext { std::u16string desc; TestFuncWithContext func; }; using Tests = std::vector>>; using TestsWithContext = std::vector>; static TestRegistry& instance () { static TestRegistry gInstance; return gInstance; } Tests tests; TestsWithContext testsWithContext; }; //------------------------------------------------------------------------ struct TestBase : ITest { TestBase (const tchar* inDesc) { if (inDesc) desc = reinterpret_cast (inDesc); } TestBase (const std::u16string& inDesc) : desc (inDesc) {} virtual ~TestBase () = default; bool PLUGIN_API setup () override { return true; } bool PLUGIN_API teardown () override { return true; } const tchar* PLUGIN_API getDescription () override { return reinterpret_cast (desc.data ()); } tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) override { QUERY_INTERFACE (_iid, obj, FUnknown::iid, FUnknown) QUERY_INTERFACE (_iid, obj, ITest::iid, ITest) *obj = nullptr; return kNoInterface; } uint32 PLUGIN_API addRef () override { return ++refCount; } uint32 PLUGIN_API release () override { if (--refCount == 0) { delete this; return 0; } return refCount; } std::atomic refCount {1}; std::u16string desc; }; //------------------------------------------------------------------------ struct FuncTest : TestBase { FuncTest (const tchar* desc, const TestFunc& func) : TestBase (desc), func (func) {} FuncTest (const tchar* desc, TestFunc&& func) : TestBase (desc), func (std::move (func)) {} bool PLUGIN_API run (ITestResult* testResult) override { return func (testResult); } TestFunc func; }; //------------------------------------------------------------------------ struct FuncWithContextTest : TestBase { FuncWithContextTest (FUnknown* context, const std::u16string& desc, const TestFuncWithContext& func) : TestBase (desc), func (func), context (context) { } bool PLUGIN_API run (ITestResult* testResult) override { return func (context, testResult); } TestFuncWithContext func; FUnknown* context; }; //------------------------------------------------------------------------ struct TestFactoryImpl : ITestFactory { TestFactoryImpl () = default; virtual ~TestFactoryImpl () = default; tresult PLUGIN_API createTests (FUnknown* context, ITestSuite* parentSuite) override { for (auto& t : TestRegistry::instance ().tests) { t.second->addRef (); parentSuite->addTest (t.first.data (), t.second); } for (auto& t : TestRegistry::instance ().testsWithContext) parentSuite->addTest (t.first.data (), new FuncWithContextTest (context, t.second.desc, t.second.func)); return kResultTrue; } tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) override { QUERY_INTERFACE (_iid, obj, FUnknown::iid, FUnknown) QUERY_INTERFACE (_iid, obj, ITestFactory::iid, ITestFactory) *obj = nullptr; return kNoInterface; } uint32 PLUGIN_API addRef () override { return ++refCount; } uint32 PLUGIN_API release () override { if (--refCount == 0) { delete this; return 0; } return refCount; } private: std::atomic refCount {1}; }; //------------------------------------------------------------------------ } // anonymous //------------------------------------------------------------------------ void registerTest (FIDString name, const tchar* desc, const TestFunc& func) { registerTest (name, new FuncTest (desc, func)); } //------------------------------------------------------------------------ void registerTest (FIDString name, const tchar* desc, TestFunc&& func) { registerTest (name, new FuncTest (desc, std::move (func))); } //------------------------------------------------------------------------ void registerTest (FIDString name, ITest* test) { assert (name != nullptr); TestRegistry::instance ().tests.push_back (std::make_pair (name, owned (test))); } //------------------------------------------------------------------------ void registerTest (FIDString name, const tchar* desc, const TestFuncWithContext& func) { std::u16string descStr; if (desc) descStr = reinterpret_cast (desc); TestRegistry::instance ().testsWithContext.push_back ( std::make_pair (name, TestRegistry::TestWithContext {descStr, func})); } //------------------------------------------------------------------------ void registerTest (FIDString name, const tchar* desc, TestFuncWithContext&& func) { std::u16string descStr; if (desc) descStr = reinterpret_cast (desc); TestRegistry::instance ().testsWithContext.push_back ( std::make_pair (name, TestRegistry::TestWithContext {descStr, std::move (func)})); } //------------------------------------------------------------------------ FUnknown* createTestFactoryInstance (void*) { return new TestFactoryImpl; } //------------------------------------------------------------------------ const FUID& getTestFactoryUID () { static FUID uid = FUID::fromTUID (TestFactoryUID); return uid; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/sampleaccurate.h0000644000000000000000000000012715124701711025224 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/sampleaccurate.h0000644000175000001440000002005515124701711025212 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/sampleaccurate.h // Created by : Steinberg, 04/2021 // Description : // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstparameterchanges.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace SampleAccurate { //------------------------------------------------------------------------ /** Utility class to handle sample accurate parameter changes coming from IParamValueQueue * * The normal use case is to setup the Parameter class once for a specific ParamID and then only * use it in the realtime process method. * * If there's a change for the parameter in the inputParameterChanges of the ProcessData structure * the Parameters beginChange method should be called with the valueQueue of the parmID and then * while processing the current audio block the parameter should be advanced as many samples as you * would like to handle parameter changes. In the end the endChanges method must be called to * cleanup internal data structures. * For convenience the endChanges method can be called without a previous beginChanges call. */ struct Parameter { Parameter (ParamID pid = 0, ParamValue initValue = 0.) noexcept; /** Set the value of the parameter * * When this is called during the beginChanges() and endChanges() sequence, the changes in the * value queue are ignored * * @param v the new value of the parameter */ void setValue (ParamValue v) noexcept; /** Set the ID of the parameter * * @param pid the new ID of the parameter */ void setParamID (ParamID pid) noexcept; /** Get the ID of the parameter * * @return ID of the parameter */ ParamID getParamID () const noexcept; /** Get the current value of the parameter * * @return current value */ ParamValue getValue () const noexcept; /** Are there any pending changes * * @return true when there are changes */ bool hasChanges () const noexcept; /** Begin change sequence * * @param valueQueue the queue with the changes */ void beginChanges (IParamValueQueue* valueQueue) noexcept; /** Advance the changes in queue * * @param numSamples how many samples to advance in the queue * @return current value */ ParamValue advance (int32 numSamples) noexcept; /** Flush all changes in the queue * * @return value after flushing */ ParamValue flushChanges () noexcept; /** End change sequence * * @return value after flushing all possible pending changes */ ParamValue endChanges () noexcept; /** Templated variant of advance * * calls Proc p with the new value if the value changes */ template void advance (int32 numSamples, Proc p); /** Templated variant of flushChanges * * calls Proc p with the new value if the value changes */ template void flushChanges (Proc p); /** Templated variant of endChanges * * calls Proc p with the new value if the value changes */ template void endChanges (Proc p); private: struct ValuePoint { ParamValue value {0.}; double rampPerSample {0.}; int32 sampleOffset {-1}; }; ValuePoint processNextValuePoint () noexcept; ParamID paramID; int32 pointCount {-1}; int32 pointIndex {0}; int32 sampleCounter {0}; ParamValue currentValue {0.}; ValuePoint valuePoint; IParamValueQueue* queue {nullptr}; }; //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE Parameter::Parameter (ParamID pid, ParamValue initValue) noexcept { setParamID (pid); setValue (initValue); } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE void Parameter::setValue (ParamValue v) noexcept { currentValue = v; pointCount = 0; valuePoint = {currentValue, 0., -1}; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE void Parameter::setParamID (ParamID pid) noexcept { paramID = pid; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE ParamID Parameter::getParamID () const noexcept { return paramID; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE ParamValue Parameter::getValue () const noexcept { return currentValue; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool Parameter::hasChanges () const noexcept { return pointCount >= 0; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE void Parameter::beginChanges (IParamValueQueue* valueQueue) noexcept { assert (queue == nullptr); assert (valueQueue->getParameterId () == getParamID ()); queue = valueQueue; pointCount = queue->getPointCount (); pointIndex = 0; sampleCounter = 0; if (pointCount) valuePoint = processNextValuePoint (); } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE ParamValue Parameter::advance (int32 numSamples) noexcept { if (pointCount < 0) return currentValue; while (valuePoint.sampleOffset >= 0 && valuePoint.sampleOffset < numSamples) { sampleCounter += valuePoint.sampleOffset; numSamples -= valuePoint.sampleOffset; currentValue = valuePoint.value; valuePoint = processNextValuePoint (); } currentValue += (valuePoint.rampPerSample * numSamples); valuePoint.sampleOffset -= numSamples; sampleCounter += numSamples; return currentValue; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE ParamValue Parameter::flushChanges () noexcept { while (pointCount >= 0) { currentValue = valuePoint.value; valuePoint = processNextValuePoint (); } currentValue = valuePoint.value; return currentValue; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE ParamValue Parameter::endChanges () noexcept { flushChanges (); pointCount = -1; queue = nullptr; return currentValue; } //------------------------------------------------------------------------ template SMTG_ALWAYS_INLINE void Parameter::advance (int32 numSamples, Proc p) { auto originalValue = currentValue; if (advance (numSamples) != originalValue) { p (currentValue); } } //------------------------------------------------------------------------ template SMTG_ALWAYS_INLINE void Parameter::flushChanges (Proc p) { auto originalValue = currentValue; if (flushChanges () != originalValue) p (currentValue); } //------------------------------------------------------------------------ template SMTG_ALWAYS_INLINE void Parameter::endChanges (Proc p) { auto originalValue = currentValue; if (endChanges () != originalValue) p (currentValue); } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE auto Parameter::processNextValuePoint () noexcept -> ValuePoint { ValuePoint nv; if (pointCount == 0 || queue->getPoint (pointIndex, nv.sampleOffset, nv.value) != kResultTrue) { pointCount = -1; return {currentValue, 0., -1}; } nv.sampleOffset -= sampleCounter; ++pointIndex; --pointCount; if (nv.sampleOffset == 0) nv.rampPerSample = (nv.value - currentValue); else nv.rampPerSample = (nv.value - currentValue) / static_cast (nv.sampleOffset); return nv; } //------------------------------------------------------------------------ } // SampleAccurate } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/testing.h0000644000000000000000000000012715124701711023710 xustar0029 mtime=1767080905.28618869 29 atime=1767080905.28618869 29 ctime=1767080905.28618869 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/testing.h0000644000175000001440000001544415124701711023704 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Examples // Filename : public.sdk/source/vst/utility/testing.h // Created by : Steinberg, 04/2021 // Description : Utility classes for custom testing in the vst validator // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/test/itest.h" #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** How to use the validator to run your own tests? \ingroup TestClass It is possible to run your own tests when the validator checks your plug-in. First you have to register a test factory in your plugin factory: \code{.cpp} #include "public.sdk/source/vst/utility/testing.h" BEGIN_FACTORY_DEF(... DEF_CLASS2 (Your Plugin Processor) DEF_CLASS2 (Your Plugin Controller) DEF_CLASS2 (INLINE_UID_FROM_FUID (getTestFactoryUID ()), PClassInfo::kManyInstances, kTestClass, "Test Factory", 0, "", "", "", createTestFactoryInstance) END_FACTORY \endcode Second: write your tests: \code{.cpp} #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/utility/testing.h" static ModuleInitializer InitMyTests ([] () { registerTest ("MyTests", STR ("two plus two is four"), [] (ITestResult* testResult) { auto result = 2 + 2; if (result == 4) return true; testResult->addErrorMessage (STR ("Unexpected universe change where 2+2 != 4.")); return false; }); }); \endcode If you need access to your audio effect or edit controller you can write your tests as done in the adelay example: \code{.cpp} #include "public.sdk/source/main/moduleinit.h" #include "public.sdk/source/vst/testsuite/vsttestsuite.h" #include "public.sdk/source/vst/utility/testing.h" static ModuleInitializer InitMyTests ([] () { registerTest ("MyTests", STR ("check one two three"), [] (FUnknown* context, ITestResult* testResult) { if (auto plugProvider = U::cast (context)) { auto controller = plugProvider->getController (); auto testController = U::cast (controller); if (!controller) { testResult->addErrorMessage (String ("Unknown IEditController")); return false; } bool result = testController->doTest (); plugProvider->releasePlugIn (nullptr, controller); return (result); } return false; }); }); \endcode After that recompile and if the validator does not run automatically after every build, start the validator manually and let it check your plug-in. */ //------------------------------------------------------------------------ /** create a Test Factory instance */ FUnknown* createTestFactoryInstance (void*); /** the test factory class ID */ static const DECLARE_UID (TestFactoryUID, 0x70AA33A3, 0x1AE74B24, 0xB726F784, 0xB706C080); /** get the test factory class ID */ const FUID& getTestFactoryUID (); /** simple test function */ using TestFunc = std::function; /** register a simple test function */ void registerTest (FIDString name, const tchar* desc, const TestFunc& func); /** register a simple test function */ void registerTest (FIDString name, const tchar* desc, TestFunc&& func); /** test function with context pointer */ using TestFuncWithContext = std::function; /** register a test function with context pointer */ void registerTest (FIDString name, const tchar* desc, const TestFuncWithContext& func); /** register a test function with context pointer */ void registerTest (FIDString name, const tchar* desc, TestFuncWithContext&& func); /** register a custom test, the test object will be owned by the implementation */ void registerTest (FIDString name, ITest* test); //------------------------------------------------------------------------ namespace Test { //------------------------------------------------------------------------ template ::value>::type* = nullptr> inline constexpr bool equal (const T& lhs, const T& rhs) noexcept { return std::abs (lhs - rhs) <= std::numeric_limits::epsilon (); } //------------------------------------------------------------------------ template ::value>::type* = nullptr> inline constexpr bool equal (const T& lhs, const T& rhs) noexcept { return lhs == rhs; } //------------------------------------------------------------------------ template inline constexpr bool notEqual (const T& lhs, const T& rhs) noexcept { return equal (lhs, rhs) == false; } //------------------------------------------------------------------------ template inline constexpr bool maxDiff (const T& lhs, const T& rhs, const T& maxDiff) noexcept { return std::abs (lhs - rhs) <= maxDiff; } #ifndef SMTG_DISABLE_VST_TEST_MACROS #ifndef SMTG_MAKE_STRING_PRIVATE_DONT_USE #define SMTG_MAKE_STRING_PRIVATE_DONT_USE(x) #x #define SMTG_MAKE_STRING(x) SMTG_MAKE_STRING_PRIVATE_DONT_USE (x) #endif // SMTG_MAKE_STRING_PRIVATE_DONT_USE #define EXPECT(condition) \ { \ if (!(condition)) \ { \ testResult->addErrorMessage (STR (__FILE__ ":" SMTG_MAKE_STRING ( \ __LINE__) ": error: " SMTG_MAKE_STRING (condition))); \ return false; \ } \ } #define EXPECT_TRUE(condition) EXPECT (condition) #define EXPECT_FALSE(condition) EXPECT (!condition) #define EXPECT_EQ(var1, var2) EXPECT ((var1 == var2)) #define EXPECT_NE(var1, var2) EXPECT ((var1 != var2)) #endif // SMTG_DISABLE_VST_TEST_MACROS //------------------------------------------------------------------------ } // Test } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/memoryibstream.h0000644000000000000000000000012715124701711025272 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/memoryibstream.h0000644000175000001440000001145515124701711025264 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // Category : Helpers // Filename : public.sdk/source/vst/utility/memoryibstream.h // Created by : Steinberg, 12/2023 // Description : // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/base/ibstream.h" #include //------------------------------------------------------------------------ namespace Steinberg { //------------------------------------------------------------------------ class ResizableMemoryIBStream : public U::Implements> { public: inline ResizableMemoryIBStream (size_t reserve = 0); inline tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead) override; inline tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten) override; inline tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result) override; inline tresult PLUGIN_API tell (int64* pos) override; inline size_t getCursor () const; inline const void* getData () const; inline void rewind (); inline std::vector&& take (); private: std::vector data; size_t cursor {0}; }; //------------------------------------------------------------------------ inline ResizableMemoryIBStream::ResizableMemoryIBStream (size_t reserve) { if (reserve) data.reserve (reserve); } //------------------------------------------------------------------------ inline tresult PLUGIN_API ResizableMemoryIBStream::read (void* buffer, int32 numBytes, int32* numBytesRead) { if (numBytes < 0 || buffer == nullptr) return kInvalidArgument; auto byteCount = std::min (numBytes, data.size () - cursor); if (byteCount > 0) { memcpy (buffer, data.data () + cursor, byteCount); cursor += byteCount; } if (numBytesRead) *numBytesRead = static_cast (byteCount); return kResultTrue; } //------------------------------------------------------------------------ inline tresult PLUGIN_API ResizableMemoryIBStream::write (void* buffer, int32 numBytes, int32* numBytesWritten) { if (numBytes < 0 || buffer == nullptr) return kInvalidArgument; auto requiredSize = cursor + numBytes; if (requiredSize >= data.capacity ()) { auto mod = (requiredSize % 1024); if (mod) { auto reserve = requiredSize + (1024 - mod); data.reserve (reserve); } } if (data.size () < requiredSize) data.resize (requiredSize); memcpy (data.data () + cursor, buffer, numBytes); cursor += numBytes; if (numBytesWritten) *numBytesWritten = numBytes; return kResultTrue; } //------------------------------------------------------------------------ inline tresult PLUGIN_API ResizableMemoryIBStream::seek (int64 pos, int32 mode, int64* result) { int64 newCursor = static_cast (cursor); switch (mode) { case kIBSeekSet: newCursor = pos; break; case kIBSeekCur: newCursor += pos; break; case kIBSeekEnd: newCursor = data.size () + pos; break; default: return kInvalidArgument; } if (newCursor < 0) return kInvalidArgument; if (newCursor > static_cast (data.size ())) return kInvalidArgument; if (result) *result = newCursor; cursor = static_cast (newCursor); return kResultTrue; } //------------------------------------------------------------------------ inline tresult PLUGIN_API ResizableMemoryIBStream::tell (int64* pos) { if (pos == nullptr) return kInvalidArgument; *pos = static_cast (cursor); return kResultTrue; } //------------------------------------------------------------------------ inline size_t ResizableMemoryIBStream::getCursor () const { return cursor; } //------------------------------------------------------------------------ inline const void* ResizableMemoryIBStream::getData () const { return data.data (); } //------------------------------------------------------------------------ inline std::vector&& ResizableMemoryIBStream::take () { cursor = 0; return std::move (data); } //------------------------------------------------------------------------ inline void ResizableMemoryIBStream::rewind () { cursor = 0; } //------------------------------------------------------------------------ } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/uid.h0000644000000000000000000000012715124701711023014 xustar0029 mtime=1767080905.28618869 29 atime=1767080905.28618869 29 ctime=1767080905.28618869 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/uid.h0000644000175000001440000001747515124701711023016 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/uid.h // Created by : Steinberg, 08/2016 // Description : UID // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "optional.h" #include "pluginterfaces/base/funknown.h" #include //------------------------------------------------------------------------ namespace VST3 { //------------------------------------------------------------------------ struct UID { #if defined(SMTG_OS_WINDOWS) && SMTG_OS_WINDOWS == 1 static constexpr bool defaultComFormat = true; #else static constexpr bool defaultComFormat = false; #endif using TUID = Steinberg::TUID; constexpr UID () noexcept = default; UID (uint32_t l1, uint32_t l2, uint32_t l3, uint32_t l4, bool comFormat = defaultComFormat) noexcept; UID (const TUID& uid) noexcept; UID (const UID& uid) noexcept; UID& operator= (const UID& uid) noexcept; UID& operator= (const TUID& uid) noexcept; constexpr const TUID& data () const noexcept; constexpr size_t size () const noexcept; std::string toString (bool comFormat = defaultComFormat) const noexcept; template static Optional fromString (const StringT& str, bool comFormat = defaultComFormat) noexcept; static UID fromTUID (const TUID _uid) noexcept; //------------------------------------------------------------------------ private: Steinberg::TUID _data {}; struct GUID { uint32_t Data1; uint16_t Data2; uint16_t Data3; uint8_t Data4[8]; }; }; //------------------------------------------------------------------------ inline bool operator== (const UID& uid1, const UID& uid2) { const uint64_t* p1 = reinterpret_cast (uid1.data ()); const uint64_t* p2 = reinterpret_cast (uid2.data ()); return p1[0] == p2[0] && p1[1] == p2[1]; } //------------------------------------------------------------------------ inline bool operator!= (const UID& uid1, const UID& uid2) { return !(uid1 == uid2); } //------------------------------------------------------------------------ inline bool operator< (const UID& uid1, const UID& uid2) { const uint64_t* p1 = reinterpret_cast (uid1.data ()); const uint64_t* p2 = reinterpret_cast (uid2.data ()); return (p1[0] < p2[0]) && (p1[1] < p2[1]); } //------------------------------------------------------------------------ inline UID::UID (uint32_t l1, uint32_t l2, uint32_t l3, uint32_t l4, bool comFormat) noexcept { if (comFormat) { _data[0] = static_cast ((l1 & 0x000000FF)); _data[1] = static_cast ((l1 & 0x0000FF00) >> 8); _data[2] = static_cast ((l1 & 0x00FF0000) >> 16); _data[3] = static_cast ((l1 & 0xFF000000) >> 24); _data[4] = static_cast ((l2 & 0x00FF0000) >> 16); _data[5] = static_cast ((l2 & 0xFF000000) >> 24); _data[6] = static_cast ((l2 & 0x000000FF)); _data[7] = static_cast ((l2 & 0x0000FF00) >> 8); _data[8] = static_cast ((l3 & 0xFF000000) >> 24); _data[9] = static_cast ((l3 & 0x00FF0000) >> 16); _data[10] = static_cast ((l3 & 0x0000FF00) >> 8); _data[11] = static_cast ((l3 & 0x000000FF)); _data[12] = static_cast ((l4 & 0xFF000000) >> 24); _data[13] = static_cast ((l4 & 0x00FF0000) >> 16); _data[14] = static_cast ((l4 & 0x0000FF00) >> 8); _data[15] = static_cast ((l4 & 0x000000FF)); } else { _data[0] = static_cast ((l1 & 0xFF000000) >> 24); _data[1] = static_cast ((l1 & 0x00FF0000) >> 16); _data[2] = static_cast ((l1 & 0x0000FF00) >> 8); _data[3] = static_cast ((l1 & 0x000000FF)); _data[4] = static_cast ((l2 & 0xFF000000) >> 24); _data[5] = static_cast ((l2 & 0x00FF0000) >> 16); _data[6] = static_cast ((l2 & 0x0000FF00) >> 8); _data[7] = static_cast ((l2 & 0x000000FF)); _data[8] = static_cast ((l3 & 0xFF000000) >> 24); _data[9] = static_cast ((l3 & 0x00FF0000) >> 16); _data[10] = static_cast ((l3 & 0x0000FF00) >> 8); _data[11] = static_cast ((l3 & 0x000000FF)); _data[12] = static_cast ((l4 & 0xFF000000) >> 24); _data[13] = static_cast ((l4 & 0x00FF0000) >> 16); _data[14] = static_cast ((l4 & 0x0000FF00) >> 8); _data[15] = static_cast ((l4 & 0x000000FF)); } } //------------------------------------------------------------------------ inline UID::UID (const TUID& uid) noexcept { *this = uid; } //------------------------------------------------------------------------ inline UID::UID (const UID& uid) noexcept { *this = uid; } //------------------------------------------------------------------------ inline UID& UID::operator= (const UID& uid) noexcept { *this = uid.data (); return *this; } //------------------------------------------------------------------------ inline UID& UID::operator= (const TUID& uid) noexcept { memcpy (_data, reinterpret_cast(uid), 16); return *this; } //------------------------------------------------------------------------ inline constexpr auto UID::data () const noexcept -> const TUID& { return _data; } //------------------------------------------------------------------------ inline constexpr size_t UID::size () const noexcept { return sizeof (TUID); } //------------------------------------------------------------------------ inline std::string UID::toString (bool comFormat) const noexcept { std::string result; result.reserve (32); if (comFormat) { const auto& g = reinterpret_cast (_data); char tmp[21] {}; snprintf (tmp, 21, "%08X%04X%04X", g->Data1, g->Data2, g->Data3); result = tmp; for (uint32_t i = 0; i < 8; ++i) { char s[3] {}; snprintf (s, 3, "%02X", g->Data4[i]); result += s; } } else { for (uint32_t i = 0; i < 16; ++i) { char s[3] {}; snprintf (s, 3, "%02X", static_cast (_data[i])); result += s; } } return result; } //------------------------------------------------------------------------ template inline Optional UID::fromString (const StringT& str, bool comFormat) noexcept { if (str.length () != 32) return {}; // TODO: this is a copy from FUID. there are no input validation checks !!! if (comFormat) { TUID uid {}; GUID g; char s[33]; strcpy (s, str.data ()); s[8] = 0; sscanf (s, "%x", &g.Data1); strcpy (s, str.data () + 8); s[4] = 0; sscanf (s, "%hx", &g.Data2); strcpy (s, str.data () + 12); s[4] = 0; sscanf (s, "%hx", &g.Data3); memcpy (uid, &g, 8); for (uint32_t i = 8; i < 16; ++i) { char s2[3] {}; s2[0] = str[i * 2]; s2[1] = str[i * 2 + 1]; int32_t d = 0; sscanf (s2, "%2x", &d); uid[i] = static_cast (d); } return {uid}; } else { TUID uid {}; for (uint32_t i = 0; i < 16; ++i) { char s[3] {}; s[0] = str[i * 2]; s[1] = str[i * 2 + 1]; int32_t d = 0; sscanf (s, "%2x", &d); uid[i] = static_cast (d); } return {uid}; } } //------------------------------------------------------------------------ inline UID UID::fromTUID (const TUID _uid) noexcept { UID result; memcpy (result._data, reinterpret_cast(_uid), 16); return result; } //------------------------------------------------------------------------ } // VST3 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/alignedalloc.h0000644000000000000000000000013215124701711024645 xustar0030 mtime=1767080905.285285125 30 atime=1767080905.285285125 30 ctime=1767080905.285285125 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/alignedalloc.h0000644000175000001440000000451715124701711024644 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/alignedalloc.h // Created by : Steinberg, 05/2023 // Description : aligned memory allocations // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ftypes.h" #include #if __APPLE__ #include #endif #ifdef _MSC_VER #include #endif //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** aligned allocation * * note that you need to use aligned_free to free the block of memory * * @param numBytes number of bytes to allocate * @param alignment alignment of memory base address. * must be a power of 2 and at least as large as sizeof (void*) or zero in which it uses malloc * for allocation * * @return allocated memory */ inline void* aligned_alloc (size_t numBytes, uint32_t alignment) { if (alignment == 0) return malloc (numBytes); void* data {nullptr}; #if SMTG_OS_MACOS && defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_15 posix_memalign (&data, alignment, numBytes); #elif defined(_MSC_VER) data = _aligned_malloc (numBytes, alignment); #else data = std::aligned_alloc (alignment, numBytes); #endif return data; } //------------------------------------------------------------------------ inline void aligned_free (void* addr, uint32_t alignment) { if (alignment == 0) std::free (addr); else { #if defined(_MSC_VER) _aligned_free (addr); #else std::free (addr); #endif } } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/objcclassbuilder.h0000644000000000000000000000012715124701711025545 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/objcclassbuilder.h0000644000175000001440000001634115124701711025536 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/objcclassbuilder.h // Created by : Steinberg, 06/2022 // Description : Objective-C class builder utilities // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include #include #include #include #include #include #include //------------------------------------------------------------------------------------ namespace Steinberg { //------------------------------------------------------------------------------------ template struct ObjCVariable { ObjCVariable (__unsafe_unretained id obj, Ivar ivar) : obj (obj), ivar (ivar) {} ObjCVariable (ObjCVariable&& o) { *this = std::move (o); } ObjCVariable& operator= (ObjCVariable&& o) { obj = o.obj; ivar = o.ivar; o.obj = nullptr; o.ivar = nullptr; return *this; } T get () const { auto offset = ivar_getOffset (ivar); return *reinterpret_cast (((__bridge uintptr_t)obj) + offset); } void set (const T& value) { auto offset = ivar_getOffset (ivar); auto storage = reinterpret_cast (((__bridge uintptr_t)obj) + offset); *storage = value; } private: __unsafe_unretained id obj; Ivar ivar {nullptr}; }; //------------------------------------------------------------------------------------ struct ObjCInstance { ObjCInstance (__unsafe_unretained id obj, Class superClass = nullptr) : obj (obj) { os.super_class = superClass; } template std::optional> getVariable (const char* name) const { if (__strong auto ivar = class_getInstanceVariable (object_getClass (obj), name)) { return {ObjCVariable (obj, ivar)}; } return {}; } template void callSuper (SEL selector, T... args) const { void (*f) (__unsafe_unretained id, SEL, T...) = (void (*) (__unsafe_unretained id, SEL, T...))objc_msgSendSuper; f (getSuper (), selector, args...); } template R callSuper (SEL selector, T... args) const { R (*f) (__unsafe_unretained id, SEL, T...) = (R (*) (__unsafe_unretained id, SEL, T...))objc_msgSendSuper; return f (getSuper (), selector, args...); } private: id getSuper () const { if (os.receiver == nullptr) { os.receiver = obj; } if (os.super_class == nullptr) { os.super_class = class_getSuperclass (object_getClass (obj)); } return (__bridge id) (&os); } __unsafe_unretained id obj; mutable objc_super os {}; }; //------------------------------------------------------------------------------------ struct ObjCClassBuilder { ObjCClassBuilder& init (const char* name, Class baseClass); template ObjCClassBuilder& addMethod (SEL selector, Func imp); template ObjCClassBuilder& addIvar (const char* name); ObjCClassBuilder& addProtocol (const char* name); ObjCClassBuilder& addProtocol (Protocol* proto); Class finalize (); private: static Class generateUniqueClass (const std::string& inClassName, Class baseClass); template ObjCClassBuilder& addMethod (SEL selector, Func imp, const char* types); ObjCClassBuilder& addIvar (const char* name, size_t size, uint8_t alignment, const char* types); template static constexpr std::tuple functionArgs (R (*) (T...)) { return std::tuple (); } template static constexpr std::tuple functionArgs (void (*) (T...)) { return std::tuple (); } template static constexpr bool isVoidReturnType (R (*) (T...)) { return false; } template static constexpr bool isVoidReturnType (void (*) (T...)) { return true; } template static std::string encodeFunction (Proc proc) { std::string result; if (isVoidReturnType (proc)) result = "v"; std::apply ([&] (auto&&... args) { ((result += @encode (decltype (args))), ...); }, functionArgs (proc)); return result; } Class cl {nullptr}; Class baseClass {nullptr}; }; //------------------------------------------------------------------------ inline ObjCClassBuilder& ObjCClassBuilder::init (const char* name, Class bc) { baseClass = bc; cl = generateUniqueClass (name, baseClass); return *this; } //------------------------------------------------------------------------------------ inline Class ObjCClassBuilder::generateUniqueClass (const std::string& inClassName, Class baseClass) { std::string className (inClassName); int32_t iteration = 0; while (objc_lookUpClass (className.data ()) != nil) { iteration++; className = inClassName + "_" + std::to_string (iteration); } Class resClass = objc_allocateClassPair (baseClass, className.data (), 0); return resClass; } //----------------------------------------------------------------------------- inline Class ObjCClassBuilder::finalize () { objc_registerClassPair (cl); auto res = cl; baseClass = cl = nullptr; return res; } //----------------------------------------------------------------------------- template inline ObjCClassBuilder& ObjCClassBuilder::addMethod (SEL selector, Func imp, const char* types) { auto res = class_addMethod (cl, selector, IMP (imp), types); assert (res == true); (void)res; return *this; } //----------------------------------------------------------------------------- template ObjCClassBuilder& ObjCClassBuilder::addMethod (SEL selector, Func imp) { return addMethod (selector, imp, encodeFunction (imp).data ()); } //------------------------------------------------------------------------ template inline ObjCClassBuilder& ObjCClassBuilder::addIvar (const char* name) { return addIvar (name, sizeof (T), static_cast (std::log2 (sizeof (T))), @encode (T)); } //----------------------------------------------------------------------------- inline ObjCClassBuilder& ObjCClassBuilder::addIvar (const char* name, size_t size, uint8_t alignment, const char* types) { auto res = class_addIvar (cl, name, size, alignment, types); assert (res == true); (void)res; return *this; } //----------------------------------------------------------------------------- inline ObjCClassBuilder& ObjCClassBuilder::addProtocol (const char* name) { if (auto protocol = objc_getProtocol (name)) return addProtocol (protocol); return *this; } //----------------------------------------------------------------------------- inline ObjCClassBuilder& ObjCClassBuilder::addProtocol (Protocol* proto) { auto res = class_addProtocol (cl, proto); assert (res == true); (void)res; return *this; } //------------------------------------------------------------------------------------ } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/versionparser.h0000644000000000000000000000013215124701711025131 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/versionparser.h0000644000175000001440000001011615124701711025120 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : VST3 SDK // Filename : public.sdk/source/vst/utility/versionparser.h // Created by : Steinberg, 04/2018 // Description : version parser helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/utility/optional.h" #include #include #include #if __cplusplus >= 201703L #include #define SMTG_VERSIONPARSER_USE_STRINGVIEW #else #include #endif //------------------------------------------------------------------------ namespace VST3 { //------------------------------------------------------------------------ struct Version { private: enum { Major, Minor, Sub, BuildNumber }; public: #ifdef SMTG_VERSIONPARSER_USE_STRINGVIEW using StringType = std::string_view; #else using StringType = std::string; #endif Version (uint32_t inMajor = 0, uint32_t inMinor = 0, uint32_t inSub = 0, uint32_t inBuildnumber = 0) { setMajor (inMajor); setMinor (inMinor); setSub (inSub); setBuildnumber (inBuildnumber); } void setMajor (uint32_t v) { storage[Major] = v; } void setMinor (uint32_t v) { storage[Minor] = v; } void setSub (uint32_t v) { storage[Sub] = v; } void setBuildnumber (uint32_t v) { storage[BuildNumber] = v; } uint32_t getMajor () const { return storage[Major]; } uint32_t getMinor () const { return storage[Minor]; } uint32_t getSub () const { return storage[Sub]; } uint32_t getBuildnumber () const { return storage[BuildNumber]; } bool operator> (const Version& v) const { if (getMajor () < v.getMajor ()) return false; if (getMajor () > v.getMajor ()) return true; if (getMinor () < v.getMinor ()) return false; if (getMinor () > v.getMinor ()) return true; if (getSub () < v.getSub ()) return false; if (getSub () > v.getSub ()) return true; if (getBuildnumber () < v.getBuildnumber ()) return false; return getBuildnumber () > v.getBuildnumber (); } static Version parse (StringType str) { // skip non digits in the front auto it = std::find_if (str.begin (), str.end (), [] (const auto& c) { return std::isdigit (c); }); if (it == str.end ()) return {}; #ifdef SMTG_VERSIONPARSER_USE_STRINGVIEW str = StringType (&(*it), std::distance (it, str.end ())); #else str = StringType (it, str.end ()); #endif Version version {}; auto part = static_cast (Major); StringType::size_type index; while (!str.empty ()) { index = str.find_first_of ('.'); if (index == StringType::npos) { // skip non digits in the back auto itBack = std::find_if (str.begin (), str.end (), [] (const auto& c) { return !std::isdigit (c); }); index = std::distance (str.begin (), itBack); if (index == 0) break; str = {str.data (), index}; index = str.size (); } StringType numberStr (str.data (), index); if (auto n = toNumber (numberStr)) version.storage[part] = *n; if (++part > BuildNumber) break; if (str.size () - index == 0) break; ++index; str = {str.data () + index, str.size () - index}; } return version; } private: std::array storage {}; static Optional toNumber (StringType str) { if (str.size () > 9) str = {str.data (), 9}; int32_t result = 0; for (const auto& c : str) { if (c < 48 || c > 57) return {}; result *= 10; result += c - 48; } return Optional {result}; } }; //------------------------------------------------------------------------ } // VST3 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/vst2persistence.h0000644000000000000000000000013215124701711025372 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/vst2persistence.h0000644000175000001440000000762515124701711025374 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : VST3 SDK // Filename : public.sdk/source/vst/utility/vst2persistence.h // Created by : Steinberg, 12/2019 // Description : vst2 persistence helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/utility/optional.h" #include "pluginterfaces/base/ibstream.h" #include #include //------------------------------------------------------------------------ namespace VST3 { //------------------------------------------------------------------------ using Vst2xChunk = std::vector; //------------------------------------------------------------------------ /** structure holding the content of a vst2 fxp format stream * * either the values member is valid or the chunk member but not both */ struct Vst2xProgram { using ProgramValues = std::vector; ProgramValues values; Vst2xChunk chunk; int32_t fxUniqueID {0}; int32_t fxVersion {0}; std::string name; }; //------------------------------------------------------------------------ /** structure holding the content of a vst2 fxb format stream * * either the programs member is valid or the chunk member but not both */ struct Vst2xState { using Programs = std::vector; Programs programs; Vst2xChunk chunk; int32_t fxUniqueID {0}; int32_t fxVersion {0}; int32_t currentProgram {0}; bool isBypassed {false}; }; //------------------------------------------------------------------------ /** Try loading the state from an old vst2 fxb format stream * * If successfully loaded, the state has either a chunk or programs but not both * The Vst2xState::isBypassed boolean will be set if a Steinberg host has written the state into a * project and the plug-in was bypassed. * * @param stream the input stream * @param vst2xUniqueID vst2 unique id expected to be stored in the stream [optional]. If present * the fxb unique id header entry must be the same as this otherwise the * return value is empty. * @return on success the optional has a Vst2xState object with the data */ Optional tryVst2StateLoad (Steinberg::IBStream& stream, Optional vst2xUniqueID = {}) noexcept; //------------------------------------------------------------------------ /** Write a vst2 fxb stream * * Writes the state into stream as a vst2 fxb format * * @param state the state which should be written * @param stream the stream where the state should be written into * @param writeBypassState write extra chunk with bypass state * @return true on success */ bool writeVst2State (const Vst2xState& state, Steinberg::IBStream& stream, bool writeBypassState = true) noexcept; //------------------------------------------------------------------------ /** Try loading the state from on old vst2 fxp format stream * * If successfully loaded, the program has either a chunk or plain values but not both * * @param stream the input stream * @param vst2xUniqueID vst2 unique id expected to be stored in the stream * @return on success the optional has a Vst2xProgram object with the data */ Optional tryVst2ProgramLoad (Steinberg::IBStream& stream, Optional vst2xUniqueID) noexcept; //------------------------------------------------------------------------ } // VST3 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/PaxHeaders/systemtime.cpp0000644000000000000000000000012715124701711024771 xustar0029 mtime=1767080905.28554689 29 atime=1767080905.28554689 29 ctime=1767080905.28554689 qtractor-1.5.11/src/vst3/public.sdk/source/vst/utility/systemtime.cpp0000644000175000001440000001143715124701711024763 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // Category : Helpers // Filename : public.sdk/source/vst/utility/systemtime.cpp // Created by : Steinberg, 06/2023 // Description : VST Component System Time API Helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "systemtime.h" #include "pluginterfaces/base/funknownimpl.h" #include #if SMTG_OS_OSX #include //------------------------------------------------------------------------ static Steinberg::Vst::SystemTime::GetImplFunc makeNativeGetSystemTimeFunc () { return [] () { return static_cast ( AudioConvertHostTimeToNanos (AudioGetCurrentHostTime ())); }; } #elif SMTG_OS_IOS #include //------------------------------------------------------------------------ static Steinberg::Vst::SystemTime::GetImplFunc makeNativeGetSystemTimeFunc () { static struct mach_timebase_info timebaseInfo; mach_timebase_info (&timebaseInfo); return [&] () { double absTime = static_cast (mach_absolute_time ()); // nano seconds double d = (absTime / timebaseInfo.denom) * timebaseInfo.numer; return static_cast (d); }; } #elif SMTG_OS_WINDOWS #ifndef NOMINMAX #define NOMINMAX #endif #include //------------------------------------------------------------------------ struct WinmmDll { static WinmmDll& instance () { static WinmmDll gInstance; return gInstance; } bool valid () const { return func != nullptr; } DWORD timeGetTime () const { return func (); } private: using TimeGetTimeFunc = DWORD (WINAPI*) (); WinmmDll () { dll = LoadLibraryA ("winmm.dll"); if (dll) { func = reinterpret_cast (GetProcAddress (dll, "timeGetTime")); } } TimeGetTimeFunc func {nullptr}; HMODULE dll; }; //------------------------------------------------------------------------ static Steinberg::Vst::SystemTime::GetImplFunc makeNativeGetSystemTimeFunc () { if (WinmmDll::instance ().valid ()) { return [dll = WinmmDll::instance ()] () { return static_cast (dll.timeGetTime ()) * 1000000; }; } return [] () { return std::numeric_limits::max (); }; } #elif SMTG_OS_LINUX //------------------------------------------------------------------------ #include static uint64_t getUptimeByClockGettime () { struct timespec time_spec; if (clock_gettime (CLOCK_BOOTTIME, &time_spec) != 0) return 0; const uint64_t uptime = time_spec.tv_sec * 1000 + time_spec.tv_nsec / 1000000; return uptime; } static Steinberg::Vst::SystemTime::GetImplFunc makeNativeGetSystemTimeFunc () { return [] () { return static_cast (getUptimeByClockGettime ()); }; } #else //------------------------------------------------------------------------ static Steinberg::Vst::SystemTime::GetImplFunc makeNativeGetSystemTimeFunc () { return [] () { return std::numeric_limits::max (); }; } #endif //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ SystemTime::SystemTime (IComponentHandler* componentHandler) { if (auto chst = U::cast (componentHandler)) { getImpl = [host = std::move (chst)] ()->int64 { int64 value = 0; if (host->getSystemTime (value) == kResultTrue) return value; return std::numeric_limits::max (); }; } else { getImpl = makeNativeGetSystemTimeFunc (); } } //------------------------------------------------------------------------ SystemTime::SystemTime (const SystemTime& st) { *this = st; } //------------------------------------------------------------------------ SystemTime::SystemTime (SystemTime&& st) noexcept { *this = std::move (st); } //------------------------------------------------------------------------ SystemTime& SystemTime::operator= (const SystemTime& st) { getImpl = st.getImpl; return *this; } //------------------------------------------------------------------------ SystemTime& SystemTime::operator= (SystemTime&& st) noexcept { std::swap (getImpl, st.getImpl); return *this; } //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstaudioeffect.h0000644000000000000000000000013215124701711023537 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstaudioeffect.h0000644000175000001440000001062715124701711023535 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstaudioeffect.h // Created by : Steinberg, 04/2005 // Description : Basic Audio Effect Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/vstbus.h" #include "public.sdk/source/vst/vstcomponent.h" #include "public.sdk/source/vst/utility/processcontextrequirements.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Default implementation for a VST 3 audio effect. \ingroup vstClasses Can be used as base class for a VST 3 effect implementation. */ class AudioEffect : public Component, public IAudioProcessor, public IProcessContextRequirements { public: //------------------------------------------------------------------------ /** Constructor */ AudioEffect (); //---Internal Methods----------- /** Creates and adds a new Audio input bus with a given speaker arrangement, busType (kMain or * kAux). */ AudioBus* addAudioInput (const TChar* name, SpeakerArrangement arr, BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); /** Creates and adds a new Audio output bus with a given speaker arrangement, busType (kMain or * kAux). */ AudioBus* addAudioOutput (const TChar* name, SpeakerArrangement arr, BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); /** Retrieves an Audio Input Bus by index. */ AudioBus* getAudioInput (int32 index); /** Retrieves an Audio Output Bus by index. */ AudioBus* getAudioOutput (int32 index); /** Creates and adds a new Event input bus with a given speaker arrangement, busType (kMain or * kAux). */ EventBus* addEventInput (const TChar* name, int32 channels = 16, BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); /** Creates and adds a new Event output bus with a given speaker arrangement, busType (kMain or * kAux). */ EventBus* addEventOutput (const TChar* name, int32 channels = 16, BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); /** Retrieves an Event Input Bus by index. */ EventBus* getEventInput (int32 index); /** Retrieves an Event Output Bus by index. */ EventBus* getEventOutput (int32 index); //---from IAudioProcessor------- tresult PLUGIN_API setBusArrangements (SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts) SMTG_OVERRIDE; tresult PLUGIN_API getBusArrangement (BusDirection dir, int32 busIndex, SpeakerArrangement& arr) SMTG_OVERRIDE; tresult PLUGIN_API canProcessSampleSize (int32 symbolicSampleSize) SMTG_OVERRIDE; uint32 PLUGIN_API getLatencySamples () SMTG_OVERRIDE { return 0; } tresult PLUGIN_API setupProcessing (ProcessSetup& setup) SMTG_OVERRIDE; tresult PLUGIN_API setProcessing (TBool state) SMTG_OVERRIDE; tresult PLUGIN_API process (ProcessData& data) SMTG_OVERRIDE; uint32 PLUGIN_API getTailSamples () SMTG_OVERRIDE { return kNoTail; } //---from IProcessContextRequirements------- uint32 PLUGIN_API getProcessContextRequirements () SMTG_OVERRIDE; //---Interface--------- OBJ_METHODS (AudioEffect, Component) DEFINE_INTERFACES DEF_INTERFACE (IAudioProcessor) DEF_INTERFACE (IProcessContextRequirements) END_DEFINE_INTERFACES (Component) REFCOUNT_METHODS (Component) //------------------------------------------------------------------------ protected: ProcessSetup processSetup; ProcessContextRequirements processContextRequirements; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstrepresentation.cpp0000644000000000000000000000012715124701711024662 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstrepresentation.cpp0000644000175000001440000002703515124701711024655 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstrepresentation.cpp // Created by : Steinberg, 08/2010 // Description : VST Representation Helper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstrepresentation.h" #include "base/source/fstring.h" #include "pluginterfaces/base/ibstream.h" namespace Steinberg { namespace Vst { #define START_TAG_STRING(string) "<" string ">" #define END_TAG_STRING(string) "" #define MEDIUM_TITLE_LIMIT 8 #define SHORT_TITLE_LIMIT 4 //------------------------------------------------------------------------ struct StringWriter { StringWriter (IBStream* stream) : stream (stream) {} void write (const ConstString& str) { stream->write (const_cast (str.text8 ()), str.length (), nullptr); } IBStream* stream; }; //------------------------------------------------------------------------ // XmlRepresentationHelper Implementation //------------------------------------------------------------------------ XmlRepresentationHelper::XmlRepresentationHelper (const Vst::RepresentationInfo& info, const FIDString companyName, const FIDString pluginName, const TUID& pluginUID, IBStream* stream) : stream (stream) { StringWriter writer (stream); String string; writer.write (R"()"); writer.write (ENDLINE_A); string.printf ( R"()", ROOTXML_TAG); writer.write (string.text8 ()); writer.write (ENDLINE_A); string.printf ("<%s %s=\"1.0\">", ROOTXML_TAG, ATTR_VERSION); writer.write (string.text8 ()); writer.write (ENDLINE_A); //---Plug-in Tag---------------- FUID uid (pluginUID); char uidText[33]; uid.toString (uidText); string.printf (R"(<%s %s="%s" %s="%s" %s="%s"/>)", PLUGIN_TAG, ATTR_CLASSID, uidText, ATTR_NAME, pluginName, ATTR_VENDOR, companyName); writer.write (string); writer.write (ENDLINE_A); //---Representation Tag---------------- string.printf ("\t<%s", REPRESENTATION_TAG); writer.write (string); string.printf (" %s=\"%s\"", ATTR_NAME, info.name); writer.write (string); string.printf (" %s=\"%s\"", ATTR_VENDOR, info.vendor); writer.write (string); string.printf (" %s=\"%s\"", ATTR_VERSION, info.version); writer.write (string); if (strcmp ((char*)info.host, "") != 0) { string.printf (" %s=\"%s\"", ATTR_HOST, info.host); writer.write (string); } writer.write (">"); writer.write (ENDLINE_A); state = kInRepresentation; } //------------------------------------------------------------------------ XmlRepresentationHelper::~XmlRepresentationHelper () { if (state == kInLayer) endLayer (); if (state == kInCell) endCell (); if (state == kInPage) endPage (); StringWriter writer (stream); String string; // end representation string.printf ("\t%s", END_TAG_STRING (REPRESENTATION_TAG)); writer.write (string); writer.write (ENDLINE_A); // end piper writer.write (END_TAG_STRING (ROOTXML_TAG)); writer.write (ENDLINE_A); } //------------------------------------------------------------------------ bool XmlRepresentationHelper::checkState (int32 newState) { if (newState - state == 1 || state - newState == 1) // we go down or up { state = newState; return true; } return false; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startPage (FIDString name, int32 unitID) { if (!checkState (kInPage)) return false; StringWriter writer (stream); String string; if (unitID != -1) string.printf (R"(<%s %s="%s" %s="%d">)", PAGE_TAG, ATTR_NAME, name, ATTR_UNITID, unitID); else string.printf ("<%s %s=\"%s\">", PAGE_TAG, ATTR_NAME, name); writer.write (string); writer.write (ENDLINE_A); return true; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::endPage () { if (!checkState (kInRepresentation)) return false; StringWriter writer (stream); String string; string.printf ("%s", END_TAG_STRING (PAGE_TAG)); writer.write (string); writer.write (ENDLINE_A); return true; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startCell () { if (!checkState (kInCell)) return false; StringWriter writer (stream); String string; string.printf ("%s", START_TAG_STRING (CELL_TAG)); writer.write (string); writer.write (ENDLINE_A); return true; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::endCell () { if (!checkState (kInPage)) return false; StringWriter writer (stream); String string; string.printf ("%s", END_TAG_STRING (CELL_TAG)); writer.write (string); writer.write (ENDLINE_A); return true; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startEndCell () { if (!checkState (kInCell)) return false; StringWriter writer (stream); String string; string.printf ("<%s/>", CELL_TAG); writer.write (string); writer.write (ENDLINE_A); if (!checkState (kInPage)) return false; return true; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startLayer (int32 type, int32 id, FIDString _function, FIDString style) { return startLayer (type, id, _function, style, false); } //------------------------------------------------------------------------ bool XmlRepresentationHelper::endLayer () { if (!checkState (kInCell)) return false; StringWriter writer (stream); String string; string.printf ("%s", END_TAG_STRING (LAYER_TAG)); writer.write (string); writer.write (ENDLINE_A); return true; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startEndLayer (int32 type, int32 id, FIDString _function, FIDString style) { return startLayer (type, id, _function, style, true); } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startLayer (int32 type, int32 id, FIDString _function, FIDString style, bool ended) { if (!checkState (kInLayer)) return false; StringWriter writer (stream); String string; string.printf (R"(<%s %s="%s" %s="%d")", LAYER_TAG, ATTR_TYPE, Vst::LayerType::layerTypeFIDString[type], ATTR_PARAMID, id); writer.write (string); if (_function) { string.printf (" %s=\"%s\"", Vst::Attributes::kFunction, _function); writer.write (string); } if (style) { string = Vst::LayerType::layerTypeFIDString[type]; if (type == Vst::LayerType::kSwitch) string.printf (" %s=\"%s\"", Vst::Attributes::kSwitchStyle, style); else if (type == Vst::LayerType::kLED) string.printf (" %s=\"%s\"", Vst::Attributes::kLEDStyle, style); else string.printf (" %s=\"%s\"", Vst::Attributes::kStyle, style); writer.write (string); } if (ended) { writer.write ("/>"); if (!checkState (kInCell)) return false; } else writer.write (">"); writer.write (ENDLINE_A); return true; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startEndCellOneLayer (int32 type, int32 id, FIDString _function, FIDString style) { if (!startCell ()) return false; startEndLayer (type, id, _function, style); endCell (); return true; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startLayer (Vst::ParameterInfo& info, FIDString _function) { return startLayer (info, _function, false); } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startLayer (Vst::ParameterInfo& info, FIDString _function, bool ended) { FIDString style = nullptr; int32 type = Vst::LayerType::kKnob; if (info.flags & Vst::ParameterInfo::kIsReadOnly) type = Vst::LayerType::kLED; else if (info.stepCount == 1) { type = Vst::LayerType::kSwitch; style = Vst::AttributesStyle::kSwitchPushIncLoopedStyle; } return startLayer (type, static_cast (info.id), _function, style, ended); } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startEndLayer (Vst::ParameterInfo& info, FIDString _function) { return startLayer (info, _function, true); } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startEndCellOneLayer (Vst::ParameterInfo& info, FIDString _function) { if (!startCell ()) return false; startEndLayer (info, _function); endCell (); return true; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startEndCellOneLayerWithParamName (Vst::ParameterInfo& info, FIDString _function /*= 0*/) { if (!startCell ()) return false; startLayer (info, _function, false); startEndTitleDisplay (info); endLayer (); endCell (); return true; } //------------------------------------------------------------------------ bool XmlRepresentationHelper::startEndTitleDisplay (Vst::ParameterInfo& info) { String nameString (info.title); if (nameString.isEmpty ()) return false; if (!checkState (kInTitleDisplay)) return false; StringWriter writer (stream); String string; string.printf ("<%s>", TITLEDISPLAY_TAG); writer.write (string); writer.write (ENDLINE_A); // start of name scope if (!checkState (kInName)) { string.printf ("%s", END_TAG_STRING (TITLEDISPLAY_TAG)); writer.write (string); writer.write (ENDLINE_A); return false; } string.printf ("<%s>%s", NAME_TAG, nameString.text8 (), NAME_TAG); writer.write (string); writer.write (ENDLINE_A); if (nameString.length () > MEDIUM_TITLE_LIMIT) { nameString.assign (info.shortTitle); if (!nameString.isEmpty ()) { nameString.removeChars (); // remove space if (nameString.length () > MEDIUM_TITLE_LIMIT) nameString.remove (MEDIUM_TITLE_LIMIT); // Trimming the rest to get a short string string.printf ("<%s>%s", NAME_TAG, nameString.text8 (), NAME_TAG); writer.write (string); writer.write (ENDLINE_A); } } if (nameString.length () > SHORT_TITLE_LIMIT) { nameString.remove (SHORT_TITLE_LIMIT); // Trimming the rest to get a short string string.printf ("<%s>%s", NAME_TAG, nameString.text8 (), NAME_TAG); writer.write (string); writer.write (ENDLINE_A); } if (!checkState (kInTitleDisplay)) return false; // end of name scope string.printf ("%s", END_TAG_STRING (TITLEDISPLAY_TAG)); writer.write (string); writer.write (ENDLINE_A); if (!checkState (kInLayer)) return false; return true; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstsinglecomponenteffect.h0000644000000000000000000000012715124701711025646 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstsinglecomponenteffect.h0000644000175000001440000001401415124701711025632 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : plublic.sdk/source/vst/vstsinglecomponenteffect.h // Created by : Steinberg, 03/2008 // Description : Recombination class of Audio Effect and Edit Controller // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstaudioprocessor.h" // work around for the name clash of IComponent::setState and IEditController::setState #define setState setEditorState #define getState getEditorState #include "public.sdk/source/vst/vsteditcontroller.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #undef setState #undef getState #include "public.sdk/source/vst/utility/processcontextrequirements.h" #include "public.sdk/source/vst/vstbus.h" #include "public.sdk/source/vst/vstparameters.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Default implementation for a non-distributable Plug-in that combines processor and edit controller in one component. \ingroup vstClasses - [released: 3.0.2] This can be used as base class for a VST 3 effect implementation in case that the standard way of defining two separate components would cause too many implementation difficulties: - Cubase 4.2 is the first host that supports combined VST 3 Plug-ins - Use this class only after giving the standard way of defining two components serious considerations! */ class SingleComponentEffect : public EditControllerEx1, public IComponent, public IAudioProcessor, public IProcessContextRequirements { public: //------------------------------------------------------------------------ SingleComponentEffect (); ~SingleComponentEffect () override; //---from IPluginBase--------- tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE; tresult PLUGIN_API terminate () SMTG_OVERRIDE; //---from IComponent----------------------- tresult PLUGIN_API getControllerClassId (TUID /*classId*/) SMTG_OVERRIDE { return kNotImplemented; } tresult PLUGIN_API setIoMode (IoMode /*mode*/) SMTG_OVERRIDE { return kNotImplemented; } int32 PLUGIN_API getBusCount (MediaType type, BusDirection dir) SMTG_OVERRIDE; tresult PLUGIN_API getBusInfo (MediaType type, BusDirection dir, int32 index, BusInfo& bus /*out*/) SMTG_OVERRIDE; tresult PLUGIN_API getRoutingInfo (RoutingInfo& /*inInfo*/, RoutingInfo& /*outInfo*/ /*out*/) SMTG_OVERRIDE { return kNotImplemented; } tresult PLUGIN_API activateBus (MediaType type, BusDirection dir, int32 index, TBool state) SMTG_OVERRIDE; tresult PLUGIN_API setActive (TBool /*state*/) SMTG_OVERRIDE { return kResultOk; } tresult PLUGIN_API setState (IBStream* /*state*/) SMTG_OVERRIDE { return kNotImplemented; } tresult PLUGIN_API getState (IBStream* /*state*/) SMTG_OVERRIDE { return kNotImplemented; } // bus setup methods AudioBus* addAudioInput (const TChar* name, SpeakerArrangement arr, BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); AudioBus* addAudioOutput (const TChar* name, SpeakerArrangement arr, BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); EventBus* addEventInput (const TChar* name, int32 channels = 16, BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); EventBus* addEventOutput (const TChar* name, int32 channels = 16, BusType busType = kMain, int32 flags = BusInfo::kDefaultActive); tresult removeAudioBusses (); tresult removeEventBusses (); tresult removeAllBusses (); //---from IAudioProcessor ------------------- tresult PLUGIN_API setBusArrangements (SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts) SMTG_OVERRIDE; tresult PLUGIN_API getBusArrangement (BusDirection dir, int32 index, SpeakerArrangement& arr) SMTG_OVERRIDE; tresult PLUGIN_API canProcessSampleSize (int32 symbolicSampleSize) SMTG_OVERRIDE; uint32 PLUGIN_API getLatencySamples () SMTG_OVERRIDE { return 0; } tresult PLUGIN_API setupProcessing (ProcessSetup& setup) SMTG_OVERRIDE; tresult PLUGIN_API setProcessing (TBool /*state*/) SMTG_OVERRIDE { return kNotImplemented; } tresult PLUGIN_API process (ProcessData& /*data*/) SMTG_OVERRIDE { return kNotImplemented; } uint32 PLUGIN_API getTailSamples () SMTG_OVERRIDE { return kNoTail; } //---from IProcessContextRequirements ------------------- uint32 PLUGIN_API getProcessContextRequirements () SMTG_OVERRIDE { return processContextRequirements.flags; } //---Interface--------- OBJ_METHODS (SingleComponentEffect, EditControllerEx1) tresult PLUGIN_API queryInterface (const TUID iid, void** obj) SMTG_OVERRIDE; REFCOUNT_METHODS (EditControllerEx1) //------------------------------------------------------------------------ protected: BusList* getBusList (MediaType type, BusDirection dir); ProcessSetup processSetup; ProcessContextRequirements processContextRequirements; BusList audioInputs; BusList audioOutputs; BusList eventInputs; BusList eventOutputs; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstparameters.h0000644000000000000000000000012715124701711023430 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstparameters.h0000644000175000001440000002112015124701711023410 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstparameters.h // Created by : Steinberg, 03/2008 // Description : VST Parameter Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "base/source/fobject.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstunits.h" #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Description of a Parameter. \ingroup vstClasses */ class Parameter : public FObject { public: //------------------------------------------------------------------------ Parameter (); Parameter (const ParameterInfo&); Parameter (const TChar* title, ParamID tag, const TChar* units = nullptr, ParamValue defaultValueNormalized = 0., int32 stepCount = 0, int32 flags = ParameterInfo::kCanAutomate, UnitID unitID = kRootUnitId, const TChar* shortTitle = nullptr); ~Parameter () override; /** Returns its read only info. */ virtual const ParameterInfo& getInfo () const { return info; } /** Returns its writable info. */ virtual ParameterInfo& getInfo () { return info; } /** Sets its associated UnitId. */ virtual void setUnitID (UnitID id) { info.unitId = id; } /** Gets its associated UnitId. */ virtual UnitID getUnitID () { return info.unitId; } /** Gets its normalized value [0.0, 1.0]. */ ParamValue getNormalized () const { return valueNormalized; } /** Sets its normalized value [0.0, 1.0]. */ virtual bool setNormalized (ParamValue v); /** Converts a normalized value to a string. */ virtual void toString (ParamValue valueNormalized, String128 string) const; /** Converts a string to a normalized value. */ virtual bool fromString (const TChar* string, ParamValue& valueNormalized) const; /** Converts a normalized value to plain value (e.g. 0.5 to 10000.0Hz). */ virtual ParamValue toPlain (ParamValue valueNormalized) const; /** Converts a plain value to a normalized value (e.g. 10000 to 0.5). */ virtual ParamValue toNormalized (ParamValue plainValue) const; /** Gets the current precision (used for string representation of float). */ virtual int32 getPrecision () const { return precision; } /** Sets the precision for string representation of float value (for example 4.34 with 2 as * precision). */ virtual void setPrecision (int32 val) { precision = val; } OBJ_METHODS (Parameter, FObject) //------------------------------------------------------------------------ protected: ParameterInfo info {}; ParamValue valueNormalized {0.}; int32 precision {4}; }; //------------------------------------------------------------------------ /** Description of a RangeParameter. \ingroup vstClasses */ class RangeParameter : public Parameter { public: //------------------------------------------------------------------------ RangeParameter (const ParameterInfo& paramInfo, ParamValue _minPlain, ParamValue _maxPlain); RangeParameter (const TChar* title, ParamID tag, const TChar* units = nullptr, ParamValue minPlain = 0., ParamValue maxPlain = 1., ParamValue defaultValuePlain = 0., int32 stepCount = 0, int32 flags = ParameterInfo::kCanAutomate, UnitID unitID = kRootUnitId, const TChar* shortTitle = nullptr); /** Gets the minimum plain value, same as toPlain (0). */ virtual ParamValue getMin () const { return minPlain; } /** Sets the minimum plain value. */ virtual void setMin (ParamValue value) { minPlain = value; } /** Gets the maximum plain value, same as toPlain (1). */ virtual ParamValue getMax () const { return maxPlain; } /** Sets the maximum plain value. */ virtual void setMax (ParamValue value) { maxPlain = value; } /** Converts a normalized value to a string. */ void toString (ParamValue _valueNormalized, String128 string) const SMTG_OVERRIDE; /** Converts a string to a normalized value. */ bool fromString (const TChar* string, ParamValue& _valueNormalized) const SMTG_OVERRIDE; /** Converts a normalized value to plain value (e.g. 0.5 to 10000.0Hz). */ ParamValue toPlain (ParamValue _valueNormalized) const SMTG_OVERRIDE; /** Converts a plain value to a normalized value (e.g. 10000 to 0.5). */ ParamValue toNormalized (ParamValue plainValue) const SMTG_OVERRIDE; OBJ_METHODS (RangeParameter, Parameter) //------------------------------------------------------------------------ protected: RangeParameter (); ParamValue minPlain; ParamValue maxPlain; }; //------------------------------------------------------------------------ /** Description of a StringListParameter. \ingroup vstClasses */ class StringListParameter : public Parameter { public: //------------------------------------------------------------------------ StringListParameter (const ParameterInfo& paramInfo); StringListParameter (const TChar* title, ParamID tag, const TChar* units = nullptr, int32 flags = ParameterInfo::kCanAutomate | ParameterInfo::kIsList, UnitID unitID = kRootUnitId, const TChar* shortTitle= nullptr); ~StringListParameter () override; /** Appends a string and increases the stepCount. */ virtual void appendString (const String128 string); /** Replaces the string at index. Index must be between 0 and stepCount+1 */ virtual bool replaceString (int32 index, const String128 string); /** clear all added String */ virtual void clear (); /** Converts a normalized value to a string. */ void toString (ParamValue _valueNormalized, String128 string) const SMTG_OVERRIDE; /** Converts a string to a normalized value. */ bool fromString (const TChar* string, ParamValue& _valueNormalized) const SMTG_OVERRIDE; /** Converts a normalized value to plain value (e.g. 0.5 to 10000.0Hz). */ ParamValue toPlain (ParamValue _valueNormalized) const SMTG_OVERRIDE; /** Converts a plain value to a normalized value (e.g. 10000 to 0.5). */ ParamValue toNormalized (ParamValue plainValue) const SMTG_OVERRIDE; OBJ_METHODS (StringListParameter, Parameter) //------------------------------------------------------------------------ protected: using StringVector = std::vector; StringVector strings; }; //------------------------------------------------------------------------ /** Collection of parameters. \ingroup vstClasses */ class ParameterContainer { public: //------------------------------------------------------------------------ ParameterContainer (); ~ParameterContainer (); /** Init param array. */ void init (int32 initialSize = 10, int32 resizeDelta = 100); /** Creates and adds a new parameter from a ParameterInfo. */ Parameter* addParameter (const ParameterInfo& info); /** Creates and adds a new parameter with given properties. */ Parameter* addParameter (const TChar* title, const TChar* units = nullptr, int32 stepCount = 0, ParamValue defaultValueNormalized = 0., int32 flags = ParameterInfo::kCanAutomate, int32 tag = -1, UnitID unitID = kRootUnitId, const TChar* shortTitle = nullptr); /** Adds a given parameter. */ Parameter* addParameter (Parameter* p); /** Returns the count of parameters. */ int32 getParameterCount () const { return params ? static_cast (params->size ()) : 0; } /** Gets parameter by index. */ Parameter* getParameterByIndex (int32 index) const; /** Removes all parameters. */ void removeAll () { if (params) params->clear (); id2index.clear (); } /** Gets parameter by ID. */ Parameter* getParameter (ParamID tag) const; /** Remove a specific parameter by ID. */ bool removeParameter (ParamID tag); //------------------------------------------------------------------------ protected: using ParameterPtrVector = std::vector>; using IndexMap = std::map; ParameterPtrVector* params {nullptr}; IndexMap id2index; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/aaxwrapper0000644000000000000000000000013215124701711022450 xustar0030 mtime=1767080905.277413924 30 atime=1767080905.277062199 30 ctime=1767080905.277413924 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/0000755000175000001440000000000015124701711022515 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/resource0000644000000000000000000000013215124701711024277 xustar0030 mtime=1767080905.278210878 30 atime=1767080905.277413924 30 ctime=1767080905.278210878 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/0000755000175000001440000000000015124701711024344 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/PaxHeaders/aaxwrapperPages.xml0000644000000000000000000000013215124701711030230 xustar0030 mtime=1767080905.278210878 30 atime=1767080905.278210878 30 ctime=1767080905.278210878 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/aaxwrapperPages.xml0000644000175000001440000001020615124701711030217 0ustar00rncbcusers DemoGain 1 -> 1 by Avid Inc. PageTable 1 MasterBypass Gain Gain MasterBypass 1 1 1 1 1 1 1 1 1 1 1 1 1 MasterBypass Gain 1 1 1 1 1 1 1 1 1 1 1 1 1 MasterBypass Gain 1 1 1 1 1 1 1 1 1 1 1 1 1 Gain MasterBypass 1 1 1 1 1 1 1 1 1 1 1 1 1 MasterBypass Gain MasterBypass Gain Ga Gn Ma Byp MByp Mstr Byp RTAS: DemoGain, 1 in X 1 out MasterBypass qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/PaxHeaders/desktop.ini0000644000000000000000000000013215124701711026526 xustar0030 mtime=1767080905.278210878 30 atime=1767080905.278210878 30 ctime=1767080905.278210878 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/desktop.ini0000644000175000001440000000017115124701711026515 0ustar00rncbcusers[.ShellClassInfo] IconResource=PlugIn.ico,0 ;For compatibility with Windows XP IconFile=PlugIn.ico IconIndex=0 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/PaxHeaders/aaxwrapper.rc0000644000000000000000000000013215124701711027054 xustar0030 mtime=1767080905.278210878 30 atime=1767080905.278210878 30 ctime=1767080905.278210878 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/aaxwrapper.rc0000644000175000001440000000422615124701711027050 0ustar00rncbcusers// Microsoft Visual C++ generated resource script. // #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "DemoMIDIResource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,1 PRODUCTVERSION 7,0,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "AAXWrapper Plug-In" VALUE "FileVersion", "1.0.0.0" VALUE "InternalName", "AAXWrapper.aaxplugin" VALUE "LegalCopyright", "(c) Steinberg Media Technologies 2020" VALUE "OriginalFilename", "AAXWrapper.aaxplugin" VALUE "ProductName", "AAX Wrapper" VALUE "ProductVersion", "0.1" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/PaxHeaders/PreBuildEvent.bat0000644000000000000000000000013215124701711027554 xustar0030 mtime=1767080905.278210878 30 atime=1767080905.278210878 30 ctime=1767080905.278210878 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/PreBuildEvent.bat0000644000175000001440000000154315124701711027547 0ustar00rncbcusersset OutDir=%1 if not exist %OutDir%\..\..\Contents mkdir %OutDir%\..\..\Contents if errorlevel 1 goto err if not exist %OutDir%\..\..\Contents\Resources mkdir %OutDir%\..\..\Contents\Resources if errorlevel 1 goto err echo Copy "aaxwrapperPages.xml" copy /Y ..\resource\aaxwrapperPages.xml %OutDir%\..\..\Contents\Resources\ > NUL if errorlevel 1 goto err attrib -r %OutDir%\..\.. if exist %OutDir%\..\..\PlugIn.ico goto PlugIn_ico_exists copy /Y ..\resource\PlugIn.ico %OutDir%\..\..\ > NUL if errorlevel 1 goto err attrib +h +r +s %OutDir%\..\..\PlugIn.ico if errorlevel 1 goto err :PlugIn_ico_exists if exist %OutDir%\..\..\desktop.ini goto desktop_ini_exists copy /Y ..\resource\desktop.ini %OutDir%\..\..\ > NUL if errorlevel 1 goto err attrib +h +r +s %OutDir%\..\..\desktop.ini if errorlevel 1 goto err :desktop_ini_exists attrib +r %OutDir%\..\.. :err qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/PaxHeaders/PlugIn.ico0000644000000000000000000000013215124701711026246 xustar0030 mtime=1767080905.278210878 30 atime=1767080905.277413924 30 ctime=1767080905.278210878 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/resource/PlugIn.ico0000644000175000001440000130535615124701711026253 0ustar00rncbcusers 00h¶ è( 00¨. ¨Öh~" ( æ'€€ (H00 ¨%6P  ¨Þu h††(0`€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿwwwwwwwwwwpwwwwwwwwwwpUUUUUUUUUUPpwwwwpwgwwwpwwpwwwwwwwwwwwwwwpˆˆˆˆ‡ˆˆˆˆpˆÿøˆwˆwˆø‡‡wwww‡wppw€ˆpUˆuUXWˆ‡…UQxˆuUXx‡UˆP€xp€wpwwwwpwgwwwpwwpwwwwwwwwwpwwwwwwˆˆˆˆ‡wˆˆ‡pˆÿøˆwˆ‡ˆˆ‡W‡wwwwwˆpupPpuUUUUUUUUUpwwwwwwwwwwpwwwwwwwwwwpwwwwwwwwwwpˆˆˆˆˆˆˆˆˆˆ€wwwwwwwpÿÿÿÿÿÿüÿø?ÿÿÿÿà?ÿÿøà?ÿÿàà?ÿÿàà?ÿÿàà?ÿÿàà?ÿÿàÀÿÿàÀÀÀÀÀÀÀÀÀÀÀÀÀÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀÿÿÀÀÀÀÀÀÀÀÀÀÀÀ‡ÀÀÿÿÀà?ÿÿÀà?ÿÿàà?ÿÿàà?ÿÿàà?ÿÿàà?ÿÿàð?ÿÿà( @€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿwwwwwwpUUUUUUwwpwwwwwwpwwˆˆ‡ˆˆUwwwxˆ€…XuˆpˆpxuPx€ˆP‡Xqwwpwwwwpwwxwwwˆˆ‡ˆˆwwwxwwwwwwwwwwwwwpwwwwwwwpWwwwwˆ‡pùÿÿÿÀ?ÿÀÿÀÿÀÿÀ?ÿÀÀ€€€?ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€?ÿ€€€ÀÀ?ÿÀÿÀÿÀÿÀÿÀÿ( €€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿwwwwppˆ€WuWuWqWwwpˆ€wwwwwwpàÿ,>€àÿ€€(0`vzz:>>Z:†Rrº¶ºZZZ–šš*vBb²‚¢*..JNNN">†ŠŠfff¾¢²2.ÎÒÒVBRfZfŽ‚Šv"V &&&rfrBFF   ~‚‚ÀÂÂ¥¦¦NRZB2>®rš²®²~V(&566n^jnrrrRf?*F:F5.6SVVâââF>F$Ž’’QVZ9::‚z‚ξÊ^bbÊ‚²ZNVmnn.&.Ä®¾%"&»¾¾fZb¢¢¦ÙÚÚ  ­®®zrzV>6"nfnGJN_bfHJJ…††ÊÊÊF2BfVb{~~¢¢¢¨Žž122QRRÖÂΪª®86:–frbjvvzN>Jž’š:&6 lFX^bœž¢Î¢¾ž‚’***nnrDFJ‡‚†ÄÆÆ§ªª¯²²tvvN2426”––ZVZ*"&ÛÞÞ¸’ªwz~BBB"""®b’Ž2nf"NÆ’²rN~VB.>†"fêêêÒ’¾ZFV‚RnVJVZNZ²žª’Rz‚BjÊ¢º*&~~‚˲ƒ†ŠNRV¶ººŒŽŽÖÖÖNFN-*.^^^fjjf^frjræææ®¦ª†VI.?BFgjnRJR@6>¢jzbnŠ~†/".JBJ6*6ކŠB>BUZ^2"1.2KNRŽŠŽ(&* ÆÂÆ.*zRjN:J(>:>¿¾Â;&Ö¢Â$"µ²¶¾’®n"R¼º¾2*2^>""ššžzBfdfj¢¶jZjz"Z‚‚†²ržprv’–""ÚÚÞqbn^V^,"*^N^vjvŠZ2 n^n:.6VVZ‚rzfbffVfJFJ^^bRNR.&*zzz>>>^:ššš....ÒÒÒjZf"vfrFFF ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌñÌÌÌÌ@{{àààà{)))))) ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿYáUúÕ++ÝüâÝ+++ÕúúUUúÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºTTTTTTTTTTTTTTTTTTTT¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûffffffffffffffffffff‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQfÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌQQQQQQQQQQQQQQQQQQQQ“B$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\ººººººººººººººººººººÆ€·7ii´77—ÿÿ·ÿÿÿpÿÿÿÿÿÿHÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃï§N——·NNpÿÿ**·>Ù¨1[ÿÿÿÿ êrrrrrrrrrrx¹OOŸŸÓO;ÿÿ´,!R××RW]ÿÿÿÌÌÌÌÌÌÌÌgþrgÌÌÌÌÌÌÌÌÌÑ8XuCC˜uI_*óE×XEu˜˜"sCÿÿÿÿÌgþårþgÌ CG‰£2|šùRòS#™ò_Ü2|"òÿÿÿÿÿVÿÿÿÿÿñþ66­6þÌÿÿÿÿÿÿ ýùÚ5™–!÷W;ÿÿÿ™Iõÿÿÿÿÿÿÿÿÿÿÿÿgåbéø6gÿÿÿÿÿÿ [~ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿÿÿÿñå4ZöÀøåÌÿÿÿÿÿÿ žÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿg64älZÀþÿÿÿÿÿ žÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿÿÿþøöĦKAµ4åñÿÿÿÿÿ žÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿÿÿñå4L }ÐÄÀ6Ìÿÿÿÿÿžžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@Ì6 .^…h¦ägžFÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡g䦄k ƒLÒþžFÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÎgøLwh¥ã†^Ä4åžFÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqBþøLƒ&Å<„‘.4åñ¯@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqþÒL•ˆa«(Í.4»ÌF@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿq¹þÒL’ÅŠÉ.4å̯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ œþøLwh¥ã‚•Ä4åÌ@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ œg䦨kÈL4åÌ@Îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ¶g .•Öh¦äøþ@ÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿѶÌ64ÄŒ}ƒ. g@ÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑxÌÌÌÌ»Òä.¦KAL46gÌÌÌÌÌBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯xÌÌÌÌþÀµ¾:- øåÌÌÌÌÌÌÎBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@`ÌÌÌÌg6ÒÀä 46 ÌÌÌÌÌÌÎBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ÁÃÌÌÌÌþÒe¬4» ÌÌÌÌÌÌÎx—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@Á þ»Ò6þ B¶··$——··Nÿÿ8ÿÿÿNèÿÿÿÿÿÿq³ÞÃÃÃÃÃÃ\ì\\ÃÃÃÃÃÃÃù¶§N——·NNpÿÿ* 81,v>18ÿÿÿÿ̰YººººË`OOŸŸÓO;ÿÿ´P]Ó”WvÓÿÿÿÌ›ÛYYYYYYYY¢¸¸¸¸¸¸¸¸¸¸ïíXuCC˜uI_n÷EWÏjtu_tEÿÿÿÌ©)ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆªïCG‰£2|šùòvSt#yCššC!ÿÿÿÿ °±½½½½½½½½½½½½½½½½½½½/zùÚjÏÚ5™P–>ÿÿÿIsIRÿÿÿÿÿ 3ª±±±±±±±±±±±±±±±±±±±›³~ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/d%%%%%%%%%%%%%%%%%%%Ž?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªdddddddddddddddddddD?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªD===================M?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®J?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà­9Jâââââââââââââââââ­?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?¤'''''''¼¼ÊÇÇÇÇÇÇÇÇÂ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿY›››ª000›ß››)ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿø?ÿÿÿÿà?ÿÿøà?ÿÿàà?ÿÿàà?ÿÿàà?ÿÿàà?ÿÿàÀÿÿàÀÀÀÀÀÀÀÀÀÀÀÀÀÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀÿÿÀÀÀÀÀÀÀÀÀÀÀÀ‡ÀÀÿÿÀà?ÿÿÀà?ÿÿàà?ÿÿàà?ÿÿàà?ÿÿàà?ÿÿàð?ÿÿà( @zrz6::¦ªªfB†Bn""NRVŠŽ’.ÆÆÆR>NZ^^Æ’²¾‚ª‚‚‚fjnžžž  ºººFJN*"&R">RZ^>*šrŽ’–– bffÒÖÖ¦bŽ>BF"nB^’†Žr2Z‚z‚®¾NFN^bfjnn  ¾¢²ÂÂÆ2&2>.:¾®º¶FŽ.*bVb:&ÎÒÒNBN†ŠŠVJVbZbJ2–šž:>B‚RrVVV¢’š^bbІТ¦¦¾ÂÂVJR.".‚~‚΢¾VBN®²²Ž’’2"rfnª¢ªJNNVZbF.’–šÞÞÞNBJ&znv26:.&.‚r~®®®–j&&ÊÎÎV^b‚††šž¢ º¾¾:*f"NZZ^Š~Š^fjÚÚÚBFFŠŠŽnbjjnrÆ¢º.*ÞÎÚF>F’RzNVZ†~†VBRJNR~rz6:>nNŠ2jÖ²Ê>*:¶bšF"6B6BÒ¾Ê ÊÊʦr’>2>–Š’ŽBrÆ‚®VFRŠRv–’–Ê’¶†‚†Â¢¶ÒÒÖÒ¢ÂvfrFFJ¦ª®’Ž’š–š¢ž¢ffj..RRV2jjn "VZ^B*–––ÖÖÖ&†z‚bbfnnn ®º>&RBNfZbN2ššž>>BbbbކŠÂÂÂ2".†~‚ 6"NNNJ.*2&.Z^b¾¾¾RVZZBRÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ0:5rrQ[&Žm_™‘~ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊʽ 1ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ4„„„„„„„„„„„„„uÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ4444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ££?ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÄÄyeUyÊ}UÊÊÊÊÊÊ\I•••••••p¨D¨Ê?++ÊÊÊÊÊÊMMMMhYY¦MMMMMi7‹/iÁ`‹dGÊÊÊÊÊÊÊÊÊ~h¬LL"¿ÊÊÊÊOg=AfN›FÊÊÊÊÊÊÊÊÊ¿¬Z2j$h~ÊÊÊÊlÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ~S­À)% ¤¦ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊM ¢V'vZSÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊʦ$³.!¶P¬MÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊhZV”K’6¤¿ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊS ¶!˜Œ$¿ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊS @ƒ3a$ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊSÃ<‚….$ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊhZVxt'6¤¿ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊʦ$6t MÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊMMM ©kˆ6ZSMMMMÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊMMMMS$P² ¤¦MMMM#ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊMMMM¦ $B†$"MMMM]UyÊUyÊÊÊÊÊM\\\\Cz|L¹Ä\\\\l,Ÿ>pÊÊÊÊÊM ‰HREEEEEE^q o d º— ‹ÊÊÊÊÊÊc·=Wq°9ÊÊÊÊÊÊbIIIIIIIIIIIIIb?ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊIIÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ8888888888888ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊʇ55555555555558ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ*(((({{{{{{(((;ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊw(((((••“žžžžœÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊùÿÿÿÀ?ÿÀÿÀÿÀÿÀ?ÿÀÀ€€€?ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€?ÿ€€€ÀÀ?ÿÀÿÀÿÀÿÀÿÀÿ( Š‚ŠzZ:*6B*"VJVŠf~^^^RBN2"  rbn666:.þþþ*&.²²²vZjªj’ njnV*F^>&"*"fff:&F2BZB‚jzšn>*:&^RZžfŠ ºººvjr’ŽŽ‚^Z:&"f^fNFN6" :6:2"."""2"¶¶¶ŽZzÂj¢ rjnnJ""."jfj>&B6BfJB*:fRb¦fŽQQQOOOOOQQQQQQQQQ$$$$$$=QQQQQQQ'GQQQQQQQ N/E"""""QQ 5->1A. QQ'K%0QQQQQQQQ3MQQQQQQQQ-#F(P:-QQQQQQQQ #FC:-QQQQQQQQ- 2,;QQQQQQQQ'*BQQQQQQQ'6&/"""""Q' !!! <1A. QQQQQQQQ++++++LQQQQQQQQ9888QQQQQQQàÿ,>€àÿ€€(  %(('''''&&&&&%%%%%$$$$$$#####"""""!!!!!                                "(.7@GOW]]\\[[ZZZYYXXWWVVUUUTSSSRRQQPPPONNNMMLLKKKJIIIHGGGFFEEDDDCBBBAA@@???>===<<;;::9988877665544332211100//..--,,+++**))((''&&&%$$$##""!!!             )3<CKT[ahow€†‡‡†…„„ƒƒ‚€€}}||{zyyxxwvuutssrqqponnmmlkjjihhgffedccbba`__^]]\[[ZYXXWVVUTTSRRQPPONMMLKKJIIHGGFEDDCBBA@@?>>=<<;:998776554322110/..-,,+**)(''&&%$##"!!         !.8AIRZcls|ƒˆ–¤«²¶¶µ³³²±°®®­¬«ª©¨§¦¦¤£¢¢¡ žžœ›™™˜—–•”“’‘‘ŽŒŠ‰‰ˆ‡…„„ƒ‚€~}|{zyxxvuttsrpoonmkkjihgfedcca`__^\[ZZYWVVUTSRQPONMLKJJIGFEEDBAA@?==<;:98765532100.-,,+*(('&%$#"!       .7AJR^hpy‰’𠍝µ»ÂÊÐÖÙÙØ×ÖÔÓÒÑÐÎÍÌËÊÉÈÆÅÄÃÂÁ¿¾½¼»¹¸·¶µ³²±°¯®­«ª©¨§¦¤£¢¡ žœ›š˜—–•”“‘ŽŒŠ‰ˆ‡†„ƒ‚€~}|{zywvutsqponmlkihgfecba`_^\[ZYXVUTSRQONMLKJHGFEDCA@?>=;:987643210.-,+*)(&%$#"      *5?HP\hpyƒ–ž¥­´¿ÆÎÖÝÜÛÚØ×ÖÕÔÓÑÐÏÎÍËÊÉÈÇÆÄÃÂÁÀ¾½¼»º¸·¶µ´³±°¯®­«ª©¨§¥¤£¢¡ ž¨´³²±°¯®­­¬«ª©¨§¦¥¥¤£¢¡ Ÿžœ›š™˜—¹§Œponlkjihgedcba_^]\[ZXWVUTRQPONLKJIHGEDCBA?>=<;987654210/.,+*)(&%$#"!    &2<ENXenv€–ž§®ºÂËÔÜÜÛÚØ×ÖÕÞÞÝÜÛÚÙØ×äããâááààßÞêîîíííììììëë õ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÉonlkjihgedcba_^]\[ZXWVUTRQPONLKJIHGEDCBA?>=<;987654210/.,+*)(&%$#"!     D JR[dk–¦¬²¶»ÀÛ åçèëîð õ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ('ÿ4*3ÿ4*3ÿ3*3ÿ3)2ÿ3)2ÿ2(2ÿ2(1ÿ1'1ÿ1'0ÿ1'0ÿ0&0ÿ0&/ÿ/%/ÿ/%.ÿ/$.ÿ.$-ÿ.#-ÿ-#-ÿ-",ÿ,",ÿ,!+ÿ8-7ÿE9DÿE9CÿI>HÿJ?IÿJ>HÿI=GÿHÿ""ÿ  ÿ ÿ ÿ®nlkjihgedcba_^]\[ZXWVUTRQPONLKJIHGEDCBA?>=<;987654210/.,+*)(&%$#"!      Ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ0&/ÿ0&0ÿ1'0ÿ1'1ÿ1(1ÿ2(2ÿ2)2ÿ?5?ÿ]T\ÿ^U]ÿ_U^ÿ_V_ÿ`W_ÿaX`ÿbYaÿcZbÿjaiÿ‡~†ÿ’‹‘ÿ“Œ’ÿ”“ÿ•Ž”ÿ–•ÿ—–ÿ˜‘—ÿ˜‘—ÿ˜‘—ÿž—ÿ·¯µÿÇÁÅÿÆÀÄÿžÄÿĽÃÿüÂÿ»ÀÿÀ¹¿ÿ¿¸¾ÿ½¶¼ÿ¼µ»ÿº³¹ÿ¹±·ÿ·¯¶ÿ¶®´ÿ´¬²ÿ²ª±ÿ±©¯ÿ¯§®ÿ®¥¬ÿ¬£ªÿª¡¨ÿ©Ÿ§ÿ§¥ÿ¥›£ÿ¤š¢ÿ¢˜ ÿ –žÿž”œÿ’›ÿ›™ÿ™Ž—ÿ˜–ÿ–‹”ÿ”‰’ÿ“‡ÿ‘…Žÿƒÿ‹ÿŒ‰ÿŠ}ˆÿˆ{†ÿ‡z„ÿ…x‚ÿƒvÿtÿ€r}ÿ~p{ÿ|nzÿ{lxÿyjvÿwhtÿugsÿteqÿ:0:ÿÿ  ÿ ÿ Ûnlkjihgedcba_^]\[ZXWVUTRQPONLKJIHGEDCBA?>=<;987654210/.,+*)(&%$#"!    ÿ ÿ ÿÿ$#ÿ$$ÿ%$ÿ1%0ÿB6AÿC7BÿD8CÿE9DÿE:DÿG;Eÿ[OZÿh\fÿi]gÿk^iÿl`jÿmblÿocmÿ|pzÿ”ˆ’ÿ–Š”ÿ—Œ•ÿ™Ž—ÿ›™ÿ’›ÿž”ÿ –žÿ¢˜ ÿ¤š¢ÿ¦œ¤ÿ§ž¥ÿ© §ÿ«¢©ÿ­£«ÿ®¦­ÿ°§®ÿ²ª°ÿ´«²ÿµ­´ÿ·¯¶ÿ¹±·ÿ»³¹ÿ¼µ»ÿ¾·½ÿÀ¹¿ÿÁ»ÀÿüÂÿžÄÿÆÀÅÿÈÂÇÿÉÃÈÿËÅÉÿÌÆËÿÌÇËÿÍÇËÿÌÇËÿËÅÊÿÊÄÉÿÈÂÇÿÇÁÆÿÅ¿ÄÿĽÂÿ»ÁÿÀ¹¿ÿ¿¸½ÿ½¶¼ÿ»´ºÿ¹²¸ÿ¸°¶ÿ¶®µÿ´¬²ÿ²ª±ÿ±¨¯ÿ¯¦­ÿ­¤¬ÿ«¢ªÿª ¨ÿ¨ž¦ÿ¦¤ÿ¤›£ÿ£™¡ÿ¡—ŸÿŸ•ÿ“›ÿœ‘™ÿš˜ÿ˜–ÿ–‹”ÿ•‰“ÿ“‡‘ÿ‘…ÿƒÿ‹ÿŒŠÿŠ}ˆÿˆ{†ÿ†y„ÿ…w‚ÿƒu€ÿsÿq}ÿ~o{ÿ|nyÿzkwÿxjuÿwhtÿufrÿsdpÿqbnÿ:0:ÿÿÿ ÿ Úlkjihfedcb`_^]\ZYXWVUSRQPONMKJIHGFDCBA@>=<;:87654310/.-,+*('&%$#!      ÿ ÿÿTFRÿp`mÿqaoÿsdpÿufrÿwhtÿyjvÿzlxÿ|mzÿ~p|ÿ€q}ÿ‚sÿƒvÿ…xƒÿ‡z…ÿ‰{‡ÿ‹~ˆÿ€ŠÿŽ‚Œÿ„Žÿ’†ÿ”ˆ‘ÿ–‰“ÿ—Œ•ÿ™Ž—ÿ›™ÿ’›ÿŸ”ÿ –Ÿÿ¢˜ ÿ¤š¢ÿ¦œ¤ÿ¨ž¦ÿ© ¨ÿ«¢©ÿ­¤¬ÿ¯¦­ÿ±¨¯ÿ²ª±ÿ´¬³ÿ¶®´ÿ¸°¶ÿ¹²¸ÿ»´ºÿ½¶¼ÿ¿¸¾ÿÀºÀÿüÁÿľÃÿÆÀÅÿÈÂÇÿÉÃÈÿËÆÊÿÍÇÌÿÎÈÍÿÎÉÍÿÍÈÌÿÌÆËÿÊÄÉÿÉÂÇÿÇÀÆÿžÄÿýÂÿÁºÀÿÀ¹¾ÿ¾¶½ÿ¼µ»ÿº²¹ÿ¹±·ÿ·¯µÿµ­³ÿ³«²ÿ±©°ÿ¯§®ÿ­¤¬ÿ¬£ªÿª ¨ÿ¨Ÿ¦ÿ¦œ¤ÿ¥›£ÿ£™¡ÿ¡—ŸÿŸ•žÿ“œÿ›‘šÿšŽ˜ÿ˜Œ–ÿ–Š”ÿ”ˆ’ÿ“†ÿ‘„ÿ‚ÿ€‹ÿ‹~‰ÿŠ|‡ÿ„vÿ~p|ÿ}nzÿ|myÿzkxÿyjvÿxhuÿvgsÿuerÿtdqÿrboÿqanÿo_mÿn^kÿm]jÿ:09ÿÿÿ ÿ Ö\[ZYXWVUTSRQPONMLLJIHGFEDCBA@?>=<<:9877543220/.-,,*)(''%$#""      / ÿ ÿM>KÿhWeÿl\hÿn^jÿp`lÿqbnÿsdpÿufrÿwhtÿyjvÿ{lwÿ|nyÿ~p{ÿ€r}ÿ‚tÿ„vÿ†xƒÿ‡z…ÿ‰|†ÿ‹‰ÿ€ŠÿƒŒÿ‘„Žÿ’‡ÿ”‰’ÿ–‹”ÿ˜–ÿš˜ÿœ‘™ÿž“›ÿŸ•ÿ¡—Ÿÿ£™¡ÿ¥›¢ÿ¦¥ÿ¨Ÿ¦ÿª¡¨ÿ£™¡ÿœ™ÿ’›ÿž“œÿŸ•ÿ¡–Ÿÿ¢˜ ÿ£™¡ÿ¥›£ÿ¡–žÿ‘…Žÿ’†ÿ“‡ÿ“ˆ‘ÿ”ˆ‘ÿ•‰’ÿ•Š“ÿ–Š“ÿ—‹”ÿ—‹”ÿ–‹”ÿ}ozÿ}nzÿ}nyÿ|nyÿ|myÿ{mxÿ{lxÿzlwÿzkwÿykvÿyjvÿyjvÿxiuÿxiuÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿdSaÿ9.8ÿÿÿ ÿ ÓNMLKKJIHGFFECCBAA@>>=<<:9987655432100/.-,++*(('&&%##"!!     ? ÿÿaP^ÿaP^ÿfUbÿjZfÿl\hÿm^jÿo`lÿn^kÿo_kÿp`mÿrbnÿscpÿteqÿvgsÿp`mÿqamÿrbnÿscoÿsdpÿteqÿufrÿn^kÿl\iÿm]iÿm]jÿm]jÿn^kÿn_kÿo_lÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿaP^ÿ8.7ÿÿÿ ÿÏ@?>>=<<;:998776554322110/..-,,+**)(''&&%$##"!!     ? ÿÿ^MZÿ^MZÿ^MZÿ^N[ÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ^MZÿ8.7ÿÿÿ ÿË2100//..--,,+**)))(''&&%%$$#""!!          _ ÿ"!ÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ\JXÿ8.7ÿÿÿ ÿÈ##"""!!                   ÿ1#/ÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿYFUÿ8.7ÿ  ÿ ÿ ÿÄ                       ÿ/!-ÿVDSÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿUCRÿ7.7ÿ  ÿ ÿ ÿÁ ÿ. ,ÿTAPÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@OÿS@Oÿ7-6ÿ!!ÿ ÿ ÿ ¿ ¿ ÿ?.;ÿR>NÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=LÿQ=Lÿ7-6ÿ!!ÿ ÿ ÿ ¿ ¿ ÿ<,9ÿOÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿA.>ÿ6-6ÿ$$ÿ ÿÿ¿?ÿ ÿD0@ÿA,=ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ@,<ÿ7-6ÿ$$ÿ ÿÿ¿?ÿ ÿB.?ÿ>*:ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ=*9ÿ7-6ÿ%%ÿ ÿÿ¿?ÿ ÿB->ÿ<(8ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ;(7ÿ7.6ÿ%%ÿ ÿÿ¿oÿÿ@,=ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ9&5ÿ7.6ÿ&&ÿ ÿÿ¿ÿÿ@+;ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7$3ÿ7.6ÿ&&ÿ ÿÿ¿ÿÿ>)9ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ4"0ÿ6.5ÿ& &ÿ ÿÿ¿ÿÿ;(7ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ2 .ÿ6.5ÿ'!'ÿ ÿÿ¿¿ÿ)&ÿ;'7ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ1-ÿ7.6ÿ'!'ÿ ÿÿ¿¿ÿ(&ÿ9%5ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ/+ÿ7.6ÿ("(ÿÿÿ¿¿ÿ)&ÿ7$3ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ.*ÿ7/6ÿ("(ÿÿÿ¿Ïÿ,(ÿ5"1ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ,(ÿ7/6ÿ)#)ÿÿÿ¿ÿÿ2.ÿ3 /ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ*&ÿ7/6ÿ)#)ÿÿÿ¿ÿÿ2 .ÿ0-ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ'$ÿ7/6ÿ)$)ÿÿÿ¿ÿÿ2 /ÿ.*ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ706ÿ*%*ÿÿÿ¿ÿÿ4!0ÿ,)ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ%!ÿ707ÿ*%*ÿÿÿ¿?ÿ ÿ4"1ÿ*'ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ# ÿ707ÿ*%*ÿÿÿÿCHKÿ@FIÿ>BFÿ8ï379?ÿÿ5$2ÿ$!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ817ÿ,',ÿÿÿÿW^bÿV]aÿTZ_ÿNTYÿGLOÿ<@Cÿ47:?oÿ ÿ5$2ÿ"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ827ÿ+'+ÿÿÿÿW^bÿV]aÿU[`ÿQW\ÿJOSÿ?CFÿ59;?ÿÿ5%2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ827ÿ,(,ÿÿÿÿW]bÿU\aÿT[_ÿRX]ÿKPTÿ?DGÿ59;?ÿÿ5%2ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ828ÿ-)-ÿÿÿÿU[`ÿTZ^ÿSY]ÿQV[ÿJOTÿ>BFÿ47:?ÿÿ5&2ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ938ÿ-)-ÿÿÿÿRY]ÿQX\ÿPVZÿNUXÿHMQÿ8=@:>A;@CBE>CF>CF>CF>CF>CF>BF>BE>BE=BE=BEB9>A9>A9>@9=@9=@8=@8<@87;>7;=69<48:o*.059=;@C9=@59<8:<XZ\O]_aace¿bdfÿcegÿbdfÿ_acÏ[]_ŸWYZ_SUWÿ ÿ4%2ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ938ÿ-)-ÿÿÿÿPVZÿOUYÿNTXÿLRVÿFLNÿ;@Bÿ=ADÿBGKÿDIMÿFKOÿGMPÿINRÿJPTÿLRVÿNSXÿNTXÿNTXÿNTXÿNTXÿNTXÿNTXÿMSWÿMSWÿMRWÿMRWÿLQVÿLQVÿLQUÿKQUÿKPTÿJPTÿJPSÿJOSÿIOSÿINRÿHNRÿHNQÿHMQÿGMQÿGMQÿGLPÿFLPÿFKOÿFKNÿEJMÿCHKÿ@DGÿ:>Aÿ ÿÿÿÿÿÿÿÿÿÿ/24ÿ>CFÿFLOÿBHKÿUVXY[\`bdßgikÿnpsÿsvxÿvy{ÿvy{ÿuxzÿrtvÿmoqÿegiÿ]_aÿWY[ŸRSU?¿ÿ&$ÿ5&2ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ949ÿ.*.ÿÿÿÿNSWÿMRVÿLQUÿJOSÿDILÿ9=@ÿCHLÿLQUÿNTXÿPUZÿQW\ÿSZ^ÿU\aÿW^cÿY`eÿY`eÿY`eÿY`eÿY`eÿY`eÿY`eÿX_dÿX_cÿX^cÿW^cÿV]bÿV]bÿV\aÿV\aÿV\`ÿU[`ÿU[`ÿU[_ÿTZ_ÿTY^ÿSY^ÿSY]ÿSX]ÿRX\ÿRW\ÿQW\ÿPW[ÿPVZÿPVYÿOTXÿLRUÿGLOÿ>CFÿ ÿÿÿÿÿÿÿÿÿÿ258ÿEJNÿOTYÿJOSÿAFIÿ9<>ÿ:<=ÿ9;ÿEJNÿOTYÿQW[ÿRY]ÿT[_ÿV]bÿX_dÿZafÿ\chÿ]diÿ]diÿ]diÿ]diÿ]diÿ\chÿ\chÿ\bgÿ[bgÿ[bgÿZafÿZ`eÿY`eÿY`dÿY_dÿX_dÿX_cÿX^cÿW^bÿW]bÿV]aÿV\aÿU\`ÿU[`ÿU[`ÿT[_ÿTZ_ÿTZ^ÿSY]ÿSY]ÿPVZÿKPTÿAEHÿÿÿÿÿÿÿÿÿÿÿ37:ÿINSÿTZ^ÿNTXÿCILÿ9<>ÿ;=>ÿ:<=ß8:;RTU/Y\]Ïgikÿqtvÿy|~ÿ~„ÿ„‡ÿƒ†‰ÿƒ†‰ÿƒ†‰ÿ‚…ˆÿ…‡ÿ„‡ÿ€ƒ†ÿ~„ÿz~€ÿwy|ÿqsvÿilnÿaceÿUWYßNPRO¿ÿ)'ÿ3%0ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ948ÿ.+.ÿÿÿÿINQÿHMPÿGLOÿEJMÿ@DGÿ69;ÿDILÿMSWÿOUYÿQW[ÿSY]ÿU[_ÿW]aÿY_dÿ[afÿ[bfÿ[bfÿ[bfÿ[bfÿ[bfÿ[afÿZaeÿZ`dÿY`dÿY`dÿY_cÿX_cÿX_bÿW^bÿW]bÿW]aÿV]`ÿV\`ÿU\`ÿU[_ÿU[_ÿT[^ÿTZ^ÿSZ]ÿSY]ÿSY]ÿRX\ÿRX\ÿRX\ÿRX\ÿQWZÿKPTÿAEHÿÿÿÿÿÿÿÿÿÿÿ47:ÿJPTÿU[_ÿPUYÿDILÿ9;=ÿ=>@ÿ>?Aÿ:;<Ï89:PRTZ\^ÿhkmÿtvxÿz}€ÿ~„ÿ€ƒ†ÿ€ƒ†ÿ€ƒ†ÿ‚…ÿ‚„ÿ~„ÿ~ƒÿ}€ƒÿ|‚ÿ|‚ÿ{~€ÿz}ÿwz|ÿsvxÿmprÿcfhÿY[\ÿOQS¿JLM/¿ÿ+)ÿ2%/ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ959ÿ.+.ÿÿÿÿGKNÿFJMÿEILÿCHJÿ>BDÿ489ÿAFIÿJPSÿLRUÿNTWÿOVYÿQX[ÿSZ^ÿU\`ÿW^bÿW^bÿW^bÿW^bÿW^bÿW^bÿW^bÿV]aÿV]`ÿU\`ÿU\`ÿU[_ÿT[_ÿT[_ÿTZ^ÿSZ^ÿSY]ÿRY]ÿRX\ÿRX\ÿQX[ÿQW[ÿPW[ÿPWZÿPVZÿOVYÿOUYÿNUXÿNTXÿNTXÿNTXÿMTWÿHNRÿ>CFÿ ÿÿÿÿÿÿÿÿÿÿ268ÿIORÿSZ^ÿNTWÿBGKÿ7:<ÿ>?AÿBCDÿ@ABÿ;<>Ï9:;HJK?PRTß]^`ÿikmÿsuxÿz|~ÿ|ÿ}‚ÿ}‚ÿ}ÿ|~ÿ|~€ÿ{}€ÿ{}ÿz|ÿz|~ÿy{}ÿxz}ÿxz|ÿwy|ÿwy{ÿuwzÿsuwÿmoqÿegjÿ]^`ÿRSUÿKLNEFHßÿ4'1ÿ2%/ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:59ÿ/,/ÿÿÿÿDHKÿCGJÿCFIÿAEGÿ@AÿDFGÿEGHÿCEFÿ>@Aï:;<79:@BCOHJK¿QSUÿ]_aÿgjlÿpsuÿvy{ÿx{}ÿx{}ÿx{}ÿx{}ÿx{}ÿwz|ÿwz|ÿvy{ÿvy{ÿuxzÿuxzÿtwyÿsvxÿsvxÿruwÿruwÿqtvÿqtvÿortÿknpÿehjÿ^`bÿTVXÿJLMßDFG/ÿÿ5(3ÿ1$.ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:69ÿ0-0ÿÿÿÿBFHÿAEGÿ@DFÿ>CDÿ:=?ÿ035ÿBDÿ468ÿ>@AÿGHIÿJKLÿJLMÿIJKÿDEGÿ@ABÿ@ACÏBDE¿DEGÿKMNÿUVXÿ^_aÿfhiÿlnpÿqsvÿtvxÿtvxÿtvxÿtvxÿtvxÿtvxÿsuxÿsuwÿrtvÿrtvÿqsuÿqsuÿprtÿoqtÿoqsÿnprÿnprÿnoqÿmoqÿlnpÿlnpÿikmÿeghÿ^`bÿVWYÿIJKÿABCÿÿ7+5ÿ0#-ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:6:ÿ1.1ÿÿÿÿ@DFÿ?CDÿ>BDÿ=@Bÿ8<=ÿ/23ÿ9=?ÿAEGÿBGIÿDHKÿFJLÿGLNÿIMPÿJORÿLQSÿLQTÿLQTÿLQTÿLQTÿLQTÿLQSÿKPSÿKPSÿKORÿJORÿJORÿJNQÿINQÿINPÿIMPÿHMPÿHMOÿHLOÿGLOÿGLNÿGKNÿFKNÿFKMÿFJMÿEJLÿEJLÿEILÿDIKÿDIKÿDIKÿDIKÿ@DFÿ6;<ÿ ÿÿÿÿÿÿÿÿÿÿ-01ÿBFIÿJORÿEJLÿ;>Aÿ245ÿ>?@ÿGHJÿKMNÿNPQÿOQRÿOQRÿOPRÿOQRÿQSTÿTVXÿY[\ÿ_abÿdfgÿhjlÿkmoÿmoqÿoqsÿortÿortÿortÿortÿortÿoqsÿnqsÿmprÿmoqÿloqÿlnpÿknpÿkmoÿjmoÿjlnÿilnÿikmÿhkmÿhjlÿgjlÿgikÿfhjÿcegÿ^`bÿVWYÿKMNÿBDE¿?@@ÿÿ9,7ÿ/#,ÿ ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ;7;ÿ1.1ÿÿÿÿ@CEÿ?CDÿ>BDÿ=@Bÿ7;<ÿ/22ÿ7:<ÿ=ACÿ?BDÿ@DFÿBFHÿCGIÿEIKÿGKMÿHLNÿHLOÿHLOÿHLOÿHLOÿHLOÿHLNÿGKNÿGKNÿGKMÿFJMÿFJMÿFJLÿEILÿEILÿEIKÿEHKÿDHKÿDHJÿCGJÿCGJÿCGIÿCFIÿBFIÿBFHÿBEHÿAEHÿAEGÿADGÿADGÿADGÿ@DFÿ=@Bÿ479ÿ ÿÿÿÿÿÿÿÿÿÿ+-/ÿ>BDÿGKMÿBEHÿ8;=ÿ023ÿ>>@ÿGHJÿLMOÿOPQÿQRTÿSTVÿTVXÿVXZÿXZ\ÿ[]_ÿ^`bÿaceÿdfhÿfhjÿikmÿkmoÿmoqÿmoqÿmoqÿmoqÿmoqÿmoqÿlnpÿlnpÿkmoÿkmoÿjlnÿjlnÿikmÿikmÿhjlÿhjlÿgikÿgikÿghjÿfhiÿegiÿegiÿdfhÿdfhÿbdfÿ^_aÿVXZÿMOPÿDFFÏ>?@/ÿÿ9.7ÿ.#,ÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ:7:ÿ0.0ÿÿÿÿ@DFÿ?CEÿ>BDÿ=@Bÿ8<=ÿ/23ÿ479ÿ:>?ÿ;?Aÿ=ABÿ>BDÿ@CEÿAEGÿCGIÿDHJÿDHJÿDHJÿDHJÿDHJÿDHJÿDHJÿCGIÿCGIÿCGIÿBFHÿBFHÿBFHÿBEGÿAEGÿAEGÿAEGÿ@DFÿ@DFÿ@DFÿ?CEÿ?CEÿ?CDÿ?BDÿ>BDÿ>BDÿ>BCÿ=ACÿ=ACÿ=ABÿ=ABÿ=ABÿ9=>ÿ145ÿ ÿÿÿÿÿÿÿÿÿÿ(+,ÿ;?AÿCGIÿ>BDÿ589ÿ023ÿ?ABÿJLMÿNPQÿQRTÿSUVÿVWYÿXZ[ÿ[\^ÿ]_`ÿ_acÿbdeÿdfhÿgijÿikmÿlnpÿnprÿprtÿprtÿprtÿprtÿprtÿprtÿprtÿoqsÿnprÿnprÿmoqÿmoqÿlnpÿlnpÿkmoÿkmoÿjlnÿjlnÿikmÿikmÿhjlÿhjkÿgikÿgijÿfhjÿegiÿaceÿZ\^ÿQRTÿFGHïBCE/ÿ ÿ<1:ÿ-#+ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ:7:ÿ1/1ÿÿÿÿCGIÿBFHÿAEGÿ@CEÿ;>@ÿ145ÿ479ÿ9=?ÿ;?@ÿ<@Bÿ>ACÿ?CEÿADFÿBFHÿCGIÿDHJÿDHJÿDHJÿDHJÿDHJÿCGIÿCGIÿCGIÿCGIÿBFHÿBFHÿBEGÿAEGÿAEGÿAEGÿADFÿ@DFÿ@DFÿ@CEÿ?CEÿ?CDÿ?BDÿ>BDÿ>BDÿ>BCÿ>ACÿ=ACÿ=ABÿ=ABÿ=ABÿ<@Aÿ9<=ÿ145ÿ ÿÿÿÿÿÿÿÿÿÿ(+,ÿ;?@ÿBFHÿ>ACÿ479ÿ134ÿCDFÿNOQÿRTUÿUWXÿXY[ÿZ\]ÿ]^`ÿ_abÿbdeÿdfhÿgikÿjkmÿlnpÿoprÿqsuÿtvxÿvxzÿvxzÿvxzÿvxzÿvxzÿvxzÿuwyÿuwyÿtvxÿtvxÿsuwÿsuwÿrtvÿqsuÿqsuÿprtÿprtÿoqsÿoqsÿnprÿnpqÿmoqÿmopÿlnpÿlmoÿkmoÿjlnÿfhjÿ`bcÿVXYÿLMNïEEG/?ÿ ÿ=2;ÿ,"*ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿÂÀÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ…„ÿ  ÿ  ÿ  ÿ;8;ÿ202ÿÿÿÿMPRÿLOQÿKNPÿILNÿCFHÿ8;<ÿ8;<ÿ<@Aÿ>ACÿ?CEÿADFÿBFHÿDGIÿEIKÿGKMÿGKMÿGKMÿGKMÿGKMÿGKMÿFJLÿFJLÿFJLÿFJKÿEIKÿEIKÿEIJÿDHJÿDHJÿDHIÿCGIÿCGIÿCGHÿBFHÿBFHÿBEGÿAEGÿAEGÿAEFÿADFÿ@DFÿ@DEÿ@CEÿ@CEÿ@CEÿ?CDÿ;?@ÿ367ÿ ÿÿÿÿÿÿÿÿÿÿ*-.ÿ>BCÿEIKÿADFÿ7:;ÿ467ÿHIJÿSUVÿXZ[ÿ[\^ÿ^_aÿ`bcÿcefÿfgiÿhjlÿkmoÿnprÿqstÿsuwÿvxzÿy{}ÿ|~€ÿ~€‚ÿ~€‚ÿ~€‚ÿ~€‚ÿ~€‚ÿ~€‚ÿ}ÿ}€ÿ|~€ÿ{}ÿ{}ÿz|~ÿz|}ÿy{}ÿy{|ÿxz|ÿwy{ÿwy{ÿvxzÿvxyÿuwyÿuwxÿtvxÿsuwÿsuwÿrtvÿrtuÿqsuÿmoqÿfhjÿ[]^ÿKLNïFGH?ÿ ÿ>4=ÿ+"*ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿÂÀÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ…„ÿ  ÿ  ÿ  ÿ;9;ÿ202ÿÿÿÿfikÿehiÿcfhÿadfÿY\^ÿKMOÿHJLÿMPQÿORSÿQSUÿSVWÿUXYÿWY[ÿY[]ÿZ]_ÿ[^`ÿ[^`ÿ[^`ÿ[^`ÿ[^`ÿZ]_ÿZ]_ÿY\^ÿY\^ÿY[]ÿX[]ÿXZ\ÿWZ\ÿWZ\ÿVY[ÿVY[ÿVXZÿUXZÿUXZÿUXYÿTWYÿTVXÿSVXÿSVWÿSUWÿRUWÿRTVÿQTVÿQTVÿQTVÿQSUÿLOPÿACEÿÿÿÿÿÿÿÿÿÿÿ689ÿPRTÿY\^ÿSUWÿFHJÿ<>?ÿNOPÿZ\]ÿ_abÿbdeÿeghÿhjkÿkmnÿnpqÿqrtÿtvwÿwyzÿz|~ÿ}€ÿ€‚„ÿƒ…‡ÿ†ˆŠÿˆŠŒÿˆŠŒÿˆŠŒÿˆŠŒÿˆŠŒÿ‡‰‹ÿ‡‰‹ÿ†ˆŠÿ†ˆŠÿ…‡‰ÿ…‡‰ÿ„†ˆÿƒ…‡ÿƒ…‡ÿ‚„†ÿ‚ƒ…ÿƒ…ÿ€‚„ÿ€‚„ÿƒÿ~€‚ÿ~€‚ÿ}ÿ}~€ÿ|~€ÿ|}ÿ{}~ÿz|~ÿz{}ÿuwyÿlmoÿ\]^ÿMNP¿?ÿÿ?5>ÿ*")ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿÂÀÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„„ÿ  ÿ  ÿ  ÿ<9<ÿ202ÿÿÿ'&)ÿ•—™ÿ“•—ÿ‘“•ÿŽ’ÿƒ…†ÿnoqÿvwyÿƒ…ÿ…‡‰ÿˆŠŒÿ‹ÿŽ’ÿ‘”–ÿ”—™ÿ—šœÿ˜šœÿ˜šœÿ˜šœÿ˜šœÿ—šœÿ—™›ÿ–™›ÿ•˜šÿ•˜šÿ”—™ÿ”–˜ÿ“•—ÿ’•—ÿ’”–ÿ‘”•ÿ“•ÿ’”ÿ‘“ÿŽ‘“ÿŽ’ÿ‘ÿŒ‘ÿŒŽÿ‹ÿŠŽÿŠŒŽÿ‰‹ÿ‰ŠŒÿ‰ŠŒÿ‰ŠŒÿˆŠŒÿ€ƒÿmopÿÿÿÿÿÿÿÿÿÿÿ\]_ÿ†ˆŠÿ–˜šÿ‹ÿuxyÿSTUÿUUWÿbceÿhijÿklnÿnoqÿqstÿtvxÿwy{ÿ{|~ÿ~€ÿƒ…ÿ„†ˆÿˆ‰‹ÿ‹ÿŽ’ÿ‘“•ÿ”–˜ÿ”–˜ÿ”–˜ÿ”–˜ÿ”–˜ÿ“•—ÿ“•—ÿ’”–ÿ’”–ÿ‘’”ÿ’”ÿ‘“ÿ‘“ÿŽ’ÿŽ‘ÿ‘ÿŒŽÿ‹ÿ‹ÿŠŒŽÿ‰‹ÿ‰‹ÿˆŠŒÿ‡‰‹ÿ‡‰Šÿ†ˆŠÿ…‡‰ÿ…‡ˆÿ…‡ˆÿƒ…‡ÿ|}ÿlnoÿ[\]ÿRST/?ÿÿA6?ÿ*"(ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿÂÀÁÿÿÿÿÿÿÿÿÿIBGÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ<:<ÿ313ÿÿÿ+),ÿ¢¤¦ÿŸ¡£ÿŸ¡ÿšœÿ‘ÿwxzÿ…†ˆÿ“•—ÿ—™›ÿ›Ÿÿž ¢ÿ¢¤¦ÿ¦¨ªÿ©«­ÿ­¯±ÿ­¯±ÿ­¯±ÿ­¯±ÿ­¯±ÿ­¯±ÿ¬®°ÿ¬®°ÿª¬®ÿª¬®ÿ©«­ÿ¨ª¬ÿ¨©«ÿ§©«ÿ¦¨ªÿ¥§©ÿ¥§©ÿ¤¥§ÿ£¥§ÿ¢¤¦ÿ¡£¥ÿ¡£¥ÿ ¢¤ÿŸ¡£ÿŸ ¢ÿž ¢ÿŸ¡ÿœž ÿ›Ÿÿ›Ÿÿ›Ÿÿšœžÿ‘“•ÿ|~ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿijlÿ™›ÿ«­¯ÿž ¢ÿ†ˆ‰ÿ]^_ÿ\]^ÿklmÿqrtÿtvwÿxy{ÿ{}~ÿ€‚ÿ‚„†ÿ†ˆ‰ÿ‰‹ÿŽÿ’”ÿ”–˜ÿ—™›ÿ›Ÿÿž ¢ÿ¡£¥ÿ¡£¥ÿ¡£¥ÿ¡£¥ÿ¡£¥ÿ ¢¤ÿ ¢¤ÿŸ¡£ÿž ¢ÿž ¢ÿŸ¡ÿœž ÿ›Ÿÿ ¢¤ÿ ¡£ÿ¤¦§ÿ¦§©ÿ¦§©ÿ¦¨©ÿ¦¨©ÿ¦¨ªÿ¦¨ªÿ§¨ªÿ§¨ªÿ§¨ªÿ¢£¥ÿ™›ÿ‘’”ÿ‘’”ÿ‘“ÿˆ‰‹ÿwy{ÿefgÿXY[??ÿÿA7?ÿ(!&ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿÂÀÁÿÿÿÿÿÿÿÿÿHBFÿ  ÿ  ÿ  ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<:<ÿ323ÿÿÿ+),ÿ¤¦¨ÿ¢¤¦ÿ ¢£ÿœž ÿ’“ÿyz|ÿˆŠ‹ÿ—™›ÿ›ŸÿŸ¡¢ÿ¢¤¦ÿ¦¨ªÿª¬®ÿ®°±ÿ±³µÿ²´µÿ²´µÿ²´µÿ²´µÿ±³µÿ±³´ÿ°²´ÿ¯±³ÿ®°²ÿ®°±ÿ­¯°ÿ¬®¯ÿ«­¯ÿª¬®ÿª«­ÿ©«­ÿ¨ª¬ÿ§©«ÿ§©ªÿ¦¨©ÿ¥¦¨ÿ¤¦¨ÿ£¥§ÿ£¤¦ÿ¢¤¥ÿ¡£¥ÿ ¢¤ÿ ¡£ÿ ¡£ÿ ¡£ÿŸ ¢ÿ•—˜ÿ€‚ÿ"""ÿÿÿÿÿÿÿÿÿÿlmoÿžŸ¡ÿ¯±³ÿ£¤¦ÿ‰‹Œÿabcÿbdeÿrtuÿyz{ÿ}~ÿ„…†ÿ‘“ÿ›œžÿš›ÿ˜™›ÿ–˜šÿ—™›ÿœžŸÿŸ ¢ÿ¢¤¦ÿ¦¨©ÿª¬­ÿ¬®°ÿ¬®°ÿ¬®°ÿ¬®°ÿ¬®°ÿ¬®¯ÿ«­¯ÿª¬®ÿ©«­ÿª¬­ÿ®°²ÿµ¶¸ÿ¸º¼ÿ¹»¼ÿº»½ÿ»¼¾ÿ¼½¾ÿ½¾¿ÿ½¿Àÿ¾¿Áÿ¿ÁÂÿÀÁÃÿÁÂÃÿÂÃÄÿÃÄÅÿÄÅÆÿÅÆÇÿÁÂÃÿ¨ª«ÿ—™›ÿŽ‘ÿ{|}ÿfgiï\]^oÿ ÿA8@ÿ' &ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ¿ÁÿÿÿÿÿÿÿÿÿIAFÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<:<ÿ323ÿÿÿ+*,ÿ¨©«ÿ¥§¨ÿ£¥¦ÿŸ¡¢ÿ“•–ÿ|}~ÿŽÿŸ ÿ¡£¤ÿ¥§¨ÿ©«¬ÿ­¯°ÿ±²´ÿµ·¸ÿ¸º»ÿ¸º¼ÿ¸º¼ÿ¸º¼ÿ¸º¼ÿ¸º»ÿ·¹»ÿ·¹ºÿ¶¸¹ÿµ·¸ÿ´¶¸ÿ³µ·ÿ²´µÿ²³µÿ±³´ÿ°²³ÿ¯±²ÿ®°²ÿ®°±ÿ­¯°ÿ¬®¯ÿ«­®ÿª¬­ÿª«¬ÿ©«¬ÿ¨ª«ÿ§©ªÿ¦¨©ÿ¦§©ÿ¥§©ÿ¥§©ÿ¤¦¨ÿ›œžÿ„†‡ÿ'''ÿ***ÿ***ÿ***ÿ***ÿ***ÿ***ÿ***ÿ111ÿÿqrsÿ¤¦§ÿ¶¸¹ÿ¨ª«ÿ‘ÿefgÿghiÿxyyÿ~€ÿ£¤¥ÿ¸¹¹ÿ¸¹ºÿ³´µÿ­®¯ÿ¦¨©ÿŸ¡¢ÿ ¢£ÿ¦¨ªÿª¬­ÿ­¯°ÿ¯±²ÿ²´µÿ´¶·ÿ´¶¸ÿ´¶¸ÿ´¶¸ÿ´¶¸ÿ´¶·ÿ³µ·ÿ²´¶ÿ²´µÿ»¼¾ÿ¾¿Áÿ¾ÀÁÿ¿ÀÁÿÀÁÂÿÀÁÃÿÁÂÃÿÂÃÄÿÂÃÅÿÃÄÅÿÄÅÆÿÅÆÇÿÅÇÇÿÆÇÈÿÇÈÉÿÈÉÊÿÉÊËÿÊËËÿÊÌÌÿÀÁÂÿ˜š›ÿ‹Žÿuvwÿdefoÿ#"ÿC:Aÿ' %ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ¿ÁÿÿÿÿÿÿÿÿÿIAFÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=;=ÿ323ÿÿÿ,+-ÿ«­®ÿ©«¬ÿ§©ªÿ£¥¦ÿ–˜™ÿ~€ÿ’”•ÿ¤¦§ÿ¨ª«ÿ¬®¯ÿ°²³ÿ´¶·ÿ¸º»ÿ¼¾Àÿ¿ÁÃÿÀÂÄÿÀÂÄÿÀÂÄÿÀÂÄÿÀÂÃÿ¿ÁÂÿ¾ÀÂÿ½¿Áÿ¼¾Àÿ¼½¿ÿ»½¿ÿº¼½ÿ¹»¼ÿ¸º»ÿ¸¹»ÿ·¹ºÿ¶·¹ÿµ·¸ÿ´¶¸ÿ³µ·ÿ²´µÿ²³µÿ±³´ÿ°²³ÿ¯±²ÿ®°±ÿ­¯°ÿ­®°ÿ­®°ÿ­®°ÿ¬­¯ÿ¡£¤ÿŠŒÿ,,,ÿ:::ÿ:::ÿ:::ÿ:::ÿ:::ÿ:::ÿ:::ÿVVVÿÿvwxÿ«­®ÿ½¿Àÿ¯±³ÿ•–—ÿjllÿnppÿ‚ÿ³´µÿÃÃÄÿÁÂÂÿ½¿¿ÿ¹»»ÿµ¶·ÿ¯±²ÿ¨ª«ÿ«­®ÿ±³´ÿµ·¸ÿ¹»¼ÿ½¿ÀÿÁÃÄÿÃÅÆÿÂÄÅÿÁÃÄÿÁÃÄÿÁÃÄÿÀÂÃÿ¿ÁÃÿ¿ÁÂÿÁÂÄÿÇÉÊÿÈÉËÿÈÊËÿÉÊËÿÉÊÌÿÊËÌÿÊËÌÿÊÌÍÿËÌÍÿËÍÎÿÌÍÎÿÍÎÏÿÍÎÏÿÎÏÐÿÎÏÐÿÏÐÑÿÐÑÒÿÐÑÒÿÉËËÿ¬­®ÿ—™šÿ†‡ˆÿpqrŸÿ%$ÿC;Bÿ&%ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ ÿ ÿ ÿ¿ÁÿÿÿÿÿÿÿÿÿIAFÿ  ÿ  ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=;=ÿ434ÿÿÿ-,.ÿ°²³ÿ­¯±ÿ«­®ÿ§©ªÿšœÿƒ„ÿ™››ÿ¬®®ÿ°²²ÿ´¶·ÿ¹º»ÿ½¿¿ÿÁÃÄÿÅÇÈÿÉËËÿÉËÌÿÉËÌÿÉËÌÿÉËÌÿÉËÌÿÈÊËÿÇÉÊÿÇÉÉÿÆÈÈÿÄÆÇÿÄÆÆÿÃÅÆÿÂÄÅÿÁÃÄÿÀÂÃÿÀÁÂÿ¿ÀÁÿ¾ÀÀÿ½¿¿ÿ¼¾¾ÿ»½½ÿº¼½ÿ¹»¼ÿ¸º»ÿ¸¹ºÿ·¹¹ÿ¶¸¸ÿµ··ÿµ··ÿµ··ÿ´¶¶ÿ©««ÿ‘’“ÿ111ÿIIIÿIIIÿIIIÿIIIÿIIIÿIIIÿIIIÿ{{{ÿÿ|}~ÿ´µ¶ÿÆÈÉÿ¸¹ºÿœžÿqrsÿwxyÿŸ  ÿÈÉÉÿÉÊÊÿÇÈÉÿÅÆÇÿÂÃÄÿ¿ÀÁÿ»¼½ÿµ¶·ÿ·¹¹ÿ½¿¿ÿÃÅÆÿÈÉÊÿÌÎÏÿÐÒÓÿÓÕÖÿÔÕÖÿÔÖÖÿÓÕÖÿÒÔÕÿÑÓÓÿÏÑÒÿÏÑÑÿÓÕÕÿÕ××ÿÕÖ×ÿÕ××ÿÕ××ÿÖרÿÖרÿÖ××ÿÖרÿÖרÿÖØØÿÖØØÿרÙÿרÙÿ×ÙÙÿØÙÙÿØÙÚÿÙÚÚÿÒÓÔÿ´µ¶ÿ¤¦¦ÿ‘““ÿ€Ÿÿ(!&ÿD<=ÿ545ÿÿÿ/./ÿµ·¸ÿ²´µÿ°²³ÿ¬®¯ÿž ¡ÿ…†‡ÿ ¢¢ÿµ¶·ÿ¹»»ÿ¾¿ÀÿÂÄÄÿÆÈÉÿËÍÍÿÏÑÒÿÓÕÕÿÔÖÖÿÔÖÖÿÔÖÖÿÔÖÖÿÓÕÕÿÒÔÔÿÑÓÓÿÑÓÓÿÐÒÒÿÏÑÑÿÎÐÐÿÌÎÏÿÌÎÎÿËÍÍÿÊÌÌÿÉËËÿÈÊÊÿÇÉÉÿÆÈÉÿÆÇÈÿÄÆÇÿÄÅÆÿÃÅÅÿÂÃÄÿÁÃÃÿÀÂÂÿ¿ÁÁÿ¾ÀÀÿ¾ÀÀÿ¾ÀÀÿ½¿¿ÿ±³´ÿ˜ššÿ677ÿYYYÿYYYÿYYYÿYYYÿYYYÿYYYÿYYYÿšššÿÿƒ„„ÿ½¿¿ÿÐÒÒÿÁÃÃÿ¤¥¥ÿwxxÿ}~~ÿºººÿÌÍÍÿÍÎÎÿÌÍÍÿËËÌÿÇÈÈÿ¾¿¿ÿ»¼¼ÿ¼¾¾ÿÁÂÂÿÆÇÇÿËÍÍÿÑÓÓÿÖØØÿÛÝÝÿÞààÿÞààÿÞààÿßááÿßááÿßááÿßááÿßààÿßàáÿßààÿÞààÿÞààÿÞßßÿÞßßÿÞßßÿÞßßÿÞßßÿÞßßÿÞßßÿÞßßÿÞßßÿÞßßÿÞßßÿÞßàÿÞßßÿÐÑÑÿ·¸¸ÿ§¨¨ÿ’““ÿ‚ƒƒŸÿ*#)ÿD=Cÿ%$ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ  ÿ  ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÁÿÿÿÿÿÿÿÿÿK@Gÿ ÿ ÿ  ÿ  ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ><>ÿ545ÿÿÿ0/1ÿº¼½ÿ¸¹ºÿµ·¸ÿ±³´ÿ£¥¦ÿ‰‹‹ÿ©ªªÿ¿ÀÀÿÄÅÅÿÉÊÊÿÍÎÎÿÒÓÓÿרØÿÛÝÝÿßááÿàááÿàááÿàááÿàááÿßááÿÞààÿÝßßÿÜÞÞÿÜÝÝÿÛÜÜÿÚÛÛÿØÚÚÿ×ÙÙÿÖØØÿÕ××ÿÕÖÖÿÓÕÕÿÓÔÔÿÒÓÓÿÑÒÒÿÐÑÑÿÏÐÐÿÎÏÏÿÍÎÎÿÌÍÍÿËÌÌÿÊËËÿÉÊÊÿÉÊÊÿÉÊÊÿÈÉÉÿ¼½½ÿ¡¢¢ÿ<<<ÿhhhÿhhhÿhhhÿhhhÿhhhÿhhhÿhhhÿ¯¯¯ÿÿ‹ŒŒÿÈÉÉÿÛÝÝÿÌÍÍÿ­®®ÿ|}}ÿ€€ÿÉÉÉÿÎÎÎÿÏÐÐÿÎÏÏÿÂÃÃÿ¬­­ÿ¬­­ÿ±²²ÿ¶¸¸ÿ¿ÀÀÿÈÉÉÿÏÐÐÿÕÖÖÿÛÜÜÿàââÿäååÿäååÿäååÿäåæÿåææÿäææÿäååÿäååÿãääÿãäåÿãääÿãääÿâããÿâããÿâããÿáâãÿââãÿâããÿâããÿââãÿâããÿáââÿáââÿßààÿÍÎÎÿ¸ººÿ¦§§ÿ‘‘ÿ€oÿ/).ÿE=Cÿ$#ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÁÿÿÿÿÿÿÿÿÿK@Gÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>=>ÿ656ÿÿÿ102ÿÀÂÃÿ¾¿Àÿ»½¾ÿ·¹¹ÿ¨ª«ÿÿ°±±ÿÈÉÉÿÌÍÍÿÑÒÒÿÖ××ÿÛÜÜÿàááÿåææÿéêêÿêëëÿêëëÿêëëÿêëëÿéêêÿèééÿçèèÿæççÿåææÿäååÿãääÿâããÿáââÿàááÿßààÿÞßßÿÝÞÞÿÜÝÝÿÛÜÜÿÚÛÛÿÙÚÚÿØÙÙÿרØÿÖ××ÿÕÖÖÿÔÕÕÿÓÔÔÿÒÓÓÿÒÓÓÿÒÓÓÿÐÑÑÿÄÅÅÿ¨©©ÿABBÿwwwÿwwwÿwwwÿwwwÿwwwÿwwwÿwwwÿ···ÿÿ‘’’ÿÑÒÒÿäååÿÕÖÖÿ´µµÿ€€€ÿ‡ˆˆÿÉÉÉÿÍÍÍÿÍÎÎÿº»»ÿœœœÿ—˜˜ÿ•––ÿ˜™™ÿ¡¡¡ÿ­®®ÿ¼½½ÿÊËËÿÕÖÖÿÞßßÿäååÿèééÿèééÿéêêÿéêêÿêêêÿéêêÿééêÿèééÿçèèÿçèèÿçèèÿçèèÿæççÿæççÿææçÿåææÿåææÿåææÿååæÿåææÿååæÿåååÿßßßÿÊËËÿº»»ÿ¥¥¥ÿŽŽï?¯ÿ5/4ÿE>Dÿ$#ÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÂÿÿÿÿÿÿÿÿÿL@Gÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?>?ÿ656ÿÿÿ223ÿÇÈÉÿÄÆÆÿÂÃÃÿ½¿¿ÿ®°°ÿ’””ÿµ¶¶ÿÎÎÎÿÓÓÓÿØØØÿÝÝÝÿâââÿçèèÿìììÿðððÿñññÿñññÿñññÿñññÿðððÿïïïÿîîîÿíííÿìììÿëëëÿêêêÿéééÿèèèÿçççÿæææÿåååÿäääÿãããÿâââÿáááÿßààÿÞßßÿÝÞÞÿÜÜÜÿÛÛÛÿÚÚÚÿÙÙÙÿØØØÿØØØÿØØØÿ×××ÿÉÉÉÿ­­­ÿFFFÿ‡‡‡ÿ‡‡‡ÿ‡‡‡ÿ‡‡‡ÿ‡‡‡ÿ‡‡‡ÿ‡‡‡ÿµµµÿÿ–––ÿÖ××ÿêêêÿÚÚÚÿ¹¹¹ÿ‚‚‚ÿŽŽŽÿÇÇÇÿÉÉÉÿ½¾¾ÿ‘‘‘ÿ‚‚‚ï}}}{||O}}~O…††ŽŽß¢¢¢ÿ¶··ÿÈÉÉÿÙÙÙÿäååÿëììÿìííÿííîÿíîîÿíîîÿíîîÿìííÿìììÿëììÿêëëÿêëëÿêëëÿêêëÿéêêÿéêêÿèééÿèééÿèèéÿèèèÿçèèÿæççÿÙÚÚÿÆÇÇÿ·¸¸ÿ£¤¤ÿŽŽ¿ƒƒƒ¿ÿ716ÿF?Eÿ$#ÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÄ¿ÂÿÿÿÿÿÿÿÿÿM@Hÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?>?ÿ767ÿÿÿ445ÿÎÐÐÿËÍÍÿÈÊÊÿÄÅÅÿ´¶¶ÿ—™™ÿ¹¹¹ÿÑÑÑÿÖÖÖÿÜÜÜÿáááÿæææÿëëëÿðððÿôôôÿõõõÿõõõÿõõõÿõõõÿôôôÿóóóÿòòòÿñññÿðððÿïïïÿîîîÿìììÿëëëÿêêêÿéééÿéééÿèèèÿçççÿæææÿåååÿãããÿâââÿáááÿàààÿßßßÿÞÞÞÿÝÝÝÿÜÜÜÿÜÜÜÿÜÜÜÿÚÚÚÿÌÌÌÿ¯¯¯ÿKKKÿ–––ÿ–––ÿ–––ÿ–––ÿ–––ÿ–––ÿ–––ÿ²²²ÿÿ˜˜˜ÿÙÙÙÿëëëÿÛÛÛÿ»»»ÿƒƒƒÿŒŒŒÿÃÃÃÿÀÀÀÿ‰‰‰ÿxxxŸtttŽŽŽ?™™™Ï®®®ÿÅÆÆÿÙÙÙÿçççÿíííÿïïðÿðññÿñññÿðððÿðððÿïïïÿîîîÿíííÿíííÿìííÿìííÿìììÿìììÿëëëÿëëëÿêêëÿêêêÿäååÿÓÓÓÿÂÃÃÿ±±±ÿŽo¿ÿ:38ÿF?Eÿ#"ÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÄ¿ÂÿÿÿÿÿÿÿÿÿM?Hÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@?@ÿ767ÿÿÿ666ÿÕ××ÿÒÔÔÿÐÑÑÿËÌÌÿ»¼¼ÿžžÿ¹¹¹ÿÐÐÐÿÕÕÕÿÚÚÚÿßßßÿåååÿééêÿîîîÿóóóÿóóóÿóóóÿóóóÿóóóÿòòóÿññòÿððñÿïïïÿîîîÿíííÿìììÿëëëÿêêêÿéééÿèèèÿçççÿååæÿåååÿäääÿãããÿâââÿáááÿàààÿßßßÿÞÞÞÿÝÝÝÿÛÛÜÿÚÚÛÿÚÚÚÿÚÚÚÿ×רÿÈÈÉÿ¬¬­ÿNNNÿ¦¦¦ÿ¦¦¦ÿ¦¦¦ÿ¦¦¦ÿ¦¦¦ÿ¦¦¦ÿ¦¦¦ÿ²²²ÿÿ••–ÿÓÓÔÿåååÿÕÕÖÿ···ÿ‚ÿÿ¾¾¾ÿÿttt_–––o§¨¨ïÀÀÀÿÕÕÕÿåååÿíîîÿñòòÿóóóÿòóóÿñòòÿññòÿðððÿïððÿïïïÿîïïÿîîîÿîîîÿíîîÿíííÿììíÿéêêÿÞÞÞÿËËËÿ»»»ÿ§§§ÿ–——¯ŒŒŒ¿ÿ<5;ÿG@Eÿ#"ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÄ¿ÂÿÿÿÿÿÿÿÿÿO@Iÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@?@ÿ878ÿÿÿ878ÿÛÜÜÿØÙÙÿÖÖÖÿÑÑÑÿÀÁÁÿ¡¢¢ÿ¢¤¥ÿ°²³ÿµ¶·ÿ¸º¼ÿ½¿ÀÿÁÃÅÿÅÇÉÿÊÌÍÿÍÏÑÿÎÏÑÿÎÏÑÿÎÏÑÿÎÏÑÿÍÏÐÿÌÎÏÿÌÍÏÿËÌÎÿÊËÍÿÉËÌÿÈÊËÿÇÉÊÿÆÈÉÿÅÇÈÿÄÆÇÿÄÅÇÿÂÄÆÿÂÃÅÿÁÂÄÿÀÂÃÿ¿ÁÂÿ¾ÀÁÿ½¿Àÿ¼¾¿ÿ¼½¾ÿ»¼¾ÿ¹»½ÿ¹º»ÿ¸º»ÿ¸¹ºÿ³´µÿ¦§¨ÿ‘‘ÿ***ÿXXXÿXXXÿXXXÿXXXÿXXXÿXXXÿXXXÿPPPÿÿ}~ÿ®¯±ÿ»½¾ÿ¯°²ÿ—˜™ÿtuuÿqqqÿ|||ÿlll_ššš¯¯°°ÿÉÉÉÿÛÛÛÿçççÿîîîÿòòòÿóóóÿóóóÿòòòÿñññÿñññÿðððÿïïïÿîîïÿìììÿéééÿàààÿÎÎÎÿ¾¿¿ÿ©©©ÿ–––ß‘‘‘?¿ÿ>7<ÿG@Fÿ""ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÄ¿ÂÿÿÿÿÿÿÿÿÿO@Iÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿA@Aÿ878ÿÿÿ989ÿàààÿÝÝÝÿÚÚÚÿÕÕÕÿÅÅÅÿ¥¦¦ÿ„†ˆÿ…ˆ‹ÿˆŒŽÿ‹‘ÿ’•ÿ’–™ÿ•™œÿ™œŸÿ›Ÿ¢ÿœŸ¢ÿœŸ¢ÿœŸ¢ÿœŸ¢ÿ›Ÿ¡ÿ›ž¡ÿšž ÿ™Ÿÿ™œŸÿ˜œžÿ—›ÿ–šœÿ–šœÿ•™›ÿ”˜›ÿ”˜šÿ“—™ÿ’–™ÿ’•˜ÿ‘•—ÿ”–ÿ“–ÿ“•ÿŽ’”ÿŽ‘“ÿ“ÿŒ’ÿ‹ÿŠŽÿˆŒŽÿ„‡‰ÿ{~€ÿlnpÿÿÿÿÿÿÿÿÿÿÿ^abÿ„†ÿŠÿ„†ÿqtvÿdffÿjjjÿhhhŸ———O§§§ß¼¼¼ÿÎÎÎÿÜÜÜÿåååÿìììÿðððÿñññÿñññÿðððÿîîîÿëëëÿçççÿßßßÿÏÏÏÿ¾¾¾ÿ®®®ÿß_¿ÿ?9>ÿGAEÿ! ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÅ¿ÂÿÿÿÿÿÿÿÿÿP@Iÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@ÿ888ÿÿÿ999ÿäääÿáááÿÞÞÞÿØØØÿÇÇÇÿ§§§ÿadfÿUY]ÿW[^ÿY]aÿ[`cÿ]beÿ_dgÿafjÿchlÿchlÿchlÿchlÿchlÿchlÿcgkÿbgkÿafjÿafiÿafiÿ`eiÿ`ehÿ_dgÿ_dgÿ_cgÿ^cfÿ^bfÿ]bfÿ]aeÿ\aeÿ\`dÿ[`dÿ[_cÿ[_cÿZ_bÿZ^bÿY^aÿY]aÿX\`ÿW[^ÿUX\ÿPTWÿILOÿÿÿÿÿÿÿÿÿÿÿ@CFÿTY\ÿY]aÿTX[ÿKORÿUWXÿgggÏ–––ŸŸŸ«««ï½½½ÿÊÊÊÿÔÔÔÿÜÜÜÿâââÿåååÿäääÿáááÿÙÙÙÿÊÊÊÿ¸¸¸ÿ©©©ÿ››œÏ“““_¿ÿICHÿHAFÿ"!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÅ¿ÃÿÿÿÿÿÿÿÿÿR@Kÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿAAAÿ888ÿÿÿ:::ÿæææÿãããÿàààÿÚÚÚÿÉÉÉÿ©©©ÿQSVÏ7CF¿@DH¿AEI¿AEI¿AEI¿AEI¿AEI¿AEI¿@EI¿@EH¿@DH¿@DH¿@DH¿?CG¿?CG¿>CF¿>CF¿>BE¿=BE¿=BE¿=AE¿=AD¿=AD¿A¿:>A¿:>@¿9=@¿7;>¿59;¿¿¿¿¿¿¿¿¿¿¯/36¿;@C¿=BE¿:?B¿6;=¿QRSßggg/———ŸŸŸo¦¦¦Ï¯¯¯ÿºººÿÀÀÀÿÀÀÀÿ½½½ÿ···ÿ®®®ÿ¢¢¢ß™™™/ÿ  ÿLEKÿHBGÿ!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÆ¿ÃÿÿÿÿÿÿÿÿÿS@Kÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿAAAÿ999ÿÿÿ:::ÿçççÿäääÿáááÿÜÜÜÿÊÊÊÿªªªÿ?–––OŸŸŸŸŸŸšššO•••ÿ ÿMGLÿHBFÿ! ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿ+ ÿðïðÿÿÿÿÿÿÿÿÿT@Lÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBBBÿ999ÿÿÿ999ÿâããÿàààÿÝÝÝÿÖ××ÿÅÅÅÿ¦¦¦ÿŠŠŠ?ÿ ÿOINÿHCGÿ! ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿ,!ÿÔÏÒÿÿÿÿÿÿÿÿÿñïðÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBBBÿ:::ÿÿÿ233ÿÈÊËÿÆÇÈÿÃÅÆÿ¼¾¿ÿ¬­®ÿ‘’“ÿz{|?ÿ ÿQKPÿHBGÿ! ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿ ÿX@Nÿñïðÿÿÿÿÿÿÿÿÿÿÿÿÿpzÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿCCCÿ:::ÿÿÿ+,,ÿ­¯±ÿª­¯ÿ¨ª¬ÿŸ¡£ÿ‘“ÿz{}ÿhij?ÿÿSMRÿICGÿ! !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿ ÿ!ÿž˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬Ÿ§ÿ"ÿ!ÿ ÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿDDDÿ;;;ÿÿÿ$%&ÿ”—ÿ‘”ÿ‰ÿ~‚„ÿqtvÿ`ceÿUWY?ÿÿUOTÿICHÿ! !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿ ÿ!ÿ#ÿ¬Ÿ§ÿÿÿÿÿÿÿÿÿÿÿÿÿñïðÿw`nÿ%ÿ$ÿ$ÿ#ÿ"ÿ ÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿDDDÿ<<<ÿÿÿÿmruÿjorÿdilÿZ^aÿNRTÿGJLŸÿÿWQUÿICHÿ! !ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿÿÿÿ!ÿ"ÿ$ÿ‘€ŠÿÿÿÿÿÿÿÿÿÿÿÿÿÖÏÓÿO0Cÿ'ÿ'ÿ&ÿ%ÿ%ÿ$ÿ#ÿ!ÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿEEEÿ<<<ÿÿÿÿJOSÿHMPÿDILÿ=ADÿ7;=Ÿÿ ÿYSXÿJDHÿ" "ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ ÿ ÿ ÿÿÿÿÿ ÿ!ÿ#ÿ$ÿ3&ÿñïðÿÿÿÿÿÿÿÿÿ¯Ÿ©ÿ7(ÿ*ÿ)ÿ)ÿ(ÿ(ÿ'ÿ&ÿ%ÿ#ÿ"ÿ ÿÿÿÿÿ ÿ ÿ ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿEEEÿ<<<ÿÿÿ ß:>A9=@7;>37:o?ÿ""ÿ[UYÿJDHÿ"!"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ ÿ"ÿ$ÿ%ÿ&ÿkP`ÿÿÿÿÿÿÿÿÿäßâÿ8)ÿ+ÿ+ÿ+ÿ+ÿ*ÿ*ÿ)ÿ(ÿ&ÿ%ÿ$ÿ"ÿ ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿFFFÿ===ÿÿÿ¿?ÿ$ $ÿ\V[ÿJEIÿ"!"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿÿÿ"ÿ$ÿ%ÿ'ÿ)ÿ”€Œÿÿÿÿÿÿÿÿÿ£›ÿ.ÿ.ÿ.ÿ.ÿ.ÿ-ÿ,ÿ+ÿ*ÿ)ÿ'ÿ%ÿ$ÿ"ÿ ÿÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿFFFÿ>>>ÿÿÿ¿?ÿ&"&ÿ^X\ÿJDIÿ"!"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ"ÿ$ÿ&ÿ(ÿ)ÿ+ÿ{`qÿÿÿÿÿÿÿÿÿØÏÔÿ=,ÿ0ÿ1ÿ1ÿ0ÿ/ÿ/ÿ.ÿ-ÿ+ÿ*ÿ(ÿ&ÿ$ÿ"ÿ ÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGGÿ>>>ÿÿÿ¿?ÿ)$)ÿ`Y^ÿKEJÿ#!#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿ ÿ"ÿ$ÿ&ÿ(ÿ*ÿ,ÿ.ÿI :ÿÿÿÿÿÿÿÿÿÿÿÿÿØÏÕÿM <ÿ3 ÿ3 ÿ3 ÿ2ÿ2ÿ0ÿ/ÿ.ÿ,ÿ*ÿ(ÿ&ÿ$ÿ"ÿ ÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHGHÿ?>?ÿÿÿ¿?ÿ+%+ÿa[`ÿLEJÿ$"$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿÿ!ÿ#ÿ&ÿ(ÿ*ÿ,ÿ.ÿ0ÿ1ÿ¥ÿÿÿÿÿÿÿÿÿÿÿÿÿòïñÿš€ÿB0ÿ5"ÿ5!ÿ4 ÿ3 ÿ1ÿ0ÿ.ÿ,ÿ*ÿ(ÿ&ÿ$ÿ!ÿÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿIHIÿ@?@ÿÿÿ¿?ÿ-',ÿc]bÿLEKÿ$"$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿ ÿ#ÿ&ÿ(ÿ*ÿ,ÿ/ÿ0ÿ2ÿ4 ÿ5!ÿ³Ÿ¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòïñÿ›€‘ÿP >ÿ6"ÿ5!ÿ4 ÿ2ÿ1ÿ/ÿ,ÿ*ÿ(ÿ&ÿ#ÿ ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿIHIÿA@Aÿÿÿ¿?ÿ0*.ÿd^cÿLFKÿ$#$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ"ÿ%ÿ'ÿ)ÿ,ÿ.ÿ1ÿ3 ÿ4!ÿ6"ÿ7#ÿ8#ÿwPiÿæßãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´Ÿ¬ÿP >ÿ6"ÿ4!ÿ3 ÿ1ÿ/ÿ,ÿ*ÿ'ÿ%ÿ"ÿ ÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿJIJÿA@Aÿÿÿ¿?ÿ4-2ÿe_dÿMFKÿ"!"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ#ÿ&ÿ)ÿ+ÿ.ÿ0ÿ3 ÿ5!ÿ7"ÿ8#ÿ:%ÿ;%ÿ<&ÿ=&ÿ†`xÿçßäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´Ÿ¬ÿP >ÿ5!ÿ3 ÿ1ÿ.ÿ,ÿ)ÿ'ÿ$ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKJKÿA@Aÿÿÿ¿?ÿ=6<ÿgafÿLFKÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ ÿ"ÿ%ÿ(ÿ*ÿ-ÿ0ÿ2ÿ5!ÿ7#ÿ9$ÿ;%ÿ=&ÿ>'ÿ?(ÿ?(ÿ@)ÿ@)ÿˆ`yÿÛÏÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽpƒÿ5!ÿ3 ÿ0ÿ.ÿ+ÿ(ÿ&ÿ"ÿ ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKJKÿBABÿÿÿ¿Oÿ>8=ÿibhÿLFKÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿ!ÿ$ÿ'ÿ)ÿ,ÿ0ÿ2ÿ5!ÿ7#ÿ9$ÿ<&ÿ='ÿ?(ÿ@)ÿA*ÿB*ÿC*ÿC+ÿC*ÿB*ÿqA_ÿÏ¿Éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿš€ÿ5!ÿ2ÿ0ÿ-ÿ*ÿ'ÿ$ÿ!ÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKJKÿCBCÿÿÿ¿ÿA:?ÿjciÿMGLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ"ÿ&ÿ(ÿ+ÿ/ÿ1ÿ4!ÿ7"ÿ9$ÿ<&ÿ>'ÿ@(ÿA*ÿC+ÿD+ÿE,ÿE,ÿF,ÿE,ÿE,ÿD+ÿC+ÿ}QlÿóïñÿÿÿÿÿÿÿÿÿÿÿÿÿP >ÿ4!ÿ1ÿ/ÿ+ÿ(ÿ&ÿ"ÿ ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿLKLÿCBCÿÿÿ¿ÿB'ÿ@)ÿB*ÿD+ÿE,ÿG-ÿG.ÿH.ÿH.ÿH.ÿG.ÿG-ÿE,ÿD+ÿN7ÿÛÏ×ÿÿÿÿÿÿÿÿÿpƒÿ6"ÿ3 ÿ1ÿ-ÿ*ÿ'ÿ$ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMLMÿDCDÿÿÿ¿ÿD>CÿlfkÿMGLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ ÿ ÿÿÿÿ"ÿ&ÿ(ÿ,ÿ/ÿ2ÿ5!ÿ8#ÿ;%ÿ>'ÿ@)ÿC*ÿE,ÿF-ÿH.ÿI/ÿJ/ÿJ0ÿK0ÿJ0ÿJ/ÿI/ÿH.ÿG-ÿE,ÿ­¢ÿÿÿÿÿÿÿÿÿ€’ÿ8#ÿ5"ÿ2ÿ/ÿ,ÿ(ÿ&ÿ"ÿÿÿÿ ÿ ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMLMÿDCDÿÿÿ¿ÿF?DÿmgkÿNGMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ ÿ#ÿ'ÿ*ÿ-ÿ1ÿ4!ÿ7#ÿ:%ÿ='ÿ@)ÿC*ÿE,ÿG-ÿI/ÿJ0ÿL1ÿM1ÿM2ÿM2ÿM2ÿM1ÿL1ÿJ0ÿT<ÿ®£ÿÿÿÿÿÿÿÿÿÿÿÿÿzQjÿ:%ÿ7#ÿ4!ÿ1ÿ.ÿ*ÿ'ÿ$ÿ ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNMNÿDCDÿÿÿ¿ÿHAFÿnhmÿNHMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ%ÿ(ÿ+ÿ/ÿ2ÿ6"ÿ9$ÿ<&ÿ?(ÿB*ÿE,ÿG.ÿI/ÿL0ÿM2ÿO3ÿP3ÿP3ÿQ4ÿP3ÿP3ÿe!Lÿ±¥ÿóïòÿÿÿÿÿÿÿÿÿÿÿÿÿÛÏ×ÿ?(ÿ=&ÿ9$ÿ6"ÿ3 ÿ/ÿ,ÿ(ÿ%ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNMNÿEDEÿÿÿ¿ ÿJCHÿoinÿNHMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ#ÿ&ÿ)ÿ-ÿ1ÿ4!ÿ7#ÿ;%ÿ>'ÿA*ÿD,ÿG.ÿJ/ÿL1ÿN2ÿP3ÿQ4ÿS5ÿS5ÿ~Bhÿ³¦ÿéßåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÏ×ÿP9ÿB*ÿ>(ÿ;%ÿ7#ÿ4!ÿ1ÿ-ÿ*ÿ&ÿ#ÿ ÿÿÿ ÿ ÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿONOÿFEFÿÿÿ¿  ÿKDJÿpkoÿOHNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ ÿÿÿÿ ÿ$ÿ'ÿ+ÿ/ÿ2ÿ6"ÿ9$ÿ='ÿ@)ÿD+ÿG-ÿI/ÿL1ÿO3ÿQ4ÿh"Nÿ”aÿɰÀÿôïòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôïòÿ¦€˜ÿJ/ÿG-ÿD+ÿA)ÿ='ÿ9$ÿ6"ÿ2ÿ/ÿ+ÿ(ÿ$ÿ!ÿÿÿÿ ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿONOÿFEFÿÿÿ¿  ÿMFKÿqlpÿOHMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ%ÿ)ÿ,ÿ0ÿ4!ÿ7#ÿ;&ÿ?(ÿB*ÿF-ÿI/ÿL1ÿZ?ÿ’a€ÿÞÏÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿʰÀÿ”aÿR4ÿO3ÿL1ÿI/ÿF-ÿC*ÿ?(ÿ<&ÿ7#ÿ4!ÿ1ÿ-ÿ)ÿ&ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿPOPÿFEFÿÿÿ¿  ÿNHMÿrmqÿOINÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ#ÿ&ÿ*ÿ.ÿ2ÿ5"ÿ9$ÿ='ÿA)ÿD,ÿH.ÿV=ÿ¦€˜ÿôïòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôïòÿ ¶ÿ™a„ÿdFÿY9ÿV8ÿT6ÿR4ÿO2ÿK0ÿH.ÿE,ÿA)ÿ='ÿ9$ÿ6"ÿ2ÿ.ÿ*ÿ'ÿ#ÿ ÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿPOPÿGFGÿ  ÿÿ¿ ÿPJOÿsnqÿOIOÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿ$ÿ'ÿ+ÿ0ÿ3 ÿ7#ÿ;%ÿ?(ÿC*ÿF-ÿk1VÿóïòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÏÚÿ¯žÿ}2bÿ_>ÿ_=ÿ^=ÿ]<ÿ[;ÿY:ÿW8ÿT6ÿQ4ÿN2ÿJ0ÿG-ÿC+ÿ?(ÿ;&ÿ7#ÿ3 ÿ0ÿ+ÿ(ÿ$ÿ ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQPQÿHGHÿ  ÿÿ¿ ÿZTXÿtosÿPIOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ%ÿ)ÿ-ÿ1ÿ5!ÿ9$ÿ='ÿA)ÿE,ÿ_!Hÿóïòÿÿÿÿÿÿÿÿÿÿÿÿÿêßæÿ®ÿs"Uÿ`>ÿa?ÿb@ÿc@ÿb@ÿa?ÿ`>ÿ_=ÿ\<ÿZ:ÿW8ÿT6ÿP4ÿL1ÿI/ÿE,ÿA*ÿ='ÿ9$ÿ5!ÿ1ÿ-ÿ)ÿ&ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQPQÿHGHÿ  ÿÿ¿ ÿ^X]ÿuptÿPJOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ#ÿ'ÿ*ÿ.ÿ3 ÿ7#ÿ;%ÿ?(ÿC+ÿG-ÿ» ±ÿÿÿÿÿÿÿÿÿÿÿÿÿ¢qÿ\<ÿ_=ÿa?ÿc@ÿeAÿfBÿfBÿfBÿeAÿc@ÿa?ÿ_>ÿ]<ÿZ:ÿV8ÿS5ÿO3ÿK0ÿG.ÿC+ÿ?(ÿ;%ÿ7#ÿ3 ÿ/ÿ+ÿ'ÿ#ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRQRÿHGHÿ!!ÿÿ¿ ÿ_Y^ÿvquÿPJOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿ$ÿ(ÿ+ÿ0ÿ4!ÿ8$ÿ<&ÿ@)ÿE,ÿI/ÿÒ¿ËÿÿÿÿÿÿÿÿÿQwÿ\;ÿ_=ÿb?ÿdAÿfCÿhCÿiDÿiDÿiDÿhCÿfCÿeAÿb?ÿ_>ÿ\<ÿY9ÿU7ÿQ4ÿM1ÿI/ÿE,ÿA)ÿ<'ÿ8$ÿ4!ÿ0ÿ,ÿ(ÿ$ÿ ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSRSÿIHIÿ!!ÿÿ¿Ÿ ÿa[`ÿwruÿQJOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ ÿÿÿÿ!ÿ%ÿ)ÿ-ÿ1ÿ5!ÿ9$ÿ>'ÿB*ÿF-ÿJ0ÿÒ¿Ëÿÿÿÿÿÿÿÿÿ˰ÁÿhIÿa?ÿeAÿgCÿiDÿkEÿlFÿlFÿlFÿkEÿiDÿhCÿeBÿb?ÿ_=ÿ[;ÿW8ÿS5ÿO3ÿK0ÿG-ÿB*ÿ>(ÿ:%ÿ5"ÿ1ÿ-ÿ)ÿ%ÿ!ÿÿÿÿ ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSRSÿIHIÿ! !ÿÿ¿¿ ÿb\aÿxsvÿQKPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ&ÿ*ÿ.ÿ2ÿ6"ÿ:%ÿ?(ÿD+ÿH.ÿL1ÿœpŒÿÿÿÿÿÿÿÿÿÿÿÿÿõïóÿÄ ·ÿ—R~ÿjEÿlGÿnHÿoHÿpIÿoHÿnHÿlGÿjEÿhCÿeAÿa?ÿ]<ÿY:ÿU7ÿQ4ÿL1ÿH.ÿD+ÿ?(ÿ;%ÿ7#ÿ2 ÿ.ÿ*ÿ&ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTSTÿJIJÿ! !ÿÿ¿¿ÿd^cÿytwÿQKPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ#ÿ&ÿ*ÿ/ÿ3 ÿ7#ÿ<&ÿA)ÿE,ÿI/ÿN2ÿR5ÿ¿ ´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõïóÿÒ°Æÿ¸¤ÿ•BwÿsKÿrKÿqJÿpIÿmGÿkEÿgCÿdAÿ_>ÿ[;ÿW8ÿS5ÿN2ÿJ/ÿE,ÿA)ÿ<&ÿ8#ÿ3 ÿ/ÿ+ÿ'ÿ#ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTSTÿJIJÿ" "ÿÿ¿¿ÿe_dÿyuwÿQKPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿ#ÿ'ÿ+ÿ0ÿ4!ÿ8$ÿ='ÿB*ÿG-ÿK0ÿP3ÿT6ÿY9ÿša…ÿëßçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÏÝÿ‘°ÿ§bŽÿ‹3kÿmGÿjEÿfBÿb?ÿ^=ÿY:ÿT6ÿP4ÿK0ÿG-ÿB*ÿ='ÿ9$ÿ4!ÿ0ÿ,ÿ(ÿ$ÿ ÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUTUÿKJKÿ" "ÿÿ¿¿ÿgafÿzvxÿQKPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿ$ÿ(ÿ,ÿ1ÿ5"ÿ:%ÿ>(ÿC+ÿH.ÿL1ÿQ4ÿV7ÿ[;ÿ_>ÿdAÿ—R~ÿÈ ºÿöïóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚÀÐÿ½‘­ÿ”R|ÿiJÿ[;ÿV8ÿR5ÿM1ÿH.ÿC+ÿ>(ÿ:%ÿ5"ÿ1ÿ,ÿ)ÿ$ÿ ÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUTUÿKJKÿ"!"ÿÿ¿¿ÿhbgÿ{vyÿRLQÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ%ÿ)ÿ-ÿ2ÿ6"ÿ;%ÿ?(ÿD,ÿI/ÿN2ÿS5ÿW9ÿ]<ÿa?ÿfBÿjEÿoHÿsKÿ3nÿ³qœÿÖ°Èÿîßéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ̰Âÿ–aƒÿ^BÿN3ÿI/ÿE,ÿ@)ÿ;&ÿ6#ÿ2ÿ-ÿ)ÿ%ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿVUVÿLKLÿ"!"ÿÿ¿¿ÿidhÿ|x{ÿRLQÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ&ÿ*ÿ.ÿ2 ÿ7#ÿ<&ÿA)ÿF-ÿJ0ÿO3ÿT6ÿY:ÿ^=ÿc@ÿhDÿmGÿqJÿuMÿxOÿ|Qÿ~RÿSÿˆ^ÿ§R‰ÿ¿©ÿÞÀÓÿöïôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôïòÿ²¥ÿV=ÿF-ÿA)ÿ<&ÿ7#ÿ3 ÿ.ÿ*ÿ&ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿVUVÿLKLÿ"!"ÿÿ¿¿ÿjeiÿ}x{ÿSMRÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿ ÿÿÿÿ"ÿ&ÿ*ÿ/ÿ3 ÿ8$ÿ='ÿB*ÿG-ÿK0ÿQ4ÿV7ÿ[;ÿ`>ÿeBÿjEÿoHÿtLÿwNÿ{QÿSÿTÿ‚UÿƒVÿ‚UÿTÿSÿ|Qÿ‰#eÿ¨bÿÉ »ÿìßèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóïòÿi1TÿB*ÿ='ÿ8$ÿ4 ÿ/ÿ+ÿ&ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿWVWÿMLMÿ#!#ÿÿ¿¿ÿlfjÿ}y|ÿSMRÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ#ÿ'ÿ+ÿ0ÿ4!ÿ9$ÿ>'ÿC+ÿH.ÿM1ÿR5ÿW8ÿ]<ÿb?ÿgCÿlGÿqJÿvMÿzPÿ~SÿUÿ„Vÿ…Xÿ†Xÿ…Xÿ„Vÿ‚UÿSÿ{PÿvMÿrJÿmGÿ{#[ÿ±ŸÿëßæÿÿÿÿÿÿÿÿÿÿÿÿÿóïñÿO8ÿ>(ÿ9$ÿ4!ÿ0ÿ+ÿ'ÿ#ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿWVWÿMLMÿ#!#ÿÿ¿¿ÿmhkÿ~z}ÿSMRÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ#ÿ'ÿ+ÿ0ÿ5!ÿ9%ÿ>(ÿD+ÿI/ÿN2ÿS6ÿY9ÿ^=ÿc@ÿiDÿnHÿsKÿxNÿ}RÿTÿ„Wÿ‡Yÿ‰Zÿ‰Zÿ‰Zÿ‡Yÿ„WÿUÿ}RÿxOÿsLÿoHÿiDÿdAÿ^=ÿ¢qÿÿÿÿÿÿÿÿÿÿÿÿÿ•p‡ÿ?(ÿ:%ÿ5"ÿ1ÿ,ÿ(ÿ#ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿXWXÿNMNÿ#"#ÿÿ¿¿ÿnimÿ|ÿTNRÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿ$ÿ(ÿ,ÿ1ÿ6"ÿ:%ÿ?(ÿE,ÿJ/ÿO3ÿT6ÿZ:ÿ_>ÿeBÿjEÿpIÿuMÿzPÿSÿƒVÿ‡YÿŠ[ÿ‹\ÿŒ\ÿ‹\ÿŠ[ÿ‡YÿƒVÿ€Tÿ{PÿuMÿqIÿkEÿeBÿ_>ÿZ:ÿ¿ ´ÿÿÿÿÿÿÿÿÿ¡€•ÿ@)ÿ;%ÿ6"ÿ1ÿ,ÿ(ÿ$ÿ ÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿXWXÿNMNÿ#"#ÿÿ¿¿ÿojnÿ€|ÿTNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿ$ÿ(ÿ,ÿ2ÿ6"ÿ;&ÿ@)ÿE,ÿJ0ÿP3ÿU7ÿ[;ÿ`>ÿfBÿlFÿrJÿwNÿ}Rÿ‚Uÿ†XÿŠ[ÿ]ÿ^ÿ_ÿ^ÿ]ÿŠ[ÿ†Xÿ‚Uÿ}RÿwNÿrJÿlGÿgCÿ~2cÿÁ µÿÿÿÿÿÿÿÿÿÿÿÿÿŠ`{ÿ@)ÿ;&ÿ6#ÿ2ÿ-ÿ)ÿ$ÿ ÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿYXYÿONOÿ$"$ÿÿ¿¿ÿpkoÿ}€ÿTOSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿ ÿ%ÿ)ÿ-ÿ2 ÿ7#ÿ<&ÿA)ÿF-ÿK0ÿQ4ÿV8ÿ\;ÿb?ÿhCÿnGÿsKÿyOÿSÿƒVÿˆZÿŒ\ÿ_ÿ’`ÿ“aÿ’`ÿ_ÿ\ÿˆZÿ„VÿSÿŠ#eÿŸRƒÿ¿‘®ÿâÏÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèßåÿR:ÿA*ÿ<&ÿ7#ÿ2 ÿ.ÿ)ÿ%ÿ!ÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿYXYÿONOÿ$"$ÿÿ¿¿ÿqlpÿ‚~ÿUOTÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿ!ÿ%ÿ)ÿ.ÿ2 ÿ7#ÿ<&ÿA*ÿG-ÿL1ÿR4ÿW8ÿ]<ÿc@ÿiDÿoHÿtLÿzPÿ€Tÿ…XÿŠ[ÿ^ÿ“aÿ•cÿ–cÿœlÿ­CˆÿÀq¤ÿÄ­ÿàÀÕÿ÷ïôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÏÙÿb!KÿG.ÿB*ÿ<'ÿ7#ÿ3 ÿ.ÿ)ÿ%ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZYZÿONOÿ$"$ÿÿ¿¿ÿrmqÿƒ‚ÿUOTÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ%ÿ*ÿ.ÿ3 ÿ8#ÿ='ÿB*ÿG.ÿM1ÿR5ÿX9ÿ^=ÿdAÿjEÿpIÿvMÿ|Qÿ‚Uÿ3xÿ°RÿȯÿסÄÿåÀØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ̰Âÿ—aƒÿS5ÿM1ÿH.ÿB*ÿ='ÿ8$ÿ3 ÿ.ÿ*ÿ&ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[Z[ÿPOPÿ$#$ÿÿ¿¿ÿsnsÿ„€ƒÿVPUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ&ÿ*ÿ/ÿ4!ÿ8$ÿ>'ÿC+ÿH.ÿM2ÿS6ÿY9ÿ_=ÿeAÿtQÿR‚ÿѱÿÞÀÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÏÝÿÁ‘°ÿ™R€ÿoNÿ_>ÿY:ÿT6ÿN2ÿH/ÿC+ÿ>(ÿ9$ÿ4!ÿ/ÿ+ÿ&ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\Z\ÿQOQÿ%#%ÿÿ¿¿ÿtosÿ„„ÿVPUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ&ÿ+ÿ/ÿ4!ÿ9$ÿ>(ÿC+ÿH/ÿN2ÿT6ÿcFÿ›a†ÿâÏÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìÐâÿÞ±Îÿȯÿ¶b™ÿ›3vÿSÿyOÿrKÿlGÿfBÿ`>ÿZ:ÿT6ÿN3ÿI/ÿD+ÿ>(ÿ9$ÿ4"ÿ0ÿ+ÿ'ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\Z\ÿRPRÿ&#&ÿÿ¿¿ÿuptÿ…„ÿWQVÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ'ÿ+ÿ0ÿ4!ÿ9$ÿ>(ÿD+ÿI/ÿN3ÿžqŽÿôïòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøïõÿãÀ×ÿÒ’»ÿ΂³ÿ¹Dÿ®%~ÿ¡kÿhÿ˜dÿ’`ÿŒ\ÿ†Xÿ€TÿyPÿsKÿmGÿfCÿ`>ÿZ:ÿT6ÿO3ÿI/ÿD,ÿ>(ÿ9%ÿ5"ÿ0ÿ+ÿ'ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ][]ÿRPRÿ&#&ÿÿ¿¿ÿuptÿ…‚„ÿWQVÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ'ÿ+ÿ0ÿ5"ÿ9%ÿ>(ÿD,ÿI/ÿ¼ ²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÏÝÿÄ‘²ÿ¨R‰ÿ–#mÿ]ÿ“aÿ™eÿŸiÿ£lÿ¦nÿ¤mÿŸiÿšeÿ“aÿ]ÿ‡Yÿ€TÿzPÿtLÿmGÿgCÿa?ÿ[;ÿU7ÿO3ÿI/ÿD,ÿ?(ÿ9%ÿ5"ÿ0ÿ+ÿ'ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^\^ÿSQSÿ&$&ÿÿ¿¿ÿuptÿ†‚…ÿXQWÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ'ÿ+ÿ0ÿ5"ÿ9%ÿ>(ÿD,ÿ`|ÿÿÿÿÿÿÿÿÿÿÿÿÿõïóÿ³¡ÿ€#^ÿtLÿzPÿTÿ‡Yÿ]ÿ”bÿšfÿ jÿ¦nÿ¨pÿ¦nÿ¡jÿšfÿ”bÿŽ]ÿ‡YÿTÿ{PÿtLÿnHÿgCÿa?ÿ[;ÿU7ÿO3ÿJ/ÿD,ÿ?(ÿ:%ÿ5"ÿ0ÿ,ÿ'ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^\^ÿSQSÿ&$&ÿÿ¿¿ÿvquÿ†ƒ…ÿYRXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ'ÿ+ÿ0ÿ5"ÿ9%ÿ?(ÿD,ÿů½ÿÿÿÿÿÿÿÿÿÖÀÎÿkKÿgCÿnHÿtLÿzPÿTÿ‡YÿŽ]ÿ”bÿšfÿ¡jÿ§oÿ«qÿ§oÿ¡kÿ›fÿ•bÿŽ^ÿˆYÿUÿ{QÿtLÿnHÿgCÿa?ÿ[;ÿU7ÿO3ÿJ0ÿD,ÿ?(ÿ:%ÿ5"ÿ0ÿ,ÿ'ÿ#ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^\^ÿTRTÿ&$&ÿÿ¿¿ÿvrvÿ†ƒ…ÿYRXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ'ÿ+ÿ0ÿ5"ÿ9%ÿ?(ÿD,ÿÑ¿Êÿÿÿÿÿÿÿÿÿ£q‘ÿa?ÿgCÿnHÿtLÿzPÿTÿ‡YÿŽ]ÿ”bÿšfÿ¡kÿ§oÿ«qÿ§oÿ¡kÿ›fÿ•bÿŽ^ÿˆYÿUÿ{QÿtLÿnHÿgCÿa?ÿ[;ÿU7ÿO3ÿJ0ÿD,ÿ?(ÿ:%ÿ5"ÿ0ÿ,ÿ'ÿ#ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_]_ÿTRTÿ'$'ÿÿ¿¿ÿvrvÿ†ƒ†ÿZSYÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ'ÿ+ÿ0ÿ5"ÿ9%ÿ?(ÿD,ÿ˜p‰ÿÿÿÿÿÿÿÿÿÿÿÿÿÄ ·ÿz#[ÿmGÿtLÿzPÿTÿ‡Yÿ]ÿ”bÿšfÿ jÿ¦nÿ©pÿ¦nÿ¡jÿšfÿ”bÿŽ^ÿ‡YÿUÿ{PÿtLÿnHÿgCÿa?ÿ[;ÿU7ÿO3ÿJ/ÿD,ÿ?(ÿ:%ÿ5"ÿ0ÿ,ÿ'ÿ#ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_^_ÿUSUÿ'$'ÿÿ¿¿ÿwrvÿ‡„†ÿZSYÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ'ÿ+ÿ0ÿ5"ÿ9%ÿ>(ÿD,ÿT<ÿèßåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÏÜÿ°qšÿ“3qÿ€Tÿ‡Xÿ]ÿ“aÿ™eÿŸiÿ¤mÿ¦nÿ¤mÿŸiÿšfÿ”aÿ]ÿ‡YÿTÿzPÿtLÿmGÿgCÿa?ÿ[;ÿU7ÿO3ÿI/ÿD,ÿ?(ÿ9%ÿ5"ÿ0ÿ+ÿ'ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`^`ÿUSUÿ'%'ÿÿ¿¿ÿwrvÿ‡„‡ÿZTYÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ'ÿ+ÿ0ÿ4!ÿ9$ÿ>(ÿD+ÿI/ÿd!LÿÞÏÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÀÕÿÍ‘¸ÿ»bœÿ²D‹ÿhÿ¡kÿ£lÿ¢kÿhÿ˜eÿ’aÿŒ\ÿ†Xÿ€TÿyPÿsKÿmGÿfCÿ`>ÿ[;ÿU7ÿO3ÿI/ÿD,ÿ?(ÿ9%ÿ5"ÿ0ÿ+ÿ'ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`^`ÿVTVÿ'%'ÿÿ¿¿ÿwrvÿ‡„‡ÿ[TZÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ&ÿ+ÿ/ÿ4!ÿ9$ÿ>(ÿC+ÿI/ÿN2ÿT6ÿ—aƒÿáÏÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùïõÿçÀÚÿÕ’½ÿÍ‚³ÿ±DŠÿŸ#sÿ‹[ÿ…WÿSÿyOÿrKÿlGÿfBÿ`>ÿZ:ÿT6ÿN3ÿI/ÿD+ÿ>(ÿ9$ÿ4"ÿ0ÿ+ÿ'ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`_`ÿVTVÿ'%'ÿÿ¿¿ÿwrvÿˆ„‡ÿ[UZÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ&ÿ*ÿ/ÿ4!ÿ8$ÿ>'ÿC+ÿH.ÿM2ÿS6ÿY:ÿ_=ÿx#Yÿ«q–ÿÒ°ÆÿöïóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÀÕÿΠ¾ÿ²q›ÿŒ3lÿkFÿfBÿ_>ÿY:ÿT6ÿN2ÿI/ÿC+ÿ>(ÿ9$ÿ4!ÿ/ÿ+ÿ'ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿa`aÿVTVÿ(%(ÿÿ¿¿ÿvquÿˆ„‡ÿ[U[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ&ÿ*ÿ/ÿ4!ÿ8$ÿ='ÿB+ÿH.ÿM2ÿS5ÿX9ÿ^=ÿdAÿjEÿpIÿvMÿ”3rÿ©RŠÿìÿâÀÖÿñßëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõïóÿΰÃÿša†ÿY9ÿS6ÿM2ÿH.ÿC+ÿ>'ÿ8$ÿ4!ÿ/ÿ*ÿ&ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿb`bÿWUWÿ(%(ÿÿ¿¿ÿvpuÿˆ„‡ÿ\V[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ&ÿ*ÿ.ÿ3!ÿ8$ÿ='ÿB*ÿG.ÿL1ÿR5ÿW9ÿ]=ÿc@ÿiDÿoIÿuMÿ{PÿTÿ†Xÿ‹[ÿ_ÿ“aÿ©4€ÿ°DŠÿÊ‚°ÿÖ¡ÃÿãÀÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôïòÿqÿM2ÿH.ÿB+ÿ='ÿ8$ÿ3!ÿ/ÿ*ÿ&ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿbabÿWUWÿ(&(ÿÿ¿¿ÿvpuÿˆ„‡ÿ]V\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ%ÿ)ÿ.ÿ2 ÿ7#ÿ<&ÿA*ÿG-ÿL1ÿQ4ÿW8ÿ\<ÿb@ÿhDÿnHÿsLÿyOÿSÿ„Vÿ‰Zÿ]ÿ_ÿ’aÿ“aÿ’aÿ‘_ÿ]ÿ‰Zÿ“#lÿŸB~ÿ¼§ÿÓ°Çÿöïóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ» ±ÿG-ÿB*ÿ='ÿ7#ÿ3 ÿ.ÿ*ÿ&ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿbabÿXVXÿ(&(ÿÿ¿¿ÿvpuÿˆ„‡ÿ]W]ÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ%ÿ)ÿ.ÿ2 ÿ7#ÿ;&ÿA)ÿF-ÿK0ÿP4ÿV7ÿ[;ÿa?ÿgCÿlGÿrKÿwNÿ}Rÿ‚Uÿ†XÿŠ[ÿ]ÿ_ÿ_ÿ_ÿ]ÿ‹[ÿ†Xÿ‚Uÿ}RÿxNÿrKÿmGÿƒ3fÿº‘«ÿôïòÿÿÿÿÿÿÿÿÿÿÿÿÿtAaÿA*ÿ<&ÿ7#ÿ2 ÿ.ÿ)ÿ%ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿcbcÿXVXÿ(&(ÿÿ¿¿ÿuotÿˆ„ˆÿ^W]ÿ!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ%ÿ)ÿ-ÿ2 ÿ6#ÿ;&ÿ@)ÿE-ÿJ0ÿO3ÿU7ÿZ;ÿ`>ÿfBÿkFÿpJÿvMÿ{Qÿ€Tÿ„WÿˆYÿ‹[ÿŒ\ÿ]ÿŒ\ÿ‹[ÿˆYÿ„Wÿ€Tÿ{QÿvMÿqJÿkFÿfBÿ`?ÿeGÿßÏÙÿÿÿÿÿÿÿÿÿ¡€•ÿA)ÿ;&ÿ7#ÿ2 ÿ-ÿ)ÿ%ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿdbdÿYWYÿ)&)ÿÿ¿¿ÿvotÿˆ„ˆÿ_X^ÿ!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ$ÿ)ÿ-ÿ1 ÿ6"ÿ;&ÿ@)ÿE,ÿJ0ÿO3ÿT6ÿY:ÿ_=ÿdAÿjEÿoHÿtLÿyOÿ}RÿUÿ„WÿˆYÿ‰ZÿŠZÿŠZÿˆYÿ„WÿUÿ~RÿyOÿtLÿoIÿjEÿeAÿ_>ÿdGÿßÏÙÿÿÿÿÿÿÿÿÿ¡€•ÿ@)ÿ;&ÿ6#ÿ2 ÿ-ÿ)ÿ%ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿeceÿZWZÿ*'*ÿÿ¿¿ÿuntÿˆƒ‡ÿ`Y_ÿ!!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ ÿ$ÿ(ÿ,ÿ1ÿ5"ÿ:%ÿ?(ÿD,ÿI/ÿN2ÿS6ÿX9ÿ]=ÿc@ÿhDÿmGÿrKÿvNÿ{QÿSÿ‚Uÿ„Wÿ†Xÿ‡Yÿ†Xÿ„Wÿ‚UÿSÿ{QÿwNÿrKÿmGÿhDÿw#Xÿ¯žÿôïòÿÿÿÿÿÿÿÿÿÿÿÿÿ[!Fÿ?)ÿ:%ÿ5"ÿ1ÿ,ÿ)ÿ$ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿeceÿZXZÿ*'*ÿÿ¿¿ÿtmsÿˆƒ‡ÿ`Y_ÿ""ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ ÿ#ÿ(ÿ,ÿ0ÿ4!ÿ9%ÿ>(ÿC+ÿH.ÿM2ÿR5ÿW8ÿ\<ÿa?ÿfBÿkFÿpIÿtLÿxOÿ|QÿSÿUÿƒVÿƒVÿƒVÿUÿTÿ|QÿxOÿuLÿ”Bwÿµ£ÿâÏÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ—p‰ÿC+ÿ>(ÿ9%ÿ5"ÿ0ÿ,ÿ(ÿ$ÿ ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfdfÿ[X[ÿ*'*ÿÿ¿¿ÿtmrÿˆƒ‡ÿaZ_ÿ""ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿ#ÿ'ÿ+ÿ/ÿ4!ÿ8$ÿ='ÿB*ÿG.ÿK1ÿP4ÿU7ÿZ;ÿ_>ÿdAÿiEÿnHÿrKÿvMÿyPÿ|Qÿ~Sÿ€Tÿ€Tÿ€Tÿ#iÿ¥Rˆÿ¼§ÿÝÀÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôïòÿƒQqÿG.ÿB*ÿ='ÿ8$ÿ4!ÿ/ÿ+ÿ'ÿ#ÿ ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfdfÿ[X[ÿ*'*ÿÿ¿¿ÿslrÿˆ‚‡ÿaZ`ÿ##ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ&ÿ*ÿ.ÿ3 ÿ7#ÿ<&ÿA)ÿE-ÿJ0ÿO3ÿT6ÿY:ÿ^=ÿb@ÿgCÿkFÿpIÿsKÿvNÿZÿ¤R‡ÿ¾¨ÿßÀÔÿ÷ïôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÏÙÿ”aÿO3ÿK0ÿF-ÿA*ÿ<'ÿ7$ÿ3!ÿ/ÿ*ÿ'ÿ#ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgegÿ[Y[ÿ*'*ÿÿ¿¿ÿslqÿˆ‚†ÿaZ`ÿ##ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ%ÿ*ÿ-ÿ2 ÿ6#ÿ;&ÿ?)ÿD,ÿI/ÿM2ÿR5ÿW8ÿ[;ÿ`?ÿeAÿiEÿ‘BuÿÁ‘°ÿåÏÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõïóÿΰÃÿ›a†ÿp"TÿW9ÿS5ÿN2ÿI/ÿE,ÿ@)ÿ;&ÿ7#ÿ2 ÿ.ÿ*ÿ&ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgegÿ\Y\ÿ*'*ÿÿ¿¿ÿrkpÿˆ‚†ÿa[aÿ##ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ%ÿ)ÿ-ÿ1ÿ5"ÿ:%ÿ>(ÿC+ÿH.ÿL1ÿQ4ÿU7ÿZ:ÿhIÿ±ŸÿëßçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÀÒÿÀ‘¯ÿ¤bŒÿ}#]ÿgCÿc@ÿ_=ÿZ:ÿU7ÿQ4ÿL2ÿH.ÿC+ÿ?(ÿ:%ÿ6"ÿ1 ÿ-ÿ)ÿ%ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿhfhÿ\Z\ÿ+(+ÿÿ¿¿ÿqjpÿˆ‚†ÿc\bÿ# #ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ ÿ$ÿ(ÿ,ÿ0ÿ4!ÿ9$ÿ='ÿB*ÿF-ÿK0ÿO3ÿS6ÿŒQwÿôïòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäÏÝÿÊ »ÿ§bŽÿ3mÿrKÿqJÿnHÿkFÿhDÿdAÿa?ÿ\<ÿX9ÿT6ÿO3ÿK0ÿF-ÿB*ÿ='ÿ9%ÿ5!ÿ0ÿ,ÿ(ÿ$ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿhfhÿ]Z]ÿ+(+ÿÿ¿¿ ÿpioÿˆ‚‡ÿd]cÿ$ $ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ ÿ#ÿ'ÿ+ÿ/ÿ3!ÿ7#ÿ<&ÿ@)ÿE,ÿI/ÿM2ÿr1[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõïóÿÆ ¸ÿ¢b‹ÿvSÿoHÿpIÿpJÿpIÿoIÿmGÿkFÿiDÿeBÿb@ÿ^=ÿZ;ÿV8ÿR5ÿN2ÿI/ÿE,ÿ@)ÿ<'ÿ8$ÿ3!ÿ/ÿ+ÿ(ÿ#ÿ ÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿigiÿ][]ÿ+(+ÿÿ¿¿ ÿphnÿˆ‚‡ÿd]cÿ$ $ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿ"ÿ&ÿ*ÿ.ÿ2 ÿ6#ÿ:%ÿ?(ÿC+ÿH.ÿK1ÿȯ¿ÿÿÿÿÿÿÿÿÿôïòÿ¥q’ÿlLÿfBÿhDÿjEÿlFÿmGÿmGÿmGÿlGÿjEÿhDÿfBÿc@ÿ`>ÿ\<ÿX9ÿT6ÿP4ÿL1ÿH.ÿC+ÿ?)ÿ;&ÿ6#ÿ2 ÿ.ÿ*ÿ&ÿ#ÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿigiÿ^[^ÿ+(+ÿÿ¿ ÿognÿˆ‚†ÿe^cÿ$!$ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ"ÿ%ÿ)ÿ-ÿ1ÿ5"ÿ9%ÿ='ÿB*ÿF-ÿJ0ÿÒ¿ËÿÿÿÿÿÿÿÿÿQxÿ]<ÿ`>ÿc@ÿeBÿgCÿiDÿjEÿjEÿjEÿiDÿgCÿeBÿc@ÿ`?ÿ]<ÿY:ÿV8ÿR5ÿN3ÿJ0ÿF-ÿB*ÿ=(ÿ9%ÿ5"ÿ1 ÿ-ÿ)ÿ%ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿigiÿ^[^ÿ+(+ÿÿ¿ ÿngmÿˆ‚†ÿe^dÿ%!%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ$ÿ(ÿ,ÿ0ÿ4!ÿ8$ÿ<&ÿ@)ÿD,ÿH.ÿƯ¾ÿÿÿÿÿÿÿÿÿßÏÚÿeGÿ]=ÿ`>ÿb@ÿdAÿfBÿfCÿgCÿfCÿfBÿdAÿc@ÿ`?ÿ^=ÿZ;ÿW8ÿT6ÿP4ÿL1ÿH/ÿD,ÿ@)ÿ<'ÿ8$ÿ4!ÿ0ÿ,ÿ(ÿ%ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjhjÿ^\^ÿ,),ÿÿ¿ ÿmflÿ‡†ÿf_eÿ%!%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ ÿ#ÿ'ÿ+ÿ.ÿ2 ÿ6#ÿ:%ÿ>(ÿB+ÿF-ÿ‚Qpÿÿÿÿÿÿÿÿÿÿÿÿÿôïòÿ­ÿr"Uÿ`>ÿa?ÿc@ÿcAÿdAÿdAÿc@ÿa?ÿ`>ÿ^=ÿ[;ÿX9ÿU7ÿR5ÿN2ÿJ0ÿG-ÿC+ÿ?(ÿ:&ÿ7#ÿ3 ÿ/ÿ+ÿ'ÿ#ÿ ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿkikÿ_\_ÿ,),ÿÿ¿ ÿe]cÿ‡†ÿg_eÿ-)-ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ"ÿ&ÿ*ÿ-ÿ1ÿ5"ÿ9$ÿ<'ÿ@)ÿD,ÿH.ÿ» ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÏÚÿ¥q’ÿt"Vÿ`?ÿ`?ÿ`?ÿ`>ÿ_=ÿ]<ÿ[;ÿX9ÿU7ÿS5ÿO3ÿL1ÿH/ÿE,ÿA*ÿ='ÿ9%ÿ5"ÿ1 ÿ-ÿ*ÿ&ÿ#ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿkikÿ_]_ÿ,),ÿÿ¿ ÿ\TZÿ‡€…ÿg`fÿ525ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ%ÿ)ÿ,ÿ0ÿ3!ÿ7#ÿ;&ÿ?(ÿB+ÿF-ÿJ0ÿ„QqÿôïòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÏÚÿ¹ªÿQyÿ]<ÿ[;ÿZ:ÿX9ÿV7ÿS6ÿP4ÿM2ÿJ0ÿF-ÿC+ÿ?)ÿ;&ÿ7$ÿ4!ÿ0ÿ,ÿ)ÿ%ÿ"ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿkikÿ_]_ÿ,),ÿÿ¿ ÿZRYÿ‡€†ÿh`gÿ626ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ!ÿ$ÿ'ÿ+ÿ/ÿ2 ÿ6"ÿ9%ÿ='ÿA*ÿD,ÿH.ÿK0ÿY?ÿ‘`€ÿÞÏÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôïòÿ˰Áÿ–aƒÿU7ÿS6ÿQ4ÿN3ÿK0ÿH.ÿE,ÿA*ÿ=(ÿ:%ÿ6#ÿ2 ÿ/ÿ+ÿ(ÿ$ÿ!ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿljlÿ`]`ÿ,),ÿÿ¿ÿZQYÿ‡†ÿiahÿ626ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿ ÿ#ÿ'ÿ*ÿ.ÿ1ÿ5"ÿ8$ÿ;&ÿ?)ÿC+ÿF-ÿI/ÿL1ÿN3ÿQ4ÿh"OÿŸqŽÿßÏÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôïòÿ³¦ÿe!LÿL1ÿI/ÿF-ÿC+ÿ?)ÿ<'ÿ8$ÿ5"ÿ1 ÿ.ÿ*ÿ'ÿ#ÿ ÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmjmÿa^aÿ-)-ÿÿ¿  ÿYPXÿ‡€†ÿjahÿ737ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ"ÿ%ÿ)ÿ,ÿ0ÿ3!ÿ6#ÿ:%ÿ=(ÿ@*ÿD,ÿG.ÿI0ÿL1ÿN3ÿP4ÿR5ÿS6ÿ_Cÿ”a‚ÿ¿ ´ÿôïòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™pŠÿG.ÿD,ÿA*ÿ>(ÿ:%ÿ7#ÿ3!ÿ0ÿ,ÿ)ÿ%ÿ"ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnknÿa^aÿ.*.ÿÿ¿  ÿXOVÿ‡€†ÿkbiÿ848ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ!ÿ$ÿ'ÿ+ÿ.ÿ1 ÿ5"ÿ8$ÿ;&ÿ>(ÿB*ÿD,ÿG.ÿJ0ÿL1ÿN2ÿO3ÿQ4ÿR5ÿR5ÿR6ÿR5ÿr1[ÿ²¦ÿéßåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­¢ÿB+ÿ?(ÿ;&ÿ8$ÿ5"ÿ2 ÿ.ÿ+ÿ(ÿ$ÿ!ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnknÿb_bÿ.*.ÿÿ¿  ÿWNUÿ†€…ÿkcjÿ848ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿ ÿ#ÿ&ÿ)ÿ-ÿ0ÿ3!ÿ6#ÿ9%ÿ<'ÿ?)ÿB+ÿE,ÿG.ÿI/ÿK0ÿM2ÿN3ÿO3ÿP3ÿP4ÿP3ÿO3ÿN3ÿM2ÿŽ`~ÿèßåÿÿÿÿÿÿÿÿÿÿÿÿÿW!Dÿ='ÿ9%ÿ6#ÿ3!ÿ0ÿ-ÿ*ÿ&ÿ#ÿ ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnlnÿb_bÿ.*.ÿÿ¿  ÿVMTÿ†€…ÿkcjÿ848ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ"ÿ%ÿ(ÿ+ÿ.ÿ1 ÿ5"ÿ7$ÿ:%ÿ=(ÿ@)ÿC+ÿE,ÿG.ÿI/ÿJ0ÿK1ÿL1ÿM2ÿM2ÿM2ÿL1ÿK1ÿJ0ÿI/ÿS;ÿÜÏ×ÿÿÿÿÿÿÿÿÿž€“ÿ:&ÿ8$ÿ5"ÿ2 ÿ.ÿ+ÿ(ÿ%ÿ"ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿoloÿc`cÿ.*.ÿÿ¿  ÿTKSÿ†…ÿlckÿ848ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ!ÿ#ÿ&ÿ*ÿ-ÿ0ÿ3 ÿ6#ÿ8$ÿ;&ÿ>(ÿ@)ÿB+ÿE,ÿF-ÿH.ÿI/ÿJ0ÿJ0ÿJ0ÿJ0ÿJ0ÿI/ÿH/ÿF-ÿE,ÿį½ÿÿÿÿÿÿÿÿÿœ€’ÿ9$ÿ6#ÿ3!ÿ0ÿ-ÿ*ÿ'ÿ$ÿ!ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpmpÿc`cÿ.*.ÿÿ¿  ÿSJRÿ†…ÿmdlÿ959ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ"ÿ%ÿ(ÿ+ÿ.ÿ1ÿ4!ÿ6#ÿ9%ÿ;&ÿ>(ÿ@)ÿB+ÿD,ÿE,ÿF-ÿG.ÿH.ÿH/ÿH.ÿG.ÿF-ÿE-ÿP9ÿ¸ ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿj@[ÿ7#ÿ4!ÿ1ÿ.ÿ+ÿ(ÿ%ÿ"ÿ ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpmpÿc`cÿ.*.ÿÿ¿oÿRIQÿ†~„ÿmelÿ:5:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ!ÿ#ÿ&ÿ)ÿ,ÿ/ÿ2 ÿ4"ÿ7$ÿ9%ÿ;&ÿ>(ÿ?)ÿA*ÿB+ÿD,ÿD,ÿE,ÿE-ÿE,ÿE,ÿD,ÿ‰`{ÿçßäÿÿÿÿÿÿÿÿÿÿÿÿÿÍ¿Èÿ7$ÿ5"ÿ2 ÿ/ÿ,ÿ*ÿ'ÿ$ÿ!ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpmpÿc`cÿ/+/ÿÿ¿?ÿQHPÿ…~„ÿmelÿ:6:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ ÿ"ÿ%ÿ(ÿ+ÿ-ÿ0ÿ2 ÿ5"ÿ7$ÿ9%ÿ;&ÿ='ÿ?(ÿ@)ÿA*ÿB*ÿB+ÿC+ÿN8ÿ•p‡ÿçßäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÏÖÿD1ÿ5"ÿ2 ÿ0ÿ-ÿ+ÿ(ÿ%ÿ"ÿ ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqnqÿdadÿ/+/ÿÿ¿?ÿKBJÿ…}„ÿnfmÿ?:>ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿ!ÿ#ÿ&ÿ)ÿ+ÿ.ÿ1ÿ3!ÿ5"ÿ7$ÿ9%ÿ:&ÿ<'ÿ=(ÿ>(ÿ?)ÿL6ÿŸ€”ÿóïñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòïñÿœ€‘ÿ7$ÿ5"ÿ3!ÿ1ÿ.ÿ,ÿ)ÿ&ÿ$ÿ!ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqnqÿdadÿ/+/ÿÿ¿?ÿ<3;ÿ…}ƒÿognÿLHLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿ"ÿ$ÿ'ÿ*ÿ,ÿ/ÿ1ÿ3!ÿ5"ÿ7#ÿ8$ÿ:%ÿ;&ÿH4ÿž€“ÿòïñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòïñÿœ€’ÿE2ÿ7#ÿ5"ÿ3!ÿ1ÿ/ÿ,ÿ*ÿ'ÿ%ÿ"ÿ ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿrorÿebeÿ/+/ÿÿ¿?ÿ;19ÿ„}ƒÿognÿMHMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿ!ÿ#ÿ%ÿ(ÿ*ÿ-ÿ/ÿ1ÿ3!ÿ5"ÿ6#ÿ7$ÿj@[ÿòïñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæßãÿœ€’ÿE2ÿ7$ÿ6#ÿ5"ÿ3!ÿ1ÿ/ÿ-ÿ*ÿ(ÿ&ÿ#ÿ!ÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿrorÿebeÿ/+/ÿÿ¿?ÿ908ÿ„|ƒÿognÿMIMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿ"ÿ$ÿ&ÿ)ÿ+ÿ-ÿ/ÿ1ÿ2 ÿ4!ÿh@Yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæßãÿ‚`vÿ8$ÿ7$ÿ6#ÿ5"ÿ4!ÿ2 ÿ1ÿ/ÿ-ÿ+ÿ)ÿ&ÿ$ÿ"ÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿspsÿfcfÿ/+/ÿÿ¿?ÿ7.6ÿƒ{‚ÿphoÿNINÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿ ÿ"ÿ$ÿ'ÿ)ÿ+ÿ-ÿ/ÿ0ÿ2 ÿåßãÿÿÿÿÿÿÿÿÿÿÿÿÿš€‘ÿ6#ÿ6#ÿ6#ÿ5"ÿ4!ÿ3!ÿ2 ÿ1ÿ/ÿ-ÿ+ÿ)ÿ'ÿ$ÿ"ÿ ÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿspsÿfcfÿ0,0ÿÿ¿?ÿ5-4ÿƒ{ÿphoÿNJNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿ!ÿ#ÿ%ÿ'ÿ)ÿ+ÿ-ÿ.ÿpPdÿÿÿÿÿÿÿÿÿòïñÿY0Jÿ3!ÿ3!ÿ3!ÿ3!ÿ2 ÿ2 ÿ1ÿ0ÿ.ÿ-ÿ+ÿ)ÿ'ÿ%ÿ#ÿ!ÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtqtÿfcfÿ0,0ÿÿ¿?ÿ3+3ÿƒ{ÿqipÿOKOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿ!ÿ#ÿ%ÿ'ÿ)ÿ*ÿ,ÿ–€Žÿÿÿÿÿÿÿÿÿ¤ÿ1ÿ1ÿ1 ÿ1ÿ1ÿ1ÿ0ÿ.ÿ-ÿ,ÿ+ÿ)ÿ'ÿ%ÿ#ÿ!ÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtqtÿfcfÿ0,0ÿÿ¿?ÿ3*2ÿƒz‚ÿriqÿPKPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿ ÿ"ÿ$ÿ&ÿ(ÿ)ÿ*ÿˆpÿÿÿÿÿÿÿÿÿË¿Çÿ/ÿ/ÿ0ÿ/ÿ/ÿ/ÿ.ÿ-ÿ,ÿ*ÿ)ÿ(ÿ&ÿ$ÿ"ÿ ÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿuruÿgdgÿ1,1ÿÿ¿ÿ1(0ÿ‚zÿsjrÿQLQÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿ ÿ"ÿ$ÿ%ÿ'ÿ)ÿD 8ÿÿÿÿÿÿÿÿÿÿÿÿÿ|`rÿ-ÿ-ÿ-ÿ-ÿ,ÿ+ÿ+ÿ*ÿ)ÿ'ÿ%ÿ$ÿ"ÿ!ÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿuruÿhdhÿ1,1ÿÿ¿ÿ/&.ÿx€ÿsjrÿQLQÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿ!ÿ"ÿ$ÿ%ÿ&ÿ(ÿÉ¿Åÿÿÿÿÿÿÿÿÿÿÿÿÿ•€ÿ+ÿ+ÿ+ÿ*ÿ*ÿ)ÿ(ÿ'ÿ%ÿ$ÿ"ÿ!ÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿvsvÿhehÿ1,1ÿÿïAEI¿?DG¿¯37:ÿÿx€ÿtksÿRMRÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿ!ÿ"ÿ#ÿ%ÿ&ÿ4'ÿñïðÿÿÿÿÿÿÿÿÿÿÿÿÿÉ¿Æÿ6)ÿ)ÿ)ÿ(ÿ'ÿ&ÿ%ÿ#ÿ"ÿ!ÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿvsvÿieiÿ1,1ÿÿÿMRVÿJPTÿGLPÿ?CGÿ7;=Ï37:ÿÿ€xÿvltÿTOTÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ  ÿ  ÿ  ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿ!ÿ"ÿ$ÿ%ÿ%ÿO1DÿÖÏÓÿÿÿÿÿÿÿÿÿÿÿÿÿñïðÿP1Eÿ'ÿ&ÿ%ÿ%ÿ$ÿ"ÿ!ÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿvsvÿieiÿ2-2ÿÿÿU[_ÿRY]ÿOTYÿGMQÿ?CFÿ6:=Ï379ÿÿ€v~ÿvltÿTOTÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿ ÿ"ÿ#ÿ$ÿ$ÿ2&ÿ­Ÿ¨ÿÿÿÿÿÿÿÿÿÿÿÿÿñïðÿN1Cÿ$ÿ$ÿ#ÿ"ÿ!ÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿwtwÿieiÿ2-2ÿÿÿW^bÿV\aÿSY^ÿMSWÿFJNÿ;@Bÿ37:?ÿ ÿv~ÿvluÿUOTÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿ ÿ!ÿ"ÿ#ÿ#ÿ$ÿv`nÿñïðÿÿÿÿÿÿÿÿÿÈ¿Åÿ#ÿ"ÿ!ÿ ÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿwtwÿifiÿ2-2ÿÿÿW^bÿV]aÿU[`ÿQW\ÿJOSÿ?CFÿ59;?ÿ ÿ~u}ÿwmuÿVPUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ  ÿ  ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿ ÿ!ÿ!ÿ"ÿ"ÿYAPÿÿÿÿÿÿÿÿÿÿÿÿÿ=!2ÿ ÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ  ÿ  ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxuxÿjfjÿ2-2ÿÿÿW^bÿV\aÿU[`ÿRX]ÿKQTÿ?DGÿ59;?ÿ ÿ~t|ÿwnvÿVPVÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ  ÿ  ÿ  ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÇ¿ÄÿÿÿÿÿÿÿÿÿW@Oÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxuxÿjgjÿ2-2ÿÿÿU\`ÿTZ^ÿSY]ÿQW[ÿJPTÿ>CFÿ48:?Ïÿ}t|ÿxnvÿXRXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ÿ  ÿ  ÿ  ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ¿ÄÿÿÿÿÿÿÿÿÿVAOÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyvyÿjgjÿ2-2ÿÿÿSY]ÿRX\ÿQW[ÿOUYÿIMRÿ=AEÿ48:o7;>?8<@?:>A?;@C?CF??DG?AEI?AFJ?AFJ?AFJ?AFJ?AFJ?AEI?AEI?AEH?@EH?@EH??DH??CG?>CG?>CF?>CF?>CF?>CE?>BE?=BE?=AE?A?:>A?9=@?7;>?59;?????????? ?+.1?7;??=BE?;?B?6;=?8:CFÿ9=@ÿ ÿÿÿÿÿÿÿÿÿÿ.14ÿ=BEÿEJMÿAFIÿ;@Bÿ8;=ÿ;=>oZ\^_adf¿gjlÿnqsÿsvyÿvy|ÿvz|ÿuy{ÿruwÿmprÿehjÿ^acßX[]TVW¿  ÿg^eÿypwÿZTZÿ+%+ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÀÄÿÿÿÿÿÿÿÿÿTBNÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿzwzÿkhkÿ3.3ÿÿ**/ÿNTXÿMSVÿLRUÿKPSÿEJMÿ:>@ÿBGKÿJPTÿLRVÿNTXÿPVZÿRX\ÿSZ_ÿU\aÿW^cÿX^cÿX^cÿX^cÿX^cÿX^cÿW^cÿW]bÿW]aÿV\aÿV\aÿU[`ÿU[`ÿTZ_ÿTZ_ÿTZ^ÿSY^ÿSY^ÿSY]ÿRY]ÿRX\ÿQX\ÿQW[ÿQW[ÿPVZÿPVZÿOUZÿOUYÿOUXÿNTWÿMRVÿJPSÿFJNÿ>BEÿ ÿÿÿÿÿÿÿÿÿÿ157ÿDIMÿMSWÿHNRÿ@EHÿ9<>ÿ:<=ï:;<XZ\?^`bÏgjlÿqtvÿx{}ÿ}€‚ÿ€ƒ…ÿ…‡ÿ„‡ÿ€ƒ†ÿ~„ÿ{~€ÿvxzÿortÿgikÿ^`bÿXZ\ŸSUW¿  ÿf]eÿzpxÿ[T[ÿ,%,ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÀÄÿÿÿÿÿÿÿÿÿTBNÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿzwzÿkhkÿ3.3ÿÿ))-ÿLQUÿKPSÿJORÿHMPÿCGJÿ8<>ÿEJNÿNTXÿPV[ÿRX]ÿTZ_ÿV]aÿX_cÿZafÿ\chÿ\chÿ\chÿ\chÿ\chÿ\chÿ\chÿ[bgÿ[bgÿ[afÿZafÿY`eÿY`eÿY_dÿX_dÿX_cÿX^cÿW^cÿW^bÿW]bÿV\aÿU\aÿU\`ÿU[`ÿT[_ÿTZ_ÿTZ_ÿSZ^ÿSY^ÿSY]ÿRX\ÿPUYÿJOSÿ@EHÿÿÿÿÿÿÿÿÿÿÿ37:ÿHNRÿRX]ÿMSWÿCHKÿ9<>ÿ;<>ÿ:;<Ï89;QSTVXZŸbdgÿnpsÿvy|ÿ}‚ÿ€ƒ†ÿƒ†‰ÿƒ†‰ÿƒ†‰ÿƒ†‰ÿ‚…ˆÿ„‡ÿ€ƒ†ÿ}€ƒÿy|ÿtwyÿnpsÿfhjÿ\^`ÿSUWŸMNP¿  ÿf\eÿ{qyÿ]V]ÿ-'-ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÀÄÿÿÿÿÿÿÿÿÿSBNÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ{x{ÿlhlÿ3.3ÿÿ((,ÿJORÿINQÿHMOÿFKNÿ@EHÿ6:<ÿDILÿMSWÿOUZÿQW\ÿSY^ÿU[`ÿW^bÿY`dÿ[bgÿ[bgÿ[bgÿ[bgÿ[bgÿ[bgÿ[bgÿZafÿZ`eÿZ`eÿY`eÿY_dÿX_dÿX_cÿW^cÿW^bÿW]bÿV]aÿV\aÿV\aÿU[`ÿU[`ÿT[_ÿTZ_ÿTZ^ÿSY^ÿSY^ÿRX]ÿRX\ÿRX\ÿRX\ÿQW[ÿKPTÿAEIÿÿÿÿÿÿÿÿÿÿÿ37:ÿJPTÿU[`ÿOUYÿDIMÿ9;=ÿ<>?ÿ=>?ÿ9:;ŸNOQOVXZïdghÿqsuÿy{}ÿ~€ƒÿƒ†ÿ„†ÿ„†ÿ€ƒ…ÿ€ƒ…ÿ‚„ÿ„ÿ~ƒÿ}€‚ÿ}€‚ÿ|~€ÿz}ÿvy{ÿqtvÿkmoÿ`bdÿUVXÿMNPIJK¿  ÿd[cÿ{rzÿ^W^ÿ.(.ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÀÄÿÿÿÿÿÿÿÿÿSCNÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ{x{ÿlilÿ3.3ÿÿ'&+ÿGLOÿFKNÿEJMÿDHKÿ>CEÿ48:ÿBFJÿKPTÿLRVÿNTXÿPVZÿRX\ÿTZ^ÿV\aÿX^cÿX^cÿX^cÿX^cÿX^cÿX^cÿX^cÿW]bÿW]aÿV\aÿV\aÿV[`ÿU[`ÿU[_ÿTZ_ÿTZ_ÿTY^ÿSY^ÿSX]ÿRX]ÿRX\ÿRW\ÿQW\ÿQW[ÿQV[ÿPVZÿPUZÿOUYÿOTYÿOTYÿOTYÿNTXÿINRÿ?CGÿ ÿÿÿÿÿÿÿÿÿÿ369ÿJNRÿTZ^ÿNTXÿCGKÿ7:<ÿ=?@ÿABCÿ>?@ÿ9;<¯89:EGHLNP¿XZ\ÿegiÿpsuÿx{}ÿ|~ÿ}€‚ÿ}€ƒÿ}‚ÿ|‚ÿ|~ÿ{~€ÿ{}€ÿz}ÿz|ÿy{~ÿx{~ÿxz}ÿwz|ÿwy{ÿuwzÿqtvÿkmoÿbdfÿYZ\ÿNPQïHIJ_¿ÿcZbÿ|rzÿ`X_ÿ/).ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÀÄÿÿÿÿÿÿÿÿÿRCNÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ|y|ÿlilÿ3.3ÿÿ%%)ÿEIKÿDHKÿCGJÿAEHÿ<@Bÿ268ÿ?CFÿGLPÿINRÿKPTÿMRVÿOTXÿPVZÿRX\ÿTZ^ÿTZ^ÿTZ^ÿTZ^ÿTZ^ÿTZ^ÿTZ^ÿSY]ÿSY]ÿRX\ÿRX\ÿRX[ÿQW[ÿQW[ÿQVZÿPVZÿPVYÿOUYÿOUXÿOTXÿNTXÿNTWÿNSWÿMSWÿMRVÿMRVÿLRUÿLQUÿKQTÿKQTÿKQTÿKPTÿFKNÿ@AÿDFGÿEFHÿCDEÿ>?@ß<>??CDFHJLŸOQSÿ[]_ÿfikÿprtÿvy{ÿy{~ÿy|~ÿy|~ÿy|~ÿy|~ÿx{}ÿx{}ÿwz|ÿwy{ÿvy{ÿvxzÿuxzÿtwyÿtwyÿsvxÿsuxÿruwÿqtvÿortÿkmoÿegiÿ]^`ÿSTVÿJLM¿EFHÿcYaÿ}s{ÿaY`ÿ5.4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÀÄÿÿÿÿÿÿÿÿÿRDNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|y|ÿmimÿ4.4ÿÿ%#(ÿBGHÿAEGÿ@EFÿ?CEÿ:=?ÿ135ÿ<@CÿDILÿFKNÿGMPÿINRÿKPTÿLRUÿNTXÿPVYÿPVZÿPVZÿPVZÿPVZÿPVZÿPVYÿOUYÿOUYÿNTXÿNTXÿNTWÿMSWÿMSVÿMSVÿLRVÿLRUÿLQUÿKQUÿKQTÿKPTÿJPSÿJPSÿJORÿIORÿINRÿINQÿHNQÿHMQÿHMQÿHMQÿHMPÿCHKÿ:>@ÿ ÿÿÿÿÿÿÿÿÿÿ/24ÿDJMÿNTWÿINQÿ=BEÿ468ÿ?@AÿGHIÿJKMÿKLNÿIKLÿEFGÿACDßACD¿CEF¯FHIÏLMOÿUWXÿ^`bÿfhjÿmoqÿrtvÿtwyÿuwyÿuwyÿuwyÿuwyÿuwyÿtvxÿtvxÿsuwÿruwÿrtvÿqtvÿqsuÿprtÿprtÿoqsÿnqsÿnprÿnprÿmoqÿlopÿilnÿegiÿ^`bÿVXYÿJKMïBCEOÿLBKÿ}r|ÿc[bÿB;AÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÀÄÿÿÿÿÿÿÿÿÿRDNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ}y}ÿmimÿ4.4ÿÿ$"&ÿ@DFÿ?CEÿ>BDÿ=@Bÿ8<=ÿ/23ÿ9=?ÿAEHÿBGJÿDIKÿFJMÿGLOÿINQÿJPSÿLQTÿLRUÿLRUÿLRUÿLRUÿLRUÿLQTÿKQTÿKQTÿKPSÿJPSÿJPSÿJORÿIORÿINQÿINQÿHNQÿHMPÿHMPÿGMPÿGLOÿGLOÿFKNÿFKNÿFKMÿEJMÿEJMÿEJLÿDILÿDILÿDILÿDIKÿ@EGÿ6;=ÿ ÿÿÿÿÿÿÿÿÿÿ-02ÿBFIÿJPSÿEJMÿ;?Aÿ246ÿ>?@ÿGIJÿKMNÿNPQÿOPRÿNPQÿMOPÿNOQÿPQRÿSUVÿXZ[ÿ^`bÿcfgÿhjlÿkmoÿnprÿprtÿprtÿprtÿprtÿprtÿprtÿortÿoqsÿnprÿnprÿmoqÿmoqÿlnpÿlnpÿkmoÿkmoÿjlnÿjlnÿikmÿikmÿhjlÿhjkÿfhjÿcegÿ]_aÿTVWÿIJLÿABCÿJ@Iÿ}s|ÿd\cÿBBDÿ=@Bÿ7;<ÿ/22ÿ7:<ÿ=BDÿ?CEÿ@EGÿBFIÿCHJÿEJLÿGKNÿHMOÿHMPÿHMPÿHMPÿHMPÿHMPÿHMOÿGLOÿGLOÿGLNÿFKNÿFKNÿFKMÿEJMÿEJMÿEJLÿEILÿDILÿDHKÿCHKÿCHKÿCGJÿCGJÿBGJÿBFIÿBFIÿAFHÿAEHÿAEHÿAEHÿAEHÿ@EGÿ=ACÿ47:ÿ ÿÿÿÿÿÿÿÿÿÿ+./ÿ>CEÿGLNÿBFIÿ8;>ÿ024ÿ=>@ÿGHJÿLMNÿOPQÿQRTÿRTUÿSUWÿUWXÿWY[ÿZ\]ÿ]_aÿacdÿdfgÿfhjÿikmÿkmoÿmoqÿmoqÿmoqÿmoqÿmoqÿmoqÿlnpÿlnpÿkmoÿkmoÿjlnÿjlnÿikmÿikmÿhjlÿhjlÿgikÿgikÿghjÿfhiÿegiÿegiÿdfhÿdfhÿbceÿ\]_ÿSUWÿJLMÿACC¿=>?ÿI?Hÿ~s}ÿe\dÿCBDÿ=@Bÿ8<=ÿ/23ÿ489ÿ:>@ÿ;?Aÿ=ACÿ>BDÿ@DFÿAEGÿCGIÿDHJÿDIKÿDIKÿDIKÿDIKÿDIKÿDHJÿCHJÿCHJÿCHJÿBGIÿBGIÿBGIÿBFHÿAFHÿAFHÿAEGÿ@EGÿ@EGÿ@DFÿ?DFÿ?DFÿ?CEÿ?CEÿ>CEÿ>CDÿ>BDÿ=BDÿ=ACÿ=ACÿ=ACÿ=ABÿ9=>ÿ146ÿ ÿÿÿÿÿÿÿÿÿÿ(+,ÿ;@AÿCGIÿ>BDÿ58:ÿ022ÿ?AAÿIKLÿNOPÿPRSÿSTVÿUWXÿXY[ÿZ\]ÿ\^`ÿ_abÿaceÿdfgÿfhjÿhjlÿkmoÿnprÿoqsÿprtÿprtÿprtÿprtÿprtÿoqsÿoqsÿnprÿmoqÿmoqÿlnpÿlnpÿlmoÿkmoÿjlnÿjlnÿikmÿikmÿhjlÿhjlÿhikÿghjÿfhjÿfhiÿdegÿ_abÿXYZÿNOPÿCDEÏ@BCÿG>Gÿ~s}ÿg^fÿD=Dÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÅÁÄÿÿÿÿÿÿÿÿÿQFOÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ~{~ÿnjnÿ4/4ÿÿ%#'ÿBFHÿAEGÿ@DFÿ?BDÿ:=?ÿ135ÿ479ÿ9=?ÿ;?@ÿ<@Bÿ>ACÿ?CEÿADFÿBFHÿCGIÿDHJÿDHJÿDHJÿDHJÿDHJÿCGIÿCGIÿCGIÿCGIÿBFHÿBFHÿBEGÿAEGÿAEGÿAEGÿADFÿ@DFÿ@DFÿ@CEÿ?CEÿ?CDÿ?BDÿ>BDÿ>BDÿ>BCÿ>ACÿ=ACÿ=ABÿ=ABÿ=ABÿ<@Aÿ9<=ÿ145ÿ ÿÿÿÿÿÿÿÿÿÿ(+,ÿ;?@ÿBFHÿ>ACÿ479ÿ133ÿCDEÿMOPÿRSUÿTVWÿWXZÿY[\ÿ\^_ÿ^`bÿacdÿcegÿfhjÿijlÿkmoÿnpqÿprtÿsuwÿuwyÿuwyÿuwyÿuwyÿuwyÿuwyÿtvxÿtvxÿsuwÿsuwÿrtvÿrtvÿqsuÿprtÿprtÿoqsÿoqsÿnprÿnprÿmoqÿmopÿlnpÿlnoÿkmoÿklnÿjlnÿhjlÿdfhÿ]_`ÿSTVÿIJLÏEGGÿF=Eÿt}ÿi_hÿF>Eÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÅÁÄÿÿÿÿÿÿÿÿÿRGPÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ{ÿokoÿ5/5ÿÿ)',ÿJNPÿIMOÿHLNÿGJLÿAEFÿ79;ÿ7:;ÿ;?@ÿ=@Bÿ>BCÿ@CEÿAEGÿBFHÿDHJÿEIKÿFJLÿFJLÿFJLÿFJLÿFJLÿEIKÿEIKÿEIKÿEIJÿDHJÿDHJÿCGIÿCGIÿCGIÿCFHÿBFHÿBFHÿBFGÿAEGÿAEGÿADFÿ@DFÿ@DFÿ@CEÿ@CEÿ?CEÿ?CDÿ?BDÿ?BDÿ?BDÿ>BCÿ:>?ÿ256ÿ ÿÿÿÿÿÿÿÿÿÿ*,-ÿ=@BÿDHJÿ@CEÿ69:ÿ356ÿGHIÿRTUÿWXZÿZ[]ÿ\^_ÿ_abÿbceÿefhÿgikÿjlmÿmnpÿoqsÿrtvÿuwyÿxz{ÿz|~ÿ|~€ÿ}ÿ}ÿ}ÿ}ÿ|~€ÿ|~€ÿ{}ÿz|~ÿz|~ÿy{}ÿy{}ÿxz|ÿxz|ÿwy{ÿvxzÿvxzÿuwyÿuwyÿtvxÿtvxÿsuwÿrtvÿrtvÿqsuÿqsuÿprtÿoqsÿkmoÿdegÿYZ\ÿKLMÏFGHOÿD;Cÿu}ÿjaiÿKCJÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÅÂÄÿÿÿÿÿÿÿÿÿRHPÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ|ÿokoÿ5/5ÿÿ427ÿadfÿ_bdÿ^acÿ\_aÿUXYÿGIKÿDFHÿIKMÿJMOÿLOQÿNQSÿPSUÿRUWÿSVXÿUXZÿUY[ÿUY[ÿUY[ÿUY[ÿUY[ÿUXZÿTXZÿTWYÿTWYÿSWYÿSVXÿRUWÿRUWÿRUWÿQUVÿQTVÿQTUÿPSUÿPSUÿPSUÿORTÿORTÿNQSÿNQSÿNQSÿMPRÿMPRÿMPQÿMPQÿMPQÿLOQÿHJLÿ=@Aÿ ÿÿÿÿÿÿÿÿÿÿ357ÿKNPÿTWYÿNQSÿBDFÿ;<=ÿMNOÿYZ\ÿ^_aÿabdÿdegÿghjÿjkmÿlnpÿpqsÿrtvÿuwyÿxz|ÿ{}ÿ~€‚ÿƒ…ÿ„†ˆÿ†ˆŠÿ†ˆŠÿ†ˆŠÿ†ˆŠÿ†ˆŠÿ†ˆŠÿ…‡‰ÿ„†ˆÿ„†ˆÿƒ…‡ÿƒ…‡ÿ‚„†ÿ‚„†ÿƒ…ÿ€‚„ÿ€‚„ÿƒÿ‚ÿ~€‚ÿ}ÿ}ÿ|~€ÿ|}ÿ{}ÿz|~ÿz|~ÿy{}ÿyz|ÿxz|ÿtuwÿklnÿZ\]ÿMNOŸ?ÿ+"+ÿ€u~ÿkbjÿXQWÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÅÂÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹†Šÿ ÿ ÿ ÿ|ÿokoÿ5/5ÿÿKIMÿ‘“ÿ‘ÿ‹ÿˆŠ‹ÿ}€ÿiklÿnpqÿxz|ÿ{}ÿ~€‚ÿƒ…ÿ„†ˆÿ‡‰‹ÿŠŒŽÿ‘ÿ’ÿ’ÿ’ÿ’ÿ‘ÿŒ‘ÿŒŽÿ‹ÿŠÿŠŒŽÿ‰ŒŽÿ‰‹ÿˆŠŒÿ‡Š‹ÿ‡‰‹ÿ†‰Šÿ†ˆŠÿ…‡‰ÿ„‡‰ÿ„†ˆÿƒ…‡ÿ‚„†ÿ‚„†ÿ„…ÿƒ…ÿ€‚„ÿ‚„ÿƒÿƒÿƒÿ~€‚ÿwxzÿfgiÿÿÿÿÿÿÿÿÿÿÿUWXÿ|€ÿ‹ÿƒ…ÿmoqÿOQQÿSUVÿ`bcÿfhiÿiklÿmnpÿpqsÿsuvÿvxyÿy{}ÿ|~€ÿ€ƒÿƒ…‡ÿ†ˆŠÿ‰‹ÿŒŽÿ’”ÿ’”–ÿ’”–ÿ’”–ÿ’”–ÿ’”–ÿ‘“•ÿ‘“•ÿ’”ÿ’”ÿ‘“ÿŽ’ÿŽ’ÿ‘ÿŒŽÿŒŽÿ‹ÿŠŒŽÿŠŒÿ‰‹ÿˆŠŒÿˆŠŒÿ‡‰‹ÿ†ˆŠÿ†ˆ‰ÿ…‡‰ÿ…†ˆÿ„†‡ÿƒ…‡ÿƒ…‡ÿƒ…ÿz{}ÿjlmÿYZ[ÿRST?ÿ*!*ÿ€u~ÿnclÿYRXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÅÂÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹†‹ÿÿÿÿ€}€ÿokoÿ505ÿÿURWÿ¢£¥ÿŸ¡£ÿŸ¡ÿ™›ÿ‘ÿwxzÿ„†‡ÿ“”–ÿ—˜šÿšœžÿžŸ¡ÿ¡£¥ÿ¥§©ÿ©«¬ÿ¬®°ÿ¬®°ÿ¬®°ÿ¬®°ÿ¬®°ÿ¬®°ÿ«­¯ÿ«­¯ÿª¬­ÿ©«­ÿ¨ª¬ÿ§©«ÿ§©ªÿ¦¨ªÿ¥§©ÿ¥¦¨ÿ¤¦¨ÿ£¥¦ÿ¢¤¦ÿ¢¤¥ÿ¡¢¤ÿ ¢¤ÿŸ¡£ÿŸ ¢ÿž ¡ÿŸ¡ÿœž ÿœŸÿ›žÿšœžÿšœžÿšœÿ’”ÿ|}ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿijkÿ˜šœÿª¬®ÿŸ¡ÿ…‡ˆÿ\]^ÿ[\]ÿiklÿoqrÿstvÿvxyÿz{}ÿ}€ÿ€‚„ÿ„†‡ÿ‡‰‹ÿ‹ŽÿŽ’ÿ’”•ÿ•—™ÿ™šœÿœž ÿŸ¡£ÿŸ¡£ÿŸ¡£ÿŸ¡£ÿŸ¡£ÿž ¢ÿž ¢ÿŸ¡ÿœž ÿ›Ÿÿ›Ÿÿšœžÿ™›ÿ™›ÿ˜šœÿ—™›ÿ—˜šÿ–˜šÿ•—™ÿ”–˜ÿ”•—ÿ“•—ÿ’”–ÿ‘“•ÿ‘“”ÿ’”ÿ‘“ÿ’ÿ’ÿŽ‘ÿ†ˆŠÿvxyÿcefÿXYZ??ÿ( (ÿ€u~ÿoemÿZSYÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÃÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ‡‹ÿÿÿÿ}ÿoloÿ505ÿÿVSXÿ¤¦§ÿ¡£¥ÿŸ¡£ÿœŸÿ‘“ÿxz{ÿ‡‰‹ÿ–˜šÿšœžÿž ¢ÿ¢£¥ÿ¥§©ÿ©«­ÿ­¯±ÿ°²´ÿ±³µÿ±³µÿ±³µÿ±³µÿ°²´ÿ°²´ÿ¯±³ÿ®°²ÿ­¯±ÿ­¯±ÿ¬®°ÿ«­¯ÿª¬®ÿ©«­ÿ©ª¬ÿ¨ª¬ÿ§©«ÿ¦¨ªÿ¦¨ªÿ¥§©ÿ¤¦§ÿ£¥§ÿ¢¤¦ÿ¢£¥ÿ¡£¥ÿ ¢¤ÿŸ¡£ÿŸ¡¢ÿŸ¡¢ÿŸ¡¢ÿž ¡ÿ”–˜ÿ‚ÿ!!"ÿÿÿÿÿÿÿÿÿÿlmnÿž ÿ®°²ÿ¢£¥ÿ‰ŠŒÿ`abÿacdÿqrtÿwyzÿ{}~ÿ€‚ÿˆŠ‹ÿ’“ÿ–˜™ÿ—˜šÿ•—™ÿ•—™ÿ™›ÿž ÿ ¢¤ÿ¤¦¨ÿ¨ª¬ÿª¬®ÿ«­¯ÿ«­¯ÿ«­¯ÿ«­¯ÿª¬®ÿª¬®ÿ©«­ÿ¨ª¬ÿ§©«ÿ¦¨ªÿ¦¨©ÿ¥§©ÿ¤¦¨ÿ¤¥§ÿ¢¤¦ÿ¢£¥ÿ¡£¥ÿ ¢¤ÿŸ¡£ÿŸ¡£ÿž ¢ÿŸ¡ÿœž ÿ›Ÿÿ›žÿšœžÿ™›ÿ™›ÿ—˜šÿŽ‘ÿ{}~ÿghiÿ\]^?ÿ''ÿ€u~ÿpfoÿZTZÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ~ÿoloÿ505ÿÿXUYÿ§¨ªÿ¤¦¨ÿ¢¤¦ÿž ¢ÿ’”•ÿ{|}ÿŒÿœžŸÿ ¡£ÿ¤¥§ÿ§©«ÿ«­¯ÿ¯±³ÿ³µ·ÿ¶¸ºÿ·¹»ÿ·¹»ÿ·¹»ÿ·¹»ÿ¶¸ºÿ¶¸ºÿµ·¹ÿ´¶¸ÿ³µ·ÿ³µ·ÿ²´¶ÿ±³´ÿ°²´ÿ¯±³ÿ®°²ÿ®°±ÿ­¯±ÿ¬®°ÿ«­¯ÿ«­®ÿª¬­ÿ©«¬ÿ¨ª¬ÿ§©«ÿ§¨ªÿ¦¨©ÿ¥§¨ÿ¤¦¨ÿ¤¦¨ÿ¤¦¨ÿ£¥§ÿ™›ÿƒ…†ÿ&&&ÿ(((ÿ(((ÿ(((ÿ(((ÿ(((ÿ(((ÿ(((ÿ,,,ÿÿpqrÿ£¤¦ÿ´¶¸ÿ§©ªÿŽÿdefÿfghÿvxxÿ}€ÿ—˜™ÿ´µµÿ·¸¹ÿ²³´ÿ¬­¯ÿ¥¦¨ÿžŸ¡ÿœžŸÿ ¢£ÿ¤¦§ÿ¨ª«ÿ¬®¯ÿ°²³ÿ²´¶ÿ³µ¶ÿ³µ¶ÿ³µ¶ÿ³µ¶ÿ²´µÿ²´µÿ±³´ÿ¯±³ÿ¯±²ÿ®°²ÿ­¯±ÿ­®°ÿ¬®¯ÿ«­®ÿª¬­ÿ©«­ÿ¨ª¬ÿ¨ª«ÿ§©ªÿ¦¨©ÿ¥§©ÿ¤¦¨ÿ¤¥§ÿ£¥¦ÿ¢¤¥ÿ¡£¤ÿ ¢¤ÿŸ¡¢ÿ™›œÿŒŽÿwxzÿefg/ÿ&&ÿ€u~ÿrhqÿ[T[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚~‚ÿplpÿ505ÿÿYW[ÿª¬®ÿ¨ª¬ÿ¦¨ªÿ¢¤¦ÿ•—™ÿ}€ÿ‘“”ÿ£¤¥ÿ§¨©ÿ«¬­ÿ¯°±ÿ³´¶ÿ·¸¹ÿ»½¾ÿ¾ÀÁÿ¿ÁÂÿ¿ÁÂÿ¿ÁÂÿ¿ÁÂÿ¾ÀÁÿ½¿Àÿ½¿Àÿ¼¾¿ÿ»½¾ÿº¼½ÿº¼½ÿ¸º»ÿ·¹ºÿ·¹ºÿ¶¸¹ÿµ·¸ÿ´¶·ÿ´µ¶ÿ³µ¶ÿ²´µÿ±³´ÿ°²³ÿ¯±²ÿ®°±ÿ®°°ÿ­¯°ÿ¬®¯ÿ«­®ÿ«­®ÿ«­®ÿª¬­ÿ ¢¢ÿ‰Š‹ÿ+++ÿ777ÿ777ÿ777ÿ777ÿ777ÿ777ÿ777ÿPPPÿÿuvwÿª«¬ÿ¼¾¿ÿ®°±ÿ”•–ÿijkÿmnoÿ~€ÿ«¬¬ÿÂÃÃÿÀÁÁÿ½½¾ÿ¸¹ºÿ³µ¶ÿ®¯°ÿ§¨©ÿ§©©ÿ«­®ÿ¯±²ÿ³µ¶ÿ·¹ºÿ¼¾¾ÿ¾ÀÁÿ¿ÁÂÿ¿ÁÂÿ¿ÁÂÿ¿ÁÂÿ¾ÀÁÿ½¿Àÿ½¿Àÿ»½¾ÿº¼½ÿº¼½ÿ¹»¼ÿ¸º»ÿ·¹ºÿ·¸¹ÿµ·¸ÿµ¶·ÿ´¶¶ÿ³µ¶ÿ²´µÿ±³´ÿ°²³ÿ¯±²ÿ¯±±ÿ®°°ÿ­¯°ÿ¬®®ÿª¬¬ÿ£¥¦ÿ—™™ÿ†‡ˆÿpqqÏÿÿt~ÿsirÿ]U\ÿ((ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚‚ÿpmpÿ505ÿÿ\Y]ÿ¯±²ÿ¬®°ÿª¬­ÿ¦¨©ÿ™›œÿ‚ƒÿ˜™šÿ«¬­ÿ¯°±ÿ³µ¶ÿ·¹ºÿ»½¾ÿÀÂÂÿÄÆÆÿÇÉÊÿÈÊËÿÈÊËÿÈÊËÿÈÊËÿÇÉÊÿÆÈÉÿÆÈÉÿÅÇÈÿÄÆÇÿÃÅÆÿÂÄÅÿÂÄÄÿÁÃÃÿ¿ÁÂÿ¿ÁÁÿ¾ÀÁÿ½¿Àÿ¼¾¿ÿ»½¾ÿ»¼½ÿº»¼ÿ¹»»ÿ¸ººÿ·¹¹ÿ¶¸¹ÿµ·¸ÿ´¶·ÿ´µ¶ÿ´µ¶ÿ´µ¶ÿ³´µÿ¨ªªÿ‘’ÿ001ÿGGGÿGGGÿGGGÿGGGÿGGGÿGGGÿGGGÿvvvÿÿ{|}ÿ²´µÿÅÇÇÿ¶¸¹ÿ›œÿpqqÿvwwÿ——˜ÿÈÈÈÿÈÉÉÿÆÇÇÿÄÅÅÿÁÂÂÿ½¾¿ÿ¹º»ÿ²´µÿµ¶·ÿ¹»¼ÿ¾¿ÀÿÂÄÄÿÇÉÉÿËÍÎÿÎÐÑÿÎÐÑÿÎÐÑÿÎÐÑÿÎÐÑÿÎÐÐÿÍÏÏÿÌÎÏÿËÍÎÿÊÌÌÿÉËËÿÈÊËÿÇÉÊÿÆÈÉÿÅÇÈÿÄÆÇÿÄÅÆÿÃÄÅÿÂÄÄÿÁÃÃÿÀÂÂÿ¿ÁÁÿ¾ÀÀÿ½¿¿ÿ¼¾¾ÿ»½¾ÿ¸ººÿ¯±±ÿ¢£¤ÿ‘‘ÿ{||Ïoopÿ  ÿu~ÿtjtÿ_W_ÿ-$-ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒƒÿpmpÿ505ÿÿ^\`ÿ´¶·ÿ±³´ÿ¯±²ÿ«­®ÿžŸ ÿ„†‡ÿŸ ¡ÿ³µµÿ¸¹ºÿ¼¾¾ÿÀÂÂÿÅÇÇÿÉËÌÿÎÐÐÿÑÓÓÿÒÔÔÿÒÔÔÿÒÔÔÿÒÔÔÿÑÓÔÿÐÒÓÿÐÒÒÿÏÑÑÿÎÐÐÿÍÏÏÿÌÎÎÿËÍÍÿÊÌÌÿÉËÌÿÈÊËÿÇÉÊÿÆÈÈÿÆÇÈÿÅÇÇÿÄÆÆÿÃÅÅÿÂÄÄÿÁÃÃÿÀÂÂÿ¿ÁÁÿ¾ÀÁÿ½¿¿ÿ½¾¿ÿ¼¾¾ÿ¼¾¾ÿ»½½ÿ°²²ÿ—˜™ÿ566ÿVVVÿVVVÿVVVÿVVVÿVVVÿVVVÿVVVÿ•••ÿ ÿ‚ƒƒÿ¼¾¾ÿÎÐÐÿ¿ÁÁÿ¢¤¤ÿvwwÿ}}}ÿµ¶¶ÿÌÍÍÿÍÍÍÿÌÌÍÿÊËËÿÈÉÉÿÁÃÃÿ¼¾¾ÿ¼¾¾ÿÀÂÂÿÅÆÇÿÊËÌÿÎÐÐÿÓÕÕÿØÚÚÿÛÝÝÿÛÝÝÿÛÝÝÿÛÝÝÿÛÝÝÿÛÜÜÿÚÛÛÿØÚÚÿØÚÚÿ×ÙÙÿÖØØÿÕ××ÿÔÕÕÿÓÔÔÿÒÓÓÿÑÓÓÿÐÒÒÿÏÑÑÿÎÐÐÿÍÎÎÿÌÍÍÿËÍÍÿÊÌÌÿÉËËÿÈÉÉÿÃÅÅÿ¹ººÿª««ÿ–˜˜ÿ‚ƒƒÏxyyÿ  ÿuÿukuÿ`Y`ÿ.%.ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿƒ€ƒÿqmqÿ505ÿÿa^bÿ¹»¼ÿ·¹¹ÿµ··ÿ°²³ÿ£¤¥ÿ‰ŠŠÿ§¨©ÿ¾¿¿ÿÂÃÃÿÇÈÈÿËÍÍÿÐÑÑÿÕÖÖÿÚÛÛÿÝßßÿÞßßÿÞßßÿÞßßÿÞßßÿÝßßÿÜÞÞÿÛÝÝÿÚÜÜÿÚÛÛÿÙÚÚÿØÙÙÿרØÿÖ××ÿÔÖÖÿÔÕÕÿÓÔÔÿÒÓÓÿÑÒÒÿÐÑÑÿÏÐÐÿÎÏÏÿÍÎÎÿÌÍÍÿËÌÌÿÊËËÿÉÊÊÿÈÉÉÿÇÉÉÿÇÈÈÿÇÈÈÿÆÇÇÿº»»ÿŸ¡¡ÿ;<<ÿfffÿfffÿfffÿfffÿfffÿfffÿfffÿ­­­ÿÿŠ‹‹ÿÆÇÇÿÚÛÛÿÊÌÌÿ«¬¬ÿ{||ÿ€€ÿÉÉÉÿÎÎÎÿÏÏÏÿÎÏÏÿÈÈÈÿ´µµÿ°±±ÿ´µ¶ÿº»»ÿÁÂÂÿÉÊÊÿÏÐÐÿÔÖÖÿÙÚÚÿÞßßÿáããÿáããÿáããÿáããÿáããÿáââÿàááÿÞààÿÝßßÿÝÞÞÿÜÝÝÿÛÜÜÿÚÛÛÿÙÚÚÿ×ÙÙÿÖ××ÿÖ××ÿÕÖÖÿÔÕÕÿÓÔÔÿÒÓÓÿÐÒÒÿÏÐÐÿÎÏÏÿÉÊÊÿ¼½½ÿ«­­ÿ—˜˜ÿ„……Ÿÿ  ÿu~ÿwlvÿaZaÿ0'/ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ!!ÿ„€„ÿqmqÿ606ÿÿdaeÿ¿ÁÂÿ½¾¿ÿº¼¼ÿ¶¸¸ÿ¨©ªÿŽŽÿ¯°°ÿÇÈÈÿËÌÌÿÐÑÑÿÕÖÖÿÚÛÛÿßààÿäååÿèééÿèééÿèééÿèééÿèééÿèééÿçèèÿæççÿåææÿäååÿãääÿâããÿáââÿàááÿßààÿÞßßÿÝÞÞÿÜÝÝÿÛÜÜÿÚÛÛÿÙÚÚÿØÙÙÿרØÿÖ××ÿÕÖÖÿÔÕÕÿÓÔÔÿÒÓÓÿÑÒÒÿÑÒÒÿÑÒÒÿÏÐÐÿÃÄÄÿ§¨¨ÿAAAÿuuuÿuuuÿuuuÿuuuÿuuuÿuuuÿuuuÿ···ÿÿ‘‘‘ÿÐÑÑÿãääÿÔÕÕÿ³´´ÿ€€ÿ‡ˆˆÿÉÉÉÿÍÎÎÿÎÏÏÿÄÅÅÿ£££ÿžžžÿžžÿ ¡¡ÿ¨©©ÿ³´´ÿÁÂÂÿÍÎÎÿÖ××ÿÞßßÿããäÿæççÿæççÿæççÿæççÿæççÿåææÿäååÿãääÿâããÿáââÿáââÿàááÿÞßßÿÝÞÞÿÜÝÝÿÛÜÜÿÚÛÛÿÙÚÚÿØÙÙÿרØÿÖ××ÿÕÖÖÿÒÓÓÿÊËËÿ¾¿¿ÿ«««ÿ“””ÿ………oÿ  ÿ€u~ÿynxÿd\cÿ2(1ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ$#ÿ„€„ÿqmqÿ606ÿÿgdhÿÆÇÈÿÃÅÅÿÀÂÂÿ¼¾¾ÿ­¯¯ÿ‘““ÿµµµÿÍÍÍÿÒÒÒÿ×××ÿÜÜÜÿáááÿæççÿëëëÿïïïÿðððÿðððÿðððÿðððÿïïïÿîîîÿíííÿìììÿëëëÿêêêÿéééÿèèèÿçççÿæææÿåååÿäääÿãããÿâââÿáááÿàààÿÞßßÿÝÞÞÿÜÝÝÿÛÛÛÿÚÚÚÿÙÚÚÿØØØÿ×××ÿ×××ÿ×××ÿÖÖÖÿÈÉÉÿ¬¬¬ÿFFFÿ„„„ÿ„„„ÿ„„„ÿ„„„ÿ„„„ÿ„„„ÿ„„„ÿ¶¶¶ÿÿ•••ÿÖÖÖÿéééÿÙÙÙÿ¸¸¸ÿ‚‚‚ÿŽŽŽÿÇÈÈÿÊËËÿÃÃÃÿ–——ÿˆ‰‰ÿƒƒƒ¿ƒƒƒ………ŒŒŒ¯––—着ªÿ½½½ÿÍÎÎÿÛÜÜÿåææÿêëëÿêëëÿêëëÿêëëÿêëëÿêêêÿéééÿçèèÿæççÿåææÿäååÿäääÿãããÿâââÿàááÿßààÿÞßßÿÝÞÞÿÜÝÝÿÛÛÛÿÙÙÙÿÓÔÔÿÊËËÿ½½½ÿª««ÿ“““ß…††/¿ÿrhqÿ{ozÿf]eÿ>4>ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ&%ÿ……ÿrnrÿ707ÿÿkhlÿÍÏÏÿÊÌÌÿÇÉÉÿÃÅÅÿ³µµÿ—˜˜ÿ¹¹¹ÿÑÑÑÿÖÖÖÿÛÛÛÿàààÿåååÿêêêÿïïïÿóóóÿôôôÿôôôÿôôôÿôôôÿóóóÿòòòÿñññÿðððÿïïïÿîîîÿíííÿìììÿëëëÿêêêÿéééÿèèèÿçççÿæææÿåååÿäääÿãããÿâââÿáááÿßßßÿÞÞÞÿÝÝÝÿÜÜÜÿÛÛÛÿÛÛÛÿÛÛÛÿÙÙÙÿËËËÿ®®®ÿJJJÿ”””ÿ”””ÿ”””ÿ”””ÿ”””ÿ”””ÿ”””ÿ³³³ÿ ÿ———ÿØØØÿëëëÿÛÛÛÿºººÿƒƒƒÿŒŒŒÿÄÄÄÿÄÄÄÿŽŽŽÿ{{{¿yzz‚ƒƒ›››ï²²²ÿÈÈÉÿÚÛÛÿçèèÿíííÿîïïÿîïïÿîïïÿíîîÿìííÿëììÿêêêÿéééÿèèèÿççèÿæççÿåææÿäååÿãããÿâââÿáááÿßßßÿÚÚÚÿÑÒÒÿÄÅÅÿ³´´ÿŸŸŸÿ“““‰ŠŠ¿ÿeZcÿ|pzÿg^fÿC9Bÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ'&ÿ†‚†ÿrnrÿ707ÿÿokoÿÔÖÖÿÑÓÓÿÏÐÐÿÊËËÿº»»ÿœÿºººÿÑÑÑÿ×××ÿÜÜÜÿáááÿæææÿëëëÿðððÿôôôÿõõõÿõõõÿõõõÿõõõÿôôôÿóóóÿòòòÿñññÿðððÿïïïÿîîîÿìììÿëëëÿêêêÿéééÿèèéÿçççÿçççÿæææÿåååÿãããÿâââÿáááÿàààÿßßßÿÞÞÞÿÝÝÝÿÜÜÜÿÜÜÜÿÜÜÜÿÙÙÙÿÊÊÊÿ®®®ÿNNNÿ£££ÿ£££ÿ£££ÿ£££ÿ£££ÿ£££ÿ£££ÿ±±±ÿÿ———ÿÖÖÖÿèèèÿØØØÿ¹¹¹ÿ‚‚‚ÿƒƒƒÿ¾¾¾ÿšššÿpqqŸ†††“““¦¦¦ÿÀÀÀÿÕÕÕÿäååÿíííÿðññÿñññÿðððÿïïïÿîîîÿíííÿëììÿêëëÿéêêÿèèéÿèèèÿçççÿæææÿäääÿßààÿØØØÿËËËÿ»»»ÿ§§§ÿ”””ψ‰‰?¿ÿcYbÿ}r|ÿh_gÿE;Dÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ†‚†ÿrnrÿ717ÿÿrnrÿÚÛÛÿרØÿÕÖÖÿÐÑÑÿ¿ÀÀÿ¡¡¡ÿ§¨©ÿ¶¸¹ÿ»¼½ÿ¿ÁÂÿÄÅÆÿÈÊËÿÍÎÏÿÑÓÔÿÕÖ×ÿÕרÿÕרÿÕרÿÕרÿÕÖ×ÿÔÕÖÿÓÔÕÿÒÓÔÿÑÓÔÿÐÒÓÿÏÑÒÿÎÐÑÿÍÏÐÿÌÎÏÿÌÍÎÿËÌÍÿÉËÌÿÉÊËÿÈÉÊÿÇÈÉÿÆÇÈÿÅÆÇÿÄÆÆÿÃÅÆÿÂÄÅÿÁÃÄÿÀÁÂÿÀÁÁÿ¿ÁÁÿ¿ÀÁÿ»¼¼ÿ­¯¯ÿ•–—ÿ+++ÿyyyÿ„„„ÿ„„„ÿ„„„ÿ„„„ÿ„„„ÿ„„„ÿrrrÿÿ‚ƒƒÿ¶·¸ÿÄÆÆÿ·¸¹ÿŸŸÿwwxÿrrrÿÿlllŸ”””/   Ï···ÿÏÏÏÿßßßÿêêêÿðððÿòòòÿñññÿðððÿïïïÿîîîÿíííÿìììÿêêêÿéééÿçççÿãããÿÜÜÜÿÒÒÒÿÄÄÄÿ°°°ÿ›››ïo¿ÿbXaÿs~ÿi`hÿFCGÿ@EHÿAFIÿAFIÿAFIÿAFIÿAFIÿAFIÿAEIÿ@EHÿ@EHÿ@EHÿ@EHÿ?DGÿ?CGÿ?CGÿ>CFÿ>CFÿ>BFÿ>BEÿ=AEÿ=AEÿ=AEÿAÿ:>Aÿ9=@ÿ7;>ÿ59<ÿïÿÿÿÿÿÿÿÿß036ÿ;@Cÿ=BEÿ;?Bÿ7;>ÿMOPÿggg?ŸŸŸ¦¦¦Ÿ®®®ïºººÿÄÄÄÿÉÉÉÿÉÉÉÿÇÇÇÿÁÁÁÿ¸¸¸ÿªªªÿ¡¡¡¯———_ÿE;Eÿ‚vÿndmÿYOXÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿ/#/ÿˆ„ˆÿsosÿ717ÿÿxtxÿçççÿäääÿáááÿÛÜÜÿÊÊÊÿªªªÿ?gggžžž?¤¤¤¨¨¨¢¢¢¿¢¢¢¯¡¡¡œœœO———ÿD;Dÿ‚vÿpfoÿZPYÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ1%0ÿ‰„ˆÿsosÿ717ÿÿwswÿåååÿâââÿßßßÿÙÙÙÿÇÇÇÿ§¨¨ÿ‹‹‹?ÿD;Cÿ„w‚ÿrhpÿ\R[ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ3'2ÿ‰…‰ÿsosÿ717ÿÿkhlÿÍÎÏÿÊËÌÿÇÉÊÿÁÂÃÿ°±²ÿ”––ÿ}}~?Oÿ6-5ÿ„x‚ÿsirÿg]eÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿ5(3ÿŠ…‰ÿtptÿ717ÿÿ]Z_ÿ±³µÿ®±³ÿ¬®°ÿ¢¥¦ÿ“•–ÿ}~€ÿjlm??ÿ&&ÿ„yƒÿuktÿkbjÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿ7+6ÿІŠÿtptÿ717ÿÿOMRÿ”˜›ÿ’•˜ÿ‘“ÿ‚…ˆÿtwyÿcfgÿWY[??ÿ&&ÿ…z„ÿxmwÿmdlÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿ9,8ÿІŠÿtptÿ717ÿÿ?=Cÿtx|ÿpuxÿkorÿ`dgÿTXZÿLOQŸ?ÿ&&ÿ†z„ÿyoxÿnemÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ;.:ÿ‹‡‹ÿtptÿ717ÿÿ,+1ÿPUYÿMRVÿJNRÿAEIÿ;?BŸÿÿ†{…ÿ{qzÿpgoÿC7Bÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ=0<ÿ‹‡‹ÿtptÿ818ÿÿ"ßÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ?2>ÿ‹‡‹ÿtptÿ818ÿÿ ÿ ÿ‡|†ÿt~ÿsjrÿMALÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿB5@ÿˆÿupuÿ929ÿ ÿ  ÿ ÿ‡}‡ÿv€ÿuktÿOBNÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6CÿD6Cÿˆÿuquÿ929ÿ ÿ  ï ÿ€vÿƒwÿwmvÿWJUÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8DÿF8Dÿˆÿvqvÿ929ÿ ÿ  ¿ ÿi^hÿ„y‚ÿynwÿ^R\ÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿH:FÿމŽÿvqvÿ929ÿ ÿ  ¿ ÿi_hÿ†{…ÿ{qyÿaU_ÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿK=IÿŽŠŽÿvqvÿ929ÿ ÿ  ¿ ÿi`hÿˆ|†ÿ|r{ÿbVaÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿM?KÿŠÿvqvÿ929ÿ ÿ  Ÿ ÿZPYÿŠ~ˆÿ~t}ÿlakÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿOBNÿŠÿvrvÿ929ÿ ÿ   ÿJAIÿ‹‰ÿv~ÿpeoÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿQDPÿ‹ÿvrvÿ929ÿ ÿ   ÿJAJÿŒ‹ÿw€ÿrgqÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿTFRÿ‹ÿwrwÿ929ÿ ÿ   ÿKBJÿŽƒŒÿƒy‚ÿuisÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿWIUÿ‘Œÿwrwÿ929ÿ ÿ  O ÿ2)2ÿŽ„ÿ…|„ÿv~ÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿXLWÿ‘Œ‘ÿwrwÿ929ÿ ÿ  ? ÿ*!*ÿ†ÿˆ~‡ÿ‚x€ÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ[OZÿ’‘ÿwrwÿ929ÿ ÿ  ? ÿ+"*ÿ‘‡ÿ‰€ˆÿƒz‚ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ]Q\ÿ’’ÿwrwÿ929ÿ ÿ  ? ÿ""ÿ“‰‘ÿŒ‚Šÿ†|„ÿeXcÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿaT_ÿ“Ž’ÿwrwÿ929ÿ ÿ  ÿ ÿ“Š’ÿ„Œÿ‡†ÿk`jÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿbWaÿ“Ž“ÿwrwÿ929ÿ ÿ  ÿ ÿ•Œ”ÿ‡ÿЉÿncmÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿfZdÿ““ÿwrwÿ929ÿ ÿ  ÿ ÿ–Ž–ÿ’ˆ‘ÿŒ‚‹ÿrgrÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿh\hÿ””ÿxrxÿ:2:ÿ ÿ  ß ÿ}t|ÿ”‹“ÿŽ…ÿzpzÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿk_jÿ””ÿxrxÿ:2:ÿ ÿ  ¿ ÿuluÿ–•ÿ‡ÿ}s|ÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿnbmÿ•”ÿxrxÿ:2:ÿ ÿ  ¿ ÿwnvÿ˜—ÿ’Š’ÿ€vÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿqfpÿ••ÿxrxÿ:2:ÿ ÿ  ¯ ÿe]eÿš‘™ÿ”Œ”ÿ‡~†ÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿtisÿ••ÿxrxÿ:2:ÿ ÿ   ÿTKSÿ›“šÿ–Ž•ÿ‹‚Šÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿvkuÿ••ÿxsxÿ:2:ÿ ÿ   ÿULUÿž–ÿ™‘˜ÿކÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿyoyÿ–‘–ÿxsxÿ:2:ÿ ÿ   ÿLDLÿŸ˜žÿ›”šÿ•Ž”ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ|s{ÿ–‘–ÿxsxÿ:2:ÿ ÿ  ? ÿ0'0ÿ¢›¡ÿ–œÿ™‘˜ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ€v~ÿ–’–ÿxsxÿ:2:ÿ ÿ  ? ÿ1(1ÿ¤£ÿ ™Ÿÿ›”›ÿ…}…ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ‚z‚ÿ—’—ÿxsxÿ:2:ÿ ÿ  ? ÿ ÿ¤ž£ÿ¢›¡ÿ‡Žÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ…}„ÿ—’—ÿxsxÿ:2:ÿ ÿ   ÿ ÿE6>ÿ>6>ÿ>6>ÿ>6>ÿ>6>ÿ>6>ÿWPWÿuouÿuouÿuouÿuouÿuouÿuouÿuouÿž™žÿ­ª­ÿ­ª­ÿ­ª­ÿ­ª­ÿ®ª®ÿ®«®ÿ®«®ÿÈÆÈÿèæèÿèæèÿèæèÿèæèÿèçèÿèçèÿèçèÿèçèÿéçèÿéçéÿéçéÿéçéÿéçéÿéçéÿéçéÿéçéÿéçéÿéçéÿéçéÿéçèÿéæèÿéæèÿéæèÿéåèÿéåèÿéäèÿéãèÿêãèÿêãèÿêâçÿêâçÿêáçÿêáçÿêàæÿêàæÿëßæÿëßæÿëÞçÿëÞæÿìÞæÿìÞæÿìÝæÿìÝæÿìÜæÿìÜæÿíÜæÿíÜçÿíÜçÿîÜçÿîÜçÿîÜçÿïÜèÿïÜèÿïÝèÿðÝèÿðÝèÿðÝéÿðÝéÿñÞêÿñßêÿñßêÿòàëÿòáëÿòáëÿóâìÿòãíÿóäíÿóäîÿóæîÿôæîÿôçïÿôçïÿôçïÿôçïÿ¤ž£ÿxsxÿ:2:ÿ ÿ    ¿ ¿ ¿ ¿ ¿ ¿ ß ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿC;CÿD<:86421.-+)'%#!   KWetЦÁÉÐÖ×ÖÕßéèèççæææå ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ)(ÿ* *ÿ*)ÿ*)ÿ))ÿ)(ÿ)(ÿ('ÿ('ÿ('ÿ'&ÿ'&ÿ&&ÿ&%ÿ&%ÿ%$ÿ%$ÿ%$ÿ$#ÿ$#ÿ ÿ ômVURQOMKIGECB@><:86421.-+)'%#!  ? ï ÿ#"ÿ#"ÿ##ÿ$#ÿ$#ÿ?3>ÿA4?ÿB5@ÿB6AÿC7BÿD8CÿE9CÿSGRÿeXcÿfZdÿg[eÿh\fÿi]hÿj_iÿl`jÿmakÿƒvÿ‘…ÿ’‡ÿ“ˆ‘ÿ”‰’ÿ•‰“ÿ”‰“ÿ”ˆ’ÿ’‡ÿ‘…ÿ„ŽÿŽ‚Œÿ‹ÿ‹‰ÿŠ}ˆÿ‰|†ÿ‡z…ÿ…xƒÿ„w‚ÿƒu€ÿtÿ€r}ÿ~p|ÿ|ozÿ{myÿzkwÿxjuÿvhtÿufrÿsdqÿrcoÿpanÿo_lÿ7-7ÿ  ÿ«UTRPNMJIGECA?=;985420.-*)&%#!   Ÿ&%ÿhXeÿjYgÿl[hÿm]jÿo_lÿq`mÿrboÿtdpÿufrÿwgtÿxiuÿzkwÿ{myÿ}ozÿp|ÿ€r}ÿ‚tÿƒvÿ…w‚ÿ‡y„ÿˆ{…ÿŠ|‡ÿ‹~ˆÿƒu€ÿ„vÿ…w‚ÿ†xƒÿ‡y„ÿ†yƒÿ…xƒÿ„wÿƒu€ÿ‚tÿs~ÿ€r}ÿ}nzÿvfrÿuerÿtdqÿsdpÿrcoÿrbnÿqanÿp`mÿo_lÿo^kÿn]jÿm\iÿl\iÿk[hÿkZgÿjYfÿiXfÿhWeÿhVdÿ7,6ÿÿ¡BA?>=;:8754310.-+*('&$#!    ¿I:Gÿ`O\ÿaP]ÿbQ^ÿcR_ÿdS`ÿdTaÿdS`ÿcR_ÿcR_ÿcS`ÿdS`ÿdT`ÿdTaÿeTaÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ_N[ÿ5,4ÿ  ÿ–-,*)((&&%$#"!       ¿F6CÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿZHVÿ5+4ÿ  ÿ‹            ÏB1?ÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿ5+4ÿ  ÿ  ÿOBÏ268¿(&ÿ# ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ5.5ÿÿ..3ÿSY^ÿGLPÿ6:<¯¿)'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ5/5ÿ  ÿ./3ÿU\`ÿMSWÿ9=@¿¿+(ÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ605ÿ ÿ,-1ÿRX\ÿKPUÿ7:=Ï7;=?9=A?<@D??DG?@EH?@EH??DH??DG??DG?>CF?=BE?=BE?=AD?<@D?;@C?;?B?:?B?:>A?9=@?8A?9=@?9;=\^`Odfhgik^`boY[]/ï3$0ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ616ÿ ÿ**.ÿMSVÿGLOÿ8<>ÿFKOÿIOSÿMSWÿPW[ÿRX]ÿRX]ÿRX\ÿQW\ÿPV[ÿOUZÿOTYÿNTXÿMTWÿMRVÿKRVÿKQUÿJPTÿJOSÿINQÿDILÿ "ÿÿÿÿÿÿEJNÿCHLÿ;=?ï=??ehipsuïz}€ÿ€ƒ†ÿ€„†ÿ|ÿruwÿgjlÏ^ab_ÿ4&1ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ626ÿ ÿ''+ÿINPÿCGJÿ7;>ÿNTXÿRX\ÿV]`ÿZaeÿ[bgÿ[bgÿ[bgÿZafÿZ`eÿX_dÿX_cÿW^bÿV]aÿV\`ÿU[_ÿTZ^ÿSY^ÿRY]ÿRX\ÿMSVÿ #$ÿÿÿÿÿÿOUYÿLRUÿ=?Aÿ>@AÏ;<=VXZ/dgiÏvy{ÿ€ƒ†ÿ‚…‡ÿ„†ÿ‚…ÿ~„ÿ|‚ÿx{~ÿoqsÿ`bdßTVXOÿ7)4ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ726ÿ ÿ%%(ÿDHKÿ?BEÿ479ÿINRÿLRVÿPVZÿTZ^ÿU[_ÿU[_ÿU[_ÿTZ^ÿSZ]ÿSY\ÿRX\ÿQW[ÿPVZÿOUYÿOUXÿNTWÿMSWÿMRVÿLRVÿJOSÿ!#ÿÿÿÿÿÿMSWÿJOSÿ=?AÿFHJÿDEFÏ=?@?HJKQSUŸbdfÿtvxÿz|ÿz}ÿz|ÿy{}ÿwz|ÿvy{ÿuxzÿtvyÿsuwÿnpsÿcdfÿSUW¿NPQÿ7*5ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ737ÿ ÿ""%ÿ?CEÿ:>?ÿ034ÿBGIÿFJMÿINQÿLQTÿNSVÿNSVÿMRUÿMRUÿLQTÿKPSÿKORÿJOQÿINQÿIMPÿHMOÿGLOÿGKNÿFJMÿFJMÿCHJÿÿÿÿÿÿÿHMPÿDILÿ;=>ÿJLMÿNPQÿLMOÿLNOÿTVXÿ`bdÿjlnÿprtÿqsuÿqsuÿqsuÿprtÿnqsÿmprÿloqÿknoÿjlnÿjlmÿhjlÿaceÿPRSï@BBOÿ8,6ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ847ÿ ÿ!"$ÿ?BDÿ:=?ÿ.12ÿACÿ:;<ÿKMNÿQRTÿVWYÿZ\]ÿ_acÿdfhÿikmÿnprÿoqsÿoqsÿoqsÿnprÿlnpÿlmoÿjlnÿjkmÿijlÿhjkÿfhjÿfgiÿ`bdÿPQSÿFHI_/ ÿ8-5ÿÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿgbfÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ†‚…ÿ*#)ÿ  ÿ858ÿ ÿ&&)ÿHKMÿBFGÿ357ÿBDÿ<@AÿÿÿÿÿÿÿAEGÿ=ACÿ?@AÿTUWÿZ[]ÿ_abÿdfhÿjlmÿoqsÿtvxÿz|~ÿ{}ÿ{}ÿ{}ÿy{}ÿxz|ÿwy{ÿvxzÿuwyÿtvxÿsuwÿrtuÿprtÿoqsÿkmoÿ\^_ÿOPQ_? ÿ9/7ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿÂÀÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGBGÿ  ÿ969ÿ ÿCBEÿ€‚„ÿvxzÿ[]^ÿmoqÿrtvÿwz|ÿ|ÿ~ƒÿ~ƒÿ~ƒÿ}€‚ÿ|~€ÿ{}ÿy|~ÿx{}ÿwz|ÿvy{ÿuxzÿtvxÿsuwÿrtvÿqtvÿnprÿ/00ÿÿÿÿÿ())ÿwy{ÿprtÿQSTÿbdeÿijlÿoqrÿuwyÿ|}ÿ‚„…ÿˆŠŒÿŽ’ÿ’”ÿ’”ÿ‘“ÿŽ’ÿŒŽÿ‹ÿŠŒŽÿŒŽÿ’ÿŽ’ÿŽ‘ÿŽ‘ÿ–˜™ÿš›ÿÿefhÿWXY?ÿ907ÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿÂÀÁÿÂÀÁÿ  ÿ  ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ979ÿ ÿSRUÿ ¢¤ÿ“•—ÿuwxÿ—™›ÿŸ ¢ÿ¦¨ªÿ­¯±ÿ°²´ÿ°²´ÿ¯±³ÿ®°²ÿ¬®°ÿ«¬®ÿ©«­ÿ¨©«ÿ¦¨ªÿ¤¦¨ÿ£¥§ÿ¡£¥ÿ ¢¤ÿŸ ¢ÿž ¢ÿ™›ÿFGHÿÿÿÿÿ889ÿ¦¨ªÿœžŸÿdefÿstuÿ~€ÿÿ‘“ÿ’“•ÿ™›ÿ ¢£ÿ¦¨ªÿ¨ª¬ÿ¨ª¬ÿ§©«ÿ¦¨©ÿ§©ªÿ®°±ÿµ¶¸ÿ·¸ºÿ¹º»ÿ»¼½ÿ¼¾¿ÿ¿ÀÁÿÁÂÃÿÃÄÅÿ¥¦¨ÿyz|ÿabc/?ÿ907ÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ  ÿ  ÿ  ÿ¿Áÿ¿Áÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ989ÿ ÿVVWÿ§©©ÿš›œÿ{}~ÿ£¥¦ÿ«­®ÿ³µ¶ÿº¼¾ÿ½¿Áÿ½¿Áÿ¼¾Àÿ»½¾ÿ¹»½ÿ¸º»ÿ¶¸¹ÿ´¶¸ÿ³µ¶ÿ±³´ÿ¯±³ÿ®¯±ÿ¬®¯ÿ«¬®ÿª¬­ÿ¥¦¨ÿSSTÿ444ÿ444ÿ444ÿ===ÿ=>>ÿ³µ¶ÿ¨©«ÿopqÿ‘‘ÿ¼½¾ÿº»¼ÿ°±²ÿ¥¦¨ÿ®°²ÿ¶¸ºÿ½¿Àÿ½¿Áÿ¼¾Àÿ»½¿ÿº¼½ÿÂÃÄÿÄÅÇÿÅÆÇÿÆÇÈÿÇÈÉÿÈÉËÿÉËÌÿËÌÍÿÌÍÎÿ¶·¸ÿ•—˜ÿvxxŸ?ÿ917ÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ  ÿ ÿ ÿ ÿÿÁÿ¿Áÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:9:ÿ ÿZZ\ÿ¯±²ÿ¢¤¥ÿ„…†ÿ³µµÿ¼¾¾ÿÅÆÇÿÍÏÏÿÐÒÒÿÐÒÒÿÏÑÑÿÍÏÐÿÌÎÎÿÊÌÌÿÈÊÊÿÆÈÈÿÄÆÇÿÃÄÅÿÁÃÃÿ¿ÁÁÿ½¿¿ÿ»½½ÿ»¼½ÿµ··ÿabbÿSSSÿSSSÿSSSÿlllÿOPPÿÅÇÇÿ¸ººÿ}ÿÂÂÃÿËÌÌÿÈÉÉÿ¿ÁÁÿ¼½¾ÿÄÆÆÿÏÑÑÿØÚÛÿÚÜÝÿÛÝÝÿÚÜÜÿÙÛÛÿÚÜÜÿÚÜÜÿÚÛÜÿÚÜÜÿÚÜÜÿÚÜÜÿÛÜÜÿÛÜÝÿÊÌÌÿ«­­ÿ‹Ÿ?ÿ827ÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÁÿÿÁÿ ÿ  ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ;:;ÿ ÿ``aÿ»¼½ÿ¬®®ÿŽÿÆÇÇÿÐÑÑÿÙÚÚÿãääÿæççÿæççÿåææÿãääÿáââÿßààÿÝÞÞÿÛÜÜÿÙÚÚÿרØÿÕÖÖÿÓÔÔÿÑÒÒÿÏÐÐÿÎÐÐÿÈÉÉÿqrrÿqqqÿqqqÿqqqÿŽŽŽÿ]]]ÿÚÜÜÿËÌÌÿŒÿËÌÌÿËËËÿ©ªªÿžŸŸÿ±±²ÿÇÈÈÿÙÚÚÿäååÿçèèÿçèèÿçèèÿæççÿåææÿåææÿäååÿãääÿãääÿãääÿáââÿËÌÌÿ¥¦¦ÿƒƒƒ$ #ÿ928ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÂÿÿÂÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ<;<ÿ ÿffgÿÇÉÉÿ¸¹¹ÿ—˜˜ÿÒÒÒÿÜÜÜÿæççÿðððÿóóóÿóóóÿòòòÿðððÿîîîÿìììÿêêêÿèèèÿæææÿäääÿâââÿßààÿÝÝÝÿÛÛÛÿÚÚÚÿÓÓÓÿ}}}ÿÿÿÿŸŸŸÿYZZÿæææÿÕÕÕÿ•••ÿÆÆÆÿžžžïzzzstt?Š‹‹_ŸŸŸßÄÄÅÿäääÿíîîÿïïïÿîïïÿíííÿìììÿëììÿëëëÿêêêÿéééÿáââÿÆÆÆÿ¥¥¥ï‹ŒŒO'"&ÿ827ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÄ¿ÂÿÄ¿Âÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=<=ÿ ÿmmmÿÕÖÖÿÄÅÅÿ™ššÿ¾¿ÀÿÇÈÉÿÑÑÒÿÙÚÛÿÜÝÞÿÜÝÞÿÛÜÝÿÙÚÛÿרÙÿÕÖ×ÿÓÔÕÿÑÒÓÿÐÑÑÿÎÏÐÿÌÍÎÿÊËÌÿÈÉÊÿÆÇÈÿÅÆÇÿ½½¾ÿdeeÿ€€€ÿ€€€ÿ€€€ÿƒƒƒÿGGGÿÊËÌÿ»¼½ÿ€€€ÿ›››ïstt/™šš½¾¾ïàááÿðññÿòóóÿñòòÿðððÿïïïÿîîîÿéééÿÕÖÖÿ³³³ÿœ………*&)ÿ837ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿÅ¿ÂÿÄ¿Âÿ ÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ===ÿ ÿqqrÿÞÞÞÿÌÍÍÿŠ‹Œÿmqtÿrvyÿw|ÿ|„ÿ~ƒ†ÿ~ƒ†ÿ}‚…ÿ|„ÿ{€ƒÿz‚ÿy}ÿx|ÿw{~ÿvz}ÿty|ÿsx{ÿrwzÿquxÿosvÿglnÿ,./ÿÿÿÿÿ')*ÿnruÿfjmÿefgÿkkk/™™™±±±¿ÐÐÐÿãããÿêêêÿìììÿéééÿØØØÿ¸¸¹ÿ¤¤¤Ÿ.)-ÿ948ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÔÏÒÿÅ¿Ãÿÿÿ ÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>>>ÿ ÿsssÿâââÿÐÐÐÿŠ‹‹ß<@D?CGBGJDJMEKNEKNEJNDIMDIMCHLCGKBGKAFJAFI@EH?DH?DG>CF>BE;?B"$&!?DGÿÿÿÿÿŽ€ˆÿÿÿÿ ÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ???ÿ ÿijjÿÏÐÑÿ½¿¿ÿŠ‹‹¿615ÿ848ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿ ÿÿÿÿ ÿ‚pzÿÿÿÿÿñïðÿ= 1ÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@ÿ ÿNOPÿ—šÿ†‰‹ÿcef¿<7;ÿ958ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿ"ÿŸ™ÿÿÿÿÿÉ¿Åÿ4&ÿ%ÿ$ÿ!ÿÿÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿAAAÿ ÿ-02ÿV\_ÿIMPïDFI/ŸFAEÿ:69ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿ ÿ#ÿA 4ÿÿÿÿÿ¼¯·ÿ+ÿ+ÿ*ÿ(ÿ%ÿ"ÿÿÿ ÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBBBÿ ÿ Ÿ:>B?6:=¿JEIÿ;7:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿ ÿÿ ÿ$ÿ'ÿ_@SÿÿÿÿÿpPcÿ0ÿ/ÿ.ÿ,ÿ*ÿ&ÿ#ÿÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿDCDÿ ÿ¿OHMÿ<7;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿ$ÿ(ÿ,ÿ=,ÿòïñÿòïñÿp‚ÿ5!ÿ3 ÿ1ÿ/ÿ+ÿ'ÿ"ÿÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿEDEÿÿ¿RLQÿ<8<ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿÿ"ÿ'ÿ,ÿ1ÿ4!ÿ\0LÿÚÏÖÿÿÿÿÿæßãÿ›€‘ÿB0ÿ3 ÿ/ÿ*ÿ&ÿ ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGFGÿÿ¿VOTÿ=8<ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ ÿ&ÿ+ÿ0ÿ5!ÿ9$ÿ<&ÿ>'ÿ‡`yÿÛÏ×ÿÿÿÿÿòïñÿŽpƒÿ3 ÿ/ÿ)ÿ$ÿÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGFGÿÿ¿YRXÿ>9=ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ#ÿ)ÿ/ÿ4!ÿ9$ÿ>'ÿA)ÿC+ÿD,ÿD+ÿrA`ÿÛÏ×ÿÿÿÿÿš€ÿ3 ÿ-ÿ'ÿ!ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿIHIÿÿ¿\V[ÿ>:>ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ ÿ&ÿ,ÿ3 ÿ8#ÿ>'ÿB*ÿF-ÿH.ÿI/ÿI/ÿH.ÿE,ÿçßäÿÍ¿Èÿ6"ÿ0ÿ*ÿ$ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿJIJÿÿ¿^X]ÿ?:>ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ"ÿ)ÿ0ÿ6"ÿ=&ÿB*ÿG.ÿK0ÿN2ÿO3ÿO3ÿn1XÿƯ¾ÿÿÿÿÿŸ€“ÿ:%ÿ4!ÿ-ÿ'ÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKJKÿÿ ¿a[_ÿ?;?ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ%ÿ,ÿ3 ÿ:%ÿA)ÿG-ÿL1ÿQ4ÿˆQtÿ´§ÿéßåÿÿÿÿÿÿÿÿÿ°¤ÿE,ÿ?(ÿ7#ÿ1ÿ)ÿ#ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿLKLÿÿ  ¿d^bÿ@'ÿE,ÿl1Wÿ½ ²ÿôïòÿÿÿÿÿÿÿÿÿÖÀÎÿ¢qÿj"PÿP3ÿI/ÿB*ÿ;%ÿ4 ÿ,ÿ%ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMLMÿÿ  ¿lfjÿ<7;ÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ"ÿ*ÿ2ÿ:$ÿA*ÿQoÿÿÿÿÿÿÿÿÿ̰Âÿ’RzÿkKÿa?ÿ^=ÿZ:ÿU6ÿN2ÿG-ÿ?(ÿ7#ÿ/ÿ'ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNMNÿÿ ¿tosÿ837ÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ%ÿ-ÿ5!ÿ='ÿF-ÿèßåÿßÏÚÿq"Tÿa?ÿfBÿhCÿgCÿeAÿ`>ÿZ:ÿS5ÿK0ÿC+ÿ:%ÿ2 ÿ*ÿ"ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿONOÿÿ ïvquÿ848ÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ'ÿ/ÿ7#ÿ@)ÿI/ÿÞÏØÿôïòÿœa‡ÿqOÿlFÿnHÿmGÿkEÿeBÿ_=ÿW8ÿO3ÿF-ÿ='ÿ4!ÿ,ÿ$ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿPOPÿÿ ÿytwÿ848ÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ!ÿ)ÿ1ÿ:%ÿC+ÿL1ÿu2\ÿêßæÿÿÿÿÿÿÿÿÿäÏÜÿÁ‘°ÿ§bŽÿ‹3kÿkEÿcAÿ[;ÿR5ÿI/ÿ@)ÿ7#ÿ.ÿ&ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQPQÿÿ ÿ{vyÿ958ÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ"ÿ*ÿ3 ÿ<&ÿF-ÿO3ÿY9ÿb@ÿ‡3hÿ‘°ÿÝÀÓÿÿÿÿÿÿÿÿÿÿÿÿÿõïóÿϰÄÿ¤q’ÿj"PÿL1ÿB*ÿ9$ÿ0ÿ'ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRQRÿÿ ÿ~y|ÿ959ÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ#ÿ,ÿ5!ÿ>(ÿH.ÿR5ÿ\;ÿfBÿpIÿxNÿ~SÿUÿ¡Bÿ¶qžÿÌ ¼ÿíßèÿÿÿÿÿÿÿÿÿǯ¾ÿP9ÿ;%ÿ2ÿ)ÿ!ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSRSÿÿ ÿ{~ÿ:69ÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ$ÿ-ÿ6"ÿ@)ÿJ0ÿU7ÿ_>ÿjEÿtLÿ}Rÿ„WÿˆYÿ‡Yÿ‚UÿzPÿqJÿ„3fÿ ¶ÿÿÿÿÿ¸ ¯ÿ='ÿ3 ÿ*ÿ"ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTSTÿÿ ÿ‚~ÿ:6:ÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ&ÿ.ÿ8#ÿA*ÿL1ÿW8ÿb@ÿnGÿxOÿ‚UÿŠ[ÿŽ]ÿ]ÿ‡YÿSÿuLÿjEÿ‡BnÿêßæÿпÊÿ>(ÿ4!ÿ+ÿ#ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUTUÿÿ ÿƒ€‚ÿ;7:ÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ&ÿ/ÿ9$ÿC+ÿN2ÿY9ÿeAÿpIÿ|Qÿ‡Xÿ_ÿ”bÿškÿ©B…ÿ¹q ÿÌ ¼ÿíßèÿÿÿÿÿôïòÿ‚Qpÿ?)ÿ5"ÿ,ÿ#ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿVUVÿÿ ÿ†ƒ…ÿ<8;ÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ'ÿ0ÿ:%ÿE,ÿO3ÿ[;ÿfCÿ–Bxÿ¶qžÿÒ ÁÿãÀ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÀÑÿ§q“ÿaDÿK1ÿA)ÿ7#ÿ-ÿ$ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿXVXÿÿÿ‡„†ÿ=8<ÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ(ÿ1ÿ;&ÿF,ÿf"MÿÁ µÿõïóÿÿÿÿÿÿÿÿÿøïõÿæÀØÿÜ¡ÇÿÏ‚´ÿ°CŠÿ˜#oÿ}RÿpIÿdAÿX9ÿM1ÿB*ÿ8#ÿ.ÿ%ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿYWYÿÿÿˆ…‡ÿ>9=ÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ(ÿ2 ÿ;&ÿ\!GÿóïòÿÿÿÿÿÇ ¹ÿ©bÿ“#kÿ^ÿœgÿ¦nÿ£lÿ˜dÿ‹\ÿ~SÿrJÿeBÿY9ÿM2ÿB+ÿ8$ÿ.ÿ%ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZXZÿÿÿ‰†‰ÿ>9>ÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ)ÿ2 ÿ;&ÿsAaÿÿÿÿÿ‘RzÿiEÿvMÿƒVÿ_ÿhÿ©pÿ¦mÿ™eÿŒ\ÿTÿrJÿeBÿY:ÿM2ÿC+ÿ8$ÿ.ÿ%ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[Y[ÿÿÿІ‰ÿ?:>ÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ(ÿ2 ÿ;&ÿ\!Gÿÿÿÿÿõïóÿ´¢ÿ3oÿƒVÿ^ÿœgÿ¥nÿ£lÿ˜dÿ‹[ÿ~SÿrJÿeBÿY9ÿM2ÿB+ÿ8$ÿ.ÿ%ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\Z\ÿÿÿІŠÿ@;?ÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿ ÿ(ÿ1ÿ;&ÿF,ÿ{Afÿêßæÿÿÿÿÿÿÿÿÿ÷ïôÿãÀÖÿÓ’¼ÿÊr¬ÿ·Dÿ¢$vÿ‰Zÿ}RÿpIÿdAÿX9ÿL1ÿB*ÿ8#ÿ.ÿ%ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\[\ÿÿ ÿ‹‡Šÿ@<@ÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ(ÿ1ÿ:%ÿE,ÿO3ÿ[;ÿƒ3fÿ¯q™ÿΠ¾ÿèÏàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÀÓÿÀ‘¯ÿ“R{ÿW8ÿK1ÿA)ÿ7#ÿ-ÿ%ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]\]ÿÿ ÿ‹†ŠÿA=Aÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ'ÿ0ÿ9%ÿC+ÿN2ÿY:ÿeAÿpIÿ|Qÿ†Xÿ^ÿ¡$uÿ­CˆÿÅ­ÿР¿ÿåÏÞÿÿÿÿÿÿÿÿÿéßåÿl1Vÿ@)ÿ6"ÿ-ÿ$ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^]^ÿÿ ÿІ‰ÿB=Bÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ&ÿ/ÿ8$ÿB+ÿM1ÿW9ÿb@ÿmHÿxOÿ‚UÿŠ[ÿŽ]ÿ]ÿ‡YÿSÿtLÿsQÿ›a†ÿôïòÿů½ÿ?(ÿ5"ÿ,ÿ#ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_^_ÿÿ ÿŠ…‰ÿD>Cÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ&ÿ.ÿ7#ÿA*ÿK0ÿU7ÿ`>ÿjEÿtLÿ}Rÿ„Wÿ‡Yÿ‡Yÿ‚UÿzPÿqJÿgCÿ¤q’ÿôïòÿ¹ °ÿ='ÿ4!ÿ+ÿ#ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿa_aÿÿ ÿŠ„ˆÿE?Dÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ%ÿ-ÿ6"ÿ?)ÿI/ÿS6ÿ]<ÿgCÿpIÿxOÿ~SÿUÿ€TÿB}ÿ²q›ÿÒ°ÆÿõïóÿÿÿÿÿÝÏØÿ\!Gÿ<&ÿ3 ÿ*ÿ"ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿb`bÿÿ ÿ‰ƒˆÿF@Eÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ#ÿ+ÿ4!ÿ='ÿG-ÿP4ÿY:ÿc@ÿtRÿ¨bÿÍ ½ÿæÏÞÿÿÿÿÿÿÿÿÿÿÿÿÿëßçÿ ¶ÿ•a‚ÿM2ÿC+ÿ:%ÿ1ÿ)ÿ!ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿcacÿÿ ÿˆ‚‡ÿFAFÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ"ÿ*ÿ2 ÿ;&ÿD,ÿM2ÿ`Dÿà¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÛÀÑÿÁ‘°ÿœR‚ÿ}#]ÿdAÿ\;ÿS5ÿJ0ÿA*ÿ8$ÿ/ÿ'ÿ ÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿdadÿÿ ÿ‡‚†ÿHBGÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿ ÿ(ÿ0ÿ8$ÿA*ÿJ/ÿÓ¿Ìÿÿÿÿÿà·ÿ–R~ÿlGÿnHÿmHÿkFÿfBÿ_>ÿW9ÿO3ÿG.ÿ>(ÿ5"ÿ-ÿ%ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿdbdÿÿ ¿‡†ÿHCGÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ&ÿ.ÿ6"ÿ>(ÿF-ÿóïòÿßÏÚÿgHÿb@ÿfBÿhDÿgCÿeBÿ`>ÿZ:ÿS6ÿK1ÿC+ÿ;&ÿ3!ÿ+ÿ$ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿeceÿÿ ¿‚|ÿMHLÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ$ÿ,ÿ3!ÿ;&ÿC+ÿ£€—ÿÿÿÿÿêßæÿ®žÿt"Wÿb?ÿa?ÿ_>ÿ[;ÿU7ÿO3ÿH.ÿ@)ÿ8$ÿ1ÿ)ÿ"ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfdfÿÿ  ¿unsÿZTYÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ"ÿ)ÿ0ÿ8$ÿ?)ÿF-ÿ`~ÿéßåÿÿÿÿÿÿÿÿÿëßæÿ¸ªÿQxÿU7ÿP4ÿK0ÿD,ÿ<'ÿ5"ÿ.ÿ'ÿ ÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgegÿÿ  ¿tmsÿ[U[ÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿ!ÿ'ÿ.ÿ5"ÿ<&ÿB+ÿI/ÿN2ÿg"NÿžqŽÿÔÀÍÿÿÿÿÿÿÿÿÿôïòÿšp‹ÿG.ÿ@*ÿ9%ÿ3!ÿ,ÿ%ÿÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿifiÿÿ  ¿skrÿ]W\ÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿ$ÿ+ÿ2 ÿ8$ÿ>(ÿD,ÿI/ÿM2ÿO3ÿQ4ÿ[Aÿ`ÿèßåÿÿÿÿÿ €”ÿ<'ÿ6#ÿ0ÿ)ÿ"ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjgjÿÿ  ¿qjpÿ^W]ÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿ"ÿ(ÿ.ÿ5"ÿ:%ÿ@)ÿD,ÿH.ÿJ0ÿK1ÿK0ÿI/ÿG.ÿçßäÿοÉÿ8$ÿ2 ÿ,ÿ&ÿ ÿÿÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿkhkÿÿ  ¿phoÿ_X^ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿÿÿÿ%ÿ+ÿ1ÿ6#ÿ;&ÿ?)ÿC+ÿE,ÿF-ÿF-ÿ\!Gÿį½ÿÿÿÿÿ›€‘ÿ4"ÿ/ÿ)ÿ#ÿÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlilÿÿ¿ofmÿ`Y_ÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿ"ÿ(ÿ-ÿ2 ÿ7#ÿ:&ÿ>(ÿ@)ÿY!Eÿ¸ ¯ÿÿÿÿÿÿÿÿÿ¨Ÿÿ5"ÿ1ÿ+ÿ&ÿ!ÿÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmjmÿÿ¿melÿa[aÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿ ÿ$ÿ)ÿ.ÿ2 ÿ6#ÿE2ÿµ ®ÿÿÿÿÿÿÿÿÿ´Ÿ­ÿP ?ÿ5"ÿ1ÿ-ÿ(ÿ#ÿÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmjmÿÿ¿kcjÿb\aÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿ!ÿ&ÿ+ÿ.ÿ2 ÿÙÏÕÿÿÿÿÿ³Ÿ¬ÿO >ÿ5"ÿ3!ÿ1ÿ-ÿ)ÿ$ÿ ÿÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnknÿÿ¿iahÿc]cÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿÿ#ÿ'ÿ*ÿa@Uÿÿÿÿÿ‹pÿ2 ÿ2 ÿ1ÿ/ÿ,ÿ)ÿ%ÿ!ÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿoloÿÿ¯h_gÿe^dÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿÿ ÿ#ÿ'ÿQ0Fÿÿÿÿÿ–€Žÿ.ÿ.ÿ-ÿ+ÿ)ÿ&ÿ"ÿÿÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿplpÿÿ\S[ÿf_fÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿÿ ÿ#ÿ&ÿÉ¿Åÿÿÿÿÿ¢›ÿ7)ÿ)ÿ'ÿ%ÿ"ÿÿÿÿÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqnqÿÿ&&*ÿCHLÿ:>BÏ268PGOÿh`gÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿÿÿÿÿ ÿ#ÿ2&ÿº¯¶ÿÿÿÿÿÖÏÔÿ3'ÿ$ÿ"ÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqnqÿÿ..3ÿSY^ÿGLPÿ6:<¯MDLÿiahÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿ!ÿ"ÿu`mÿÿÿÿÿ‚p|ÿ!ÿÿÿÿÿÿ ÿ ÿ ÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿrorÿÿ./4ÿU\`ÿMSWÿ9=?¿JBIÿkcjÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ  ÿ ÿ ÿÿÿÿÿÿÿÿÿÕÏÓÿÆ¿Äÿÿÿÿÿÿ ÿ ÿ  ÿ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿspsÿÿ--2ÿRX\ÿKQUÿ7;>Ï7;=?9=A?<@D??CG?@EH?@EH??DH??DG??DG?>CF?=BE?=BE?=AD?<@D?;@C?;?B?:?B?:>A?9=@?8A?9=@?9;=`ce/ehkogik_bd_\_`H@Gÿldkÿÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ ÿÿÿÿÿÿÿÿÿÆÀÄÿÆÀÄÿÿÿÿÿÿ ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿuquÿÿ8;?ÿNSWÿGMPÿ8Fÿnfmÿÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ ÿÿÿÿÿÿÿÿÿÆÀÄÿÅÀÄÿÿÿÿÿÿÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿuruÿÿ:<@ÿINQÿCHKÿ8<>ÿNTXÿRX]ÿV]aÿZafÿ\bgÿ\bgÿ[bgÿ[afÿZ`eÿY_dÿX_cÿW^bÿW]bÿV\aÿU[`ÿTZ_ÿSY^ÿSY]ÿRX\ÿMSWÿ!#%ÿÿÿÿÿÿOUYÿLRUÿ=?@ÿ<=?¿MOP\^`¿psuÿ~„ÿ‚„‡ÿ„‡ÿ€ƒ†ÿ‚…ÿ|ÿuxzÿikmÿZ\^¿LMO?D@ÿ035ÿBGJÿFKNÿINQÿLRUÿNSWÿNSWÿMSVÿMRVÿLRUÿKQTÿKPSÿJORÿIORÿINQÿHMPÿGLOÿGLOÿFKNÿFKNÿCHKÿ ÿÿÿÿÿÿHMPÿDILÿ;=>ÿKLMÿOQRÿLNOÿMNPÿUWXÿacdÿjmnÿpsuÿrtvÿrtvÿrtvÿpsuÿoqsÿnprÿmoqÿlnpÿkmoÿjlnÿhjlÿbdeÿRSUßDEF/?+"*ÿtksÿ2+2ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÁÄÿÅÁÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxtxÿÿ236ÿ?BDÿ:=?ÿ.12ÿ<@Bÿ?CEÿBFHÿEILÿFKMÿFKMÿFJMÿEJLÿDIKÿDIKÿCHJÿCGIÿBFIÿAFHÿAEGÿ@DGÿ@DFÿ?CEÿ?CEÿ=ACÿÿÿÿÿÿÿAEHÿ>BDÿ:;<ÿKLNÿQRSÿUWXÿY[]ÿ_abÿdegÿijlÿmoqÿoqsÿoqsÿnprÿmoqÿlnpÿkmoÿjlnÿikmÿhjlÿgikÿfhjÿegiÿ`acÿOQRÿFGH_?) (ÿvlvÿ5-4ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÅÁÄÿÅÁÄÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿyuyÿÿ79<ÿFJKÿ@DFÿ246ÿ;?@ÿ>ACÿAEFÿDHJÿEIKÿEIKÿEIKÿDHJÿCGIÿCGIÿBFHÿBEGÿAEGÿ@DFÿ@DEÿ?CEÿ?BDÿ>BCÿ>BCÿ<@Aÿÿÿÿÿÿÿ@DFÿ=@Bÿ>@AÿSTVÿYZ\ÿ^`aÿcegÿijlÿnprÿsuwÿxz|ÿz|~ÿz|~ÿy{}ÿxz|ÿwy{ÿvxzÿuwyÿtvxÿstvÿrtuÿprtÿoqsÿnprÿjlnÿ[]^ÿNOP_?''ÿxnwÿ7/7ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÅÂÄÿâàáÿŒ…Šÿ‹…Šÿ‹…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…ŠÿŠ…Šÿ3+2ÿ ÿzvzÿÿ__cÿz|~ÿpstÿVXYÿgikÿlnpÿqsuÿvxzÿxz|ÿxz|ÿwz|ÿvy{ÿuxzÿtvxÿsuwÿrtvÿqsuÿprtÿoqsÿnprÿmoqÿlnpÿlnoÿhjlÿ,--ÿÿÿÿÿ&&'ÿqsuÿjlnÿOQRÿabdÿgijÿmoqÿtuwÿz|}ÿ€‚ƒÿ†ˆŠÿŒŽÿŽ’ÿŽ’ÿ‘ÿŒŽÿŠŒŽÿ‰‹ÿˆŠŒÿ‡‰Šÿ‹ÿŽÿŒŽÿŒŽÿŒÿŒÿ„…‡ÿcdfïVWX?&%ÿzoyÿ:19ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÂÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRKQÿÿ{w{ÿÿ}|ÿ ¢¤ÿ“•—ÿuvxÿ–˜šÿžŸ¡ÿ¥§©ÿ¬®°ÿ¯±³ÿ¯±³ÿ®°²ÿ­¯±ÿ«­¯ÿª«­ÿ¨ª¬ÿ§¨ªÿ¥§©ÿ¤¥§ÿ¢¤¦ÿ¡¢¤ÿŸ¡£ÿžŸ¡ÿŸ¡ÿ˜šœÿEFGÿÿÿÿÿ789ÿ¥§©ÿ›Ÿÿcdfÿrstÿz{|ÿˆŠŒÿŽ‘ÿ’”ÿ—™›ÿž ¢ÿ¤¦¨ÿ¦¨ªÿ¦¨ªÿ¥§©ÿ¤¦¨ÿ¤¦¨ÿ«­¯ÿ±²´ÿµ·¸ÿ·¹ºÿ¹º¼ÿ»¼¾ÿ½¾Àÿ¿ÁÂÿÂÃÄÿ§¨ªÿy{|ÿabc/?$$ÿ{pzÿ;3;ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|x|ÿÿ„ÿ¦¨©ÿ™›œÿ{|}ÿ¢£¥ÿ©«­ÿ±³µÿ¹»¼ÿ¼¾¿ÿ¼¾¿ÿ»½¾ÿ¹»½ÿ¸º»ÿ¶¸ºÿ´¶¸ÿ³µ¶ÿ±³µÿ°±³ÿ®°±ÿ¬®¯ÿ«¬®ÿ©«¬ÿ¨ª¬ÿ£¥¦ÿQRSÿ111ÿ111ÿ111ÿ999ÿ<=>ÿ²³µÿ¦¨©ÿnopÿŠŒŒÿ¸¹¹ÿ¹ººÿ®°±ÿ£¥¦ÿ­®°ÿ´¶¸ÿ»¼¾ÿ»½¾ÿº¼½ÿ¹»¼ÿ¸º»ÿÀÁÂÿÂÄÅÿÃÅÆÿÄÆÇÿÅÇÇÿÇÈÉÿÈÉÊÿÉËËÿËÌÍÿ¾¿¿ÿ”–—ÿvwx¯ ÿ}r|ÿMEMÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ}y}ÿÿˆˆŠÿ¯±²ÿ¡£¤ÿƒ„…ÿ±³³ÿº¼¼ÿÃÅÅÿËÍÍÿÎÐÐÿÎÐÐÿÍÏÏÿÌÎÎÿÊÌÌÿÈÊÊÿÆÈÈÿÄÆÆÿÃÅÅÿÁÃÃÿ¿ÁÁÿ½¿¿ÿ»½¾ÿº¼¼ÿ¹»»ÿ³µµÿ`aaÿPPPÿPPPÿPPPÿhhhÿNNNÿÃÅÆÿ¶¸¸ÿ|}}ÿÁÁÂÿËËËÿÇÈÈÿ¿ÀÁÿº¼¼ÿÃÅÅÿÎÏÐÿרÙÿÙÚÛÿÙÛÛÿØÚÚÿÖØØÿÙÚÛÿÙÚÛÿÙÚÛÿÙÚÛÿÙÚÛÿÙÛÛÿÚÛÛÿÚÛÛÿÉÊËÿª«¬ÿ…†‡Ïnpp  ÿ~s}ÿPHPÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿ~z~ÿÿ’ÿº¼¼ÿ«­­ÿŽŽÿÅÆÆÿÏÐÐÿØÙÙÿâããÿåææÿåææÿäååÿâããÿàááÿÞßßÿÜÝÝÿÚÛÛÿØÙÙÿÖ××ÿÔÕÕÿÒÓÓÿÐÑÑÿÎÏÏÿÍÎÎÿÇÈÈÿpppÿoooÿoooÿoooÿŒŒŒÿ]]]ÿÙÚÚÿÊËËÿˆ‰‰ÿÌÌÌÿÏÏÏÿ³´´ÿ«¬¬ÿº»»ÿÌÌÌÿÙÚÚÿãääÿæççÿæççÿæççÿåææÿååæÿäååÿãääÿãääÿãããÿããäÿáââÿÒÓÓÿ±±±ÿŽŽŽŸ ÿ€tÿTKTÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ%$ÿ{ÿ  ÿššœÿÆÈÈÿ·¸¹ÿ——˜ÿÑÑÑÿÛÛÛÿåææÿïïïÿòòòÿòòòÿñññÿïïïÿíííÿëëëÿéééÿçççÿåååÿãããÿáááÿÞßßÿÜÜÜÿÚÚÚÿÙÚÚÿÒÒÒÿ|||ÿŽŽŽÿŽŽŽÿŽŽŽÿžžžÿZZZÿåååÿÔÔÔÿ•––ÿÈÈÈÿ£¤¤ÿ‡‡‡‰‰‰?–——®®®ïÏÐÐÿèèèÿíîîÿïïïÿîîïÿíííÿëììÿëëëÿêêëÿéêêÿèééÿäååÿÏÐÐÿ³³³ï˜˜˜_ÿ€uÿWMVÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ('ÿ€|€ÿ  ÿ¥£¥ÿÔÕÕÿÄÄÄÿš››ÿÃÄÄÿÌÍÍÿÖ××ÿßààÿáâãÿáâãÿàáâÿßààÿÝÞÞÿÛÜÜÿÙÙÚÿרØÿÕÖÖÿÓÔÔÿÑÒÒÿÏÐÐÿÍÎÎÿËÌÌÿÊËËÿÁÂÂÿqqrÿ­­­ÿ­­­ÿ­­­ÿ¥¥¥ÿIIIÿÏÐÐÿÀÁÁÿ‡‡‡ÿ¤¤¤ï~~~/’’’¬¬¬ÉÉÉÿèèèÿòòòÿóóóÿñññÿðððÿïïïÿîîîÿìììÿÞÞÞÿÂÂÂÿ©©©¯•••ÿv€ÿ]T\ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ+*ÿ}ÿ  ÿ¬«­ÿÝÞÞÿÌÌÌÿ‹ÿswzÿx|ÿ~‚…ÿƒ‡‹ÿ…‰Œÿ…‰Œÿ„ˆ‹ÿƒ‡‹ÿ‚†Šÿ…ˆÿ„‡ÿ~ƒ†ÿ}‚„ÿ|€ƒÿ{‚ÿz~ÿy|€ÿw{~ÿuy|ÿmqtÿ/12ÿÿÿÿÿ*+,ÿtx{ÿlpsÿhiiÿooo?©©©/ÃÃÿÞÞÞÿìììÿðððÿðððÿîîîÿâââÿÉÉÉÿ±±±¿žžž?¿f[eÿkajÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ/".ÿ‚}‚ÿ  ÿ°®°ÿâââÿÐÐÐÿŠ‹‹ß<@D?CGBGJDJMEKNEKNEJNDIMDIMCHLCGKBGKAFJAFI@EH?DH?DG>CF>BE;?B"$&?DG2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ>2=ÿ…€…ÿ  ÿ**/Ÿ>CF;?B/ I?Iÿ|q{ÿNBMÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6BÿC6Bÿ‡†ÿ!!ÿ ? J@Iÿt~ÿSFQÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:EÿG:Eÿ‡‚‡ÿ!!ÿ ? J@IÿƒxÿWJUÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?JÿL?Jÿˆƒˆÿ!!ÿ ? O2)2ÿ†|„ÿeZdÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿQDOÿ‰„‰ÿ!!ÿ ? ?*!*ÿ‰ˆÿi^hÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿUHTÿŠ…Šÿ!!ÿ ? ?*"*ÿŒ‚‹ÿncmÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿZNYÿ‹†‹ÿ!!ÿ ? ?+"+ÿ†Žÿtjsÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ_S^ÿ‹‡‹ÿ!!ÿ ? ÿ“Š’ÿv~ÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿdYcÿŒˆŒÿ""ÿ ? ÿ––ÿ„zƒÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿj^iÿˆÿ""ÿ ? ÿš‘™ÿˆˆÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿpdoÿމŽÿ""ÿ ? ßy€ÿ”‹“ÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿujtÿŠÿ""ÿ ? ¿{szÿ˜‘˜ÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ{rzÿ‹ÿ##ÿ ? ¿~v}ÿ–œÿ„{ƒÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿx€ÿ‘Œ‘ÿ##ÿ ? ¯ZRYÿŸ˜žÿކÿކÿކŽÿ‡ŽÿˆŽÿˆÿ‘‰ÿ‘‰ÿ‘Šÿ’Š‘ÿ’Š‘ÿ’‹‘ÿ“‹’ÿ“‹’ÿ“Œ’ÿ”Œ“ÿ”Œ“ÿ”“ÿ”“ÿ”“ÿ•”ÿ•”ÿ•”ÿ•”ÿ•”ÿ•”ÿ•”ÿ•”ÿ•”ÿ•”ÿ•”ÿ•”ÿ•”ÿ”“ÿ’Š‘ÿ‘‰ÿ‘‰ÿ‘‰ÿ‘‰ÿ‘‰ÿ‘‰ÿˆÿˆÿˆÿˆÿˆÿ‡Žÿ‡Žÿ‡Žÿ‡ŽÿކŽÿކÿކÿŽ…ÿ’’ÿ##ÿ ? O ÿKCKÿgagÿhbhÿhbhÿibiÿiciÿ’’ÿ › ÿ œ ÿ¡œ¡ÿ¡¡ÿ¢¢ÿ¢ž¢ÿ½º½ÿÜÚÜÿÜÚÜÿÜÛÜÿÝÛÝÿÝÛÝÿÝÛÝÿÝÜÝÿÝÜÝÿÞÜÞÿÞÜÞÿÞÜÞÿÞÜÞÿÞÜÞÿÞÜÞÿÞÛÞÿÞÛÝÿÞÚÝÿÞÚÝÿÞÙÝÿÞÙÜÿÞØÜÿß×ÜÿßÖÜÿßÕÜÿßÕÜÿßÕÜÿàÔÜÿàÔÜÿàÔÜÿàÔÜÿàÔÜÿàÔÜÿàÔÜÿàÕÜÿßÕÜÿßÖÜÿßÖÜÿß×ÝÿߨÝÿߨÝÿß×Üÿ›–›ÿ##ÿ ? /       ¿ ¿ ¿ ¿ ¿ ¿ ¿ ß ÿ ÿ ÿ ÿ ÿ ÿ ÿ''ÿE=EÿE=EÿE=EÿE=EÿE=EÿE=DÿE71+$ Ÿ_N\ÿgWdÿiXeÿk[hÿm]jÿo_lÿqbnÿsdpÿufrÿwhtÿsdpÿqamÿo`lÿn^kÿm]iÿk[hÿjZgÿiXeÿgVdÿfUbÿiYfÿÔSNID?:50+&! ßTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿTAPÿYGVÿÀ ÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿF3BÿM:Iÿ ¿/!ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿ;'7ÿB/>ÿ ¿?ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ0,ÿ8'5ÿ ¿_ ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ&#ÿ0 -ÿ ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)&ÿ# %ÿMSXïÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ# ÿ! $ÿIORÿQW[ÿX^cÿW]bÿU[_ÿRX]ÿPVZÿNSWÿÿÿINRÿ8BDÿGKNÿMRUÿLQTÿJNQÿHLOÿFJMÿDILÿÿÿAEGÿ468ÿ''(ß456ŸMOPÿlnpÿprtÿhjlÿRTUÿ001¯ ¿  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿgbfÿ…‚…ÿ…‚…ÿ…‚…ÿ…‚…ÿ…‚…ÿ…‚…ÿ…‚…ÿ…‚…ÿ…‚…ÿ…‚…ÿ727ÿ'&)ÿ_bdÿ_bdÿfjlÿehjÿbfhÿ`ceÿ]`bÿ\_`ÿÿÿWZ\ÿJKMÿXYZÿnprÿ~€‚ÿ„†ˆÿƒ…ÿƒ…ÿƒ…‡ÿ{}~ÿQRSÏ¿ÿÿÿÿÿÿÿÿ ÿ  ÿ†€„ÿ ÿÿÿÿÿÿÿÿÿÿÿ98:ÿ ¢£ÿ¯±²ÿ½¿Àÿº¼½ÿ¶¸¹ÿ±³´ÿ¬®¯ÿ©«¬ÿ555ÿ???ÿ¡£¤ÿƒ„ÿ¡¡¢ÿ¡£¤ÿ°²³ÿ¶¸¹ÿ¸º¼ÿ¿ÁÂÿÃÄÆÿ»¼½ÿdefÏ¿ÿÿÿÿÿÿÿ ÿ ÿ ÿˆ€…ÿ ÿ ÿÿÿÿÿÿÿÿÿÿA@Aÿ½¿¿ÿÙÚÚÿêêêÿæççÿáááÿÛÜÜÿÕÖÖÿÑÒÒÿ‡‡‡ÿ{{{ÿÈÉÉÿ§§§ÿïz{|Ï«¬¬ÿÚÛÜÿàááÿÜÝÝÿÂÂÃÿyzz¿CDE¿ÿÿÿÿÿÿÿ ÿ ÿÿ‹€‡ÿ ÿ ÿÿÿÿÿÿÿÿÿÿGGGÿÒÒÒÿ†‰‹ÿ“•ÿŽ‘“ÿŠÿ†‰‹ÿƒ†ˆÿ~ƒÿ...ÿ%%%ÿz}~ÿ_`aÿ\\\fff?ŒŒŒß¬¬¬ÿšššßnnn_¿ ÿÿÿÿÿÿ ÿ ÿÿs`kÿ‚p{ÿÿ ÿ ÿÿÿÿÿÿÿÿÿ/01ÿuz}ÿ¿ÿÿÿÿÿÿ ÿÿ&ÿ¤œÿ.ÿ(ÿÿ ÿÿÿÿÿÿÿÿÿ¿Ïÿÿÿÿÿ ÿÿ%ÿ2ÿž€’ÿ¨ŸÿM <ÿ)ÿÿ ÿÿÿÿÿÿÿÿ¿ÿÿÿÿÿÿ ÿÿ.ÿ='ÿH.ÿU<ÿï¼ÿ3 ÿ"ÿ ÿÿÿÿÿÿÿÿ¿ÿÿÿÿÿÿ ÿ$ÿ7#ÿuAbÿ¾ ³ÿ·©ÿzAeÿ='ÿ)ÿÿÿÿÿÿÿÿÿ¿  ÿÿÿÿÿÿÿ+ÿA)ÿÖÀÎÿpOÿiDÿ\;ÿG-ÿ1ÿÿ ÿÿÿÿÿÿÿ¿ ÿÿÿÿÿ ÿÿ0ÿH.ÿ’R{ÿÄ‘²ÿÌ ¼ÿ² ÿd!Lÿ6#ÿ ÿ ÿÿÿÿÿÿÿ¿ ÿ!!ÿÿÿÿ ÿÿ4!ÿO3ÿlFÿ…Wÿ‰Zÿ}WÿÕÀÍÿ;&ÿ#ÿ ÿÿÿÿÿÿÿ¿ ÿ$"$ÿÿÿÿ ÿÿ7#ÿg"Nÿ¯q™ÿÕ¡ÃÿË‚±ÿÅ‘³ÿƒAkÿ>(ÿ%ÿ ÿÿÿÿÿÿÿ¿ÿ&$&ÿÿÿÿ ÿ ÿ8$ÿ˰Áÿ‡#dÿ—dÿ jÿTÿ]<ÿ?)ÿ&ÿ ÿÿÿÿÿÿÿ¿ÿ)')ÿÿÿÿ ÿÿ7#ÿ‡QsÿÊ »ÿÕ¡ÃÿÓ‘¼ÿ¬b’ÿo"Sÿ>(ÿ%ÿ ÿÿÿÿÿÿÿ¿ÿ,),ÿÿÿÿ ÿÿ5"ÿO3ÿlFÿ„WÿˆZÿŽ3mÿ̰Âÿ;&ÿ$ÿ ÿÿÿÿÿÿÿ  ¿ÿ.+.ÿÿÿÿ ÿÿ1ÿI/ÿu"Wÿ³qœÿÍ ¾ÿ¼‘­ÿ„Qrÿ7#ÿ!ÿ ÿÿÿÿÿÿÿ!!¿ÿ0-0ÿÿÿÿ ÿÿ+ÿA*ÿ˰Áÿy#ZÿhDÿ[;ÿH.ÿ1ÿÿ ÿÿÿÿÿÿÿ""¿ÿ3/3ÿÿÿÿÿÿ&ÿ9%ÿQoÿÀ ´ÿ¸©ÿe"Mÿ>(ÿ+ÿÿ ÿÿÿÿÿÿÿ##¿ÿ515ÿÿÿÿÿ ÿ ÿ0ÿ?)ÿI/ÿU=ÿį¼ÿ5"ÿ$ÿÿ ÿÿÿÿÿÿ  ÿ$ $¿Ï737ÿÿÿÿÿ ÿÿ'ÿ3!ÿyPjÿοÉÿsPgÿ+ÿÿ ÿÿÿÿÿÿÿ!!ÿ% %¿¿959ÿÿÿÿÿ ÿ ÿÿ)ÿ±Ÿªÿ0ÿ+ÿ"ÿÿ ÿÿÿÿÿÿÿ""ÿ&!&¿  ¿<7<ÿ  ÿÿÿÿÿ ÿÿ ÿž™ÿx`oÿ"ÿÿ ÿ ÿÿÿÿÿÿÿ$$ÿ205ÿLQVï  ¿?:?ÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿÿÿÿŽ€‰ÿÿÿ ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ  ÿ&!&ÿ1/4ÿIORÿHMQÿNTXÿNSWÿLQUÿJOSÿHMQÿFJNÿÿÿAFIÿ59;ÿ355IKMŸUWY¿KMN<>?¿C=CÿÿÿÿÿÿÿÿÿÿŒ‚‰ÿÿÿÿÿÿÿÿÿÿÿ+%+ÿ/,1ÿ>BDÿGKNÿMRUÿLQTÿJNQÿHLOÿFJMÿDILÿÿÿAEGÿ478ÿ+,,¿9:;SUWïoqtÿsuwÿkmoÿTVWÿ9:;¿HAHÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿŒ„Šÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ1)1ÿ95:ÿ_bdÿ_bdÿfjlÿehjÿbfhÿ`ceÿ]`bÿ\_`ÿÿÿWZ\ÿGIKÿOPRÿfgiÿwy{ÿ}ÿz|~ÿwy{ÿtvxÿdfhÿBCDŸ¯MFMÿ"!ÿÿÿÿÿÿÿÿÿqjpÿŽˆÿŽˆÿŽˆÿŽˆÿŽˆÿŽˆÿŽˆÿŽˆÿŽˆÿŽˆÿTMTÿKGKÿ ¢£ÿ¯±²ÿ½¿Àÿº¼½ÿ¶¸¹ÿ±³´ÿ¬®¯ÿ©«¬ÿ)))ÿ...ÿ¡£¤ÿ{}~ÿš›œÿœž ÿ§©ªÿ®°±ÿª¬®ÿ¦¨ªÿ¢¤¦ÿ–˜™ÿXYZïSKRÿ*)ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ""ÿ?5>ÿSOSÿ½¿¿ÿÙÚÚÿêêêÿæççÿáááÿÛÜÜÿÕÖÖÿÑÒÒÿ{{{ÿuuuÿÉÉÉÿ¨©©ÿ¤¤¤ÿ”••ß»½½ÿÖØØÿÔÖÖÿÎÐÐÿ»¼¼ÿƒƒßNOP/KAJÿ8,6ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿ,*ÿGŸ6-6¿6-6¿6-6¿MEM¿d\d¿d\d¿d\d¿d\d¿d\d¿d\d¿d\d¿d\d¿]U]ÏNEMÿNEMÿUMTÿ%%à?ºÉàÿÿà?øàÿàà?ÿÿàÀ?ÿÿàÀ?ÿÿàÀ?ÿÿàÀÿÿàÀÀÀÀÀÀÀÀÀÀÀÀÀÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀ?ÿÿÀÀÿÿÀÀÀÀÀÀÀÀÀÀÀÀÀÀÿßÀÀ?ÿÿÀÀ?ÿÿàÀ?ÿÿàà?ÿÿàà?ÿÿàà?ÿÿàà?ÿÿà( @ %gs‡‚|vpjertqmaB<60$2%0WITÿbU`ÿmakÿmakÿqfoÿxmvÿ}s{ÿ{qyÿ…Žÿ–Š“ÿ‹ˆÿs~ÿvgsÿTFQÿƒ]TK<) 1"/ßP-;ÿ?,)ÿ=)9ÿ=)9ÿ=)9ÿ=)9ÿ=)9ÿ=)9ÿ=)9ÿ=)9ÿ=)9ÿ=)9ÿ=)9ÿ=)9ÿ=)9ÿ/,ÿ?# ÿ-)ÿ-)ÿ-)ÿ-)ÿ-)ÿ-)ÿ-)ÿ-)ÿ-)ÿ-)ÿ-)ÿ-)ÿ-)ÿ# ÿ?? ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ:(ÿ ÿ ÿÿÿÿ?ÿÿ ÿ'ÿD,ÿ“R{ÿÄ‘²ÿÇ ¹ÿs1[ÿ8$ÿÿ ÿÿÿÿ?ÿÿ ÿ!ÿ:%ÿ›pŒÿÆ ¸ÿ†BmÿI/ÿ0ÿÿÿÿÿÿ?ÿÿ ÿÿ.ÿB*ÿd!LÿÑ¿Ëÿ:&ÿ&ÿ ÿÿÿÿÿ_ÿÿÿ ÿ#ÿ2 ÿÁ¯ºÿP ?ÿ,ÿÿ ÿÿÿÿÿ=@Eÿ]di/ÿÿÿ ÿÿ$ÿ¡›ÿD!7ÿ!ÿÿ  ÿÿÿÿÿ348ÿMSWÿU\`ÿSY]ÿPVZÿMSWÿÿ48:ÿOTXïV\aÿPVZïFKN  ÿ  ÿ  ÿ ÿÿÿXCQÿWCPÿÿÿ  ÿ  ÿ  ÿ  ÿ  ÿYY\ÿadfÿknpÿhkmÿdgiÿadfÿÿDFHÿgklÿhlmÿbegÿQSTo ÿ ÿ ÿ ÿ ÿ ÿVHRÿª¢¨ÿŒ„Šÿ‰„‰ÿ‰„‰ÿ‰„‰ÿ‰„‰ÿ‰„‰ÿ/'.ÿ‹ŠŒÿÅÇÇÿØÚÚÿÒÔÔÿËÌÌÿÄÅÅÿaaaÿ¸¹¹ÿÒÓÔÿÔÕÖÿÈÉÊÿ¢¤¥_?ÿ! ÿ! ÿ! ÿ! ÿ! ÿ" ÿ" ÿ! ÿ! ÿ! ÿ! ÿ! ÿ! ÿÿ›šœÿ‰¿–š¿’–™¿Œ“¿ˆ‹Ž¿mmm¿†ˆŠ¿¥¨ª¯‰ÿ‘”–ÏÝÝÝ?%$ÿ.".ÿ.".ÿ.".ÿ.".ÿ.".ÿ.".ÿ.".ÿ.".ÿ.".ÿ.".ÿ.".ÿ.".ÿ.".ÿ%$ÿ<1=ÿ>1=ÿ>1=ÿ>1=ÿ>1=ÿ>1=ÿ>1=ÿ>1=ÿ>1=ÿ>1=ÿ>1=ÿ>1=ÿ>1=ÿ0$/ÿ? >2=ÿPCNÿPCNÿPCNÿPCNÿPCNÿPCNÿPCNÿPCNÿPCNÿPCNÿPCNÿPCNÿPCNÿ>2=ÿ ?A6@ÿdXcÿdXcÿdXcÿdXcÿdXcÿdXcÿdXcÿdXcÿdXcÿdXcÿdXcÿdXcÿdXcÿMBLÿ ?NDMß‚yÿƒz‚ÿ„{ƒÿ„|ƒÿ…|„ÿ…|„ÿ…|„ÿ…|„ÿ…|„ÿ…|„ÿ„{ƒÿƒz‚ÿ‚yÿcZcÿ ?F=E€z€¿{€¿{¿‚|¿‚|‚¿™”™Ï‡‡ÿ‡‡ÿ••ÿ£ž¢ÿ¢ž¢ÿ¢ž¢ÿ¡¡ÿ‘Œ‘ï ÀÀ?À?ÿÀ?ÿ€?ÿ€?ÿ€€€€€?ÿ€?ÿ€?ÿ€?ÿ€?ÿ€?ÿ€?ÿ€?ÿ€?ÿ€?ÿ€?ÿ€?߀€€€€?ÿ€?ÿ€?ÿÀ?ÿÀ?ÿÀ?ÿ(  eSaMr`nmdS`dS`dS`dS`dS`RBOO_<)8ÿE0@ÿE0@ÿE0@ÿE0@ÿE0@ÿE0@ÿ0 -¿%!ÿ%!ÿ&!ÿ&!ÿ&!ÿ%!ÿ%!ÿ#"ßÿÿÿA+9ÿvirÿqjoÿojnÿÿgfgÿgfgÿgfgÿ767ÿgfgÿhghŸÿ ÿ%ÿvZkÿ9-ÿ ÿÿ#"#ÿÿµ´´ÿº¹¹ÿ^]]ÿÿÿÿÿ±°°Ÿÿ ÿ='ÿ[Cÿ‰g|ÿÿÿñ ÿ/ÿX9ÿ¨h‘ÿeJÿ,ÿ ÿí ÿ9$ÿnHÿ›oÿ¥gÿ6"ÿ ÿø ÿ:%ÿoIÿÀi¡ÿzYÿ7#ÿ ÿö ÿ2 ÿ]<ÿƒ_ÿœg‰ÿ0ÿ ÿé ÿ%ÿC+ÿZ{ÿA*ÿ#ÿ ÿÝ  ÿÿ-ÿT)Eÿ€hxÿuhqÿmhmÿÿgfgÿgfgÿgfgÿ767ÿgfgÿhghŸÿ ÿ(!ÿ,#ÿ(!ÿ ÿÿ;4;ÿÿµ´´ÿº¹¹ÿ^]]ÿÿÿÿÿ±°°Ÿ8+7ÿ8+7ÿ9*6ÿ:)7ÿ9*7ÿ8+7ÿ8+7ÿ('¿ VJUÿ]P[ÿ]P[ÿ]P[ÿ]P[ÿ]P[ÿ]P[ÿA5@¿ ?NEMÿe\dÿe\dÿŠ‚‰ÿŠ‚‰ÿŠ‚‰ÿŠ‚‰ÿg_f¯ÀÀ€qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/aaxwrapper_description.h0000644000000000000000000000013215124701711027453 xustar0030 mtime=1767080905.277413924 30 atime=1767080905.277413924 30 ctime=1767080905.277413924 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/aaxwrapper_description.h0000644000175000001440000000411615124701711027445 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_description.h // Created by : Steinberg, 08/2017 // Description : VST 3 -> AAX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma once #include "base/source/fstring.h" using namespace Steinberg; struct AAX_Aux_Desc { const char* mName; int32 mChannels; // -1 for same as output channel }; struct AAX_Meter_Desc { const char* mName; uint32 mID; uint32 mOrientation; // see AAX_EMeterOrientation uint32 mType; // see AAX_EMeterType }; struct AAX_MIDI_Desc { const char* mName; uint32 mMask; }; struct AAX_Plugin_Desc { const char* mEffectID; // unique for each channel layout as in "com.steinberg.aaxwrapper.mono" const char* mName; uint32 mPlugInIDNative; // unique for each channel layout uint32 mPlugInIDAudioSuite; // unique for each channel layout int32 mInputChannels; int32 mOutputChannels; int32 mSideChainInputChannels; AAX_MIDI_Desc* mMIDIports; AAX_Aux_Desc* mAuxOutputChannels; // zero terminated AAX_Meter_Desc* mMeters; uint32 mLatency; }; struct AAX_Effect_Desc { const char* mManufacturer; const char* mProduct; uint32 mManufacturerID; uint32 mProductID; const char* mCategory; TUID mVST3PluginID; uint32 mVersion; const char* mPageFile; AAX_Plugin_Desc* mPluginDesc; }; // reference this in the Plug-In to force inclusion of the wrapper in the link extern int AAXWrapper_linkAnchor; AAX_Effect_Desc* AAXWrapper_GetDescription (); // to be defined by the Plug-In /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/aaxwrapper_parameters.h0000644000000000000000000000013215124701711027273 xustar0030 mtime=1767080905.277413924 30 atime=1767080905.277413924 30 ctime=1767080905.277413924 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/aaxwrapper_parameters.h0000644000175000001440000001622515124701711027271 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_parameters.h // Created by : Steinberg, 08/2017 // Description : VST 3 -> AAX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma once #include "public.sdk/source/vst/basewrapper/basewrapper.h" #include "AAX_CEffectParameters.h" #include "AAX_Push8ByteStructAlignment.h" class AAXWrapper; struct AAX_Plugin_Desc; //------------------------------------------------------------------------ // helper to convert to/from AAX/Vst IDs struct AAX_CID { char str[10] {0}; AAX_CID () {} AAX_CID (Steinberg::Vst::ParamID id) { set (id); } void set (Steinberg::Vst::ParamID id) { snprintf (str, 10, "p%lX", static_cast (id)); } operator const char* () const { return str; } }; Steinberg::Vst::ParamID getVstParamID (const char* aaxid); #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wignored-attributes" #pragma clang diagnostic ignored "-Wincompatible-ms-struct" #endif //------------------------------------------------------------------------ class AAXWrapper_Parameters : public AAX_CEffectParameters { public: // Constructor AAXWrapper_Parameters (int32_t plugIndex); ~AAXWrapper_Parameters (); static AAX_CEffectParameters* AAX_CALLBACK Create (); // Overrides from AAX_CEffectParameters AAX_Result EffectInit () SMTG_OVERRIDE; AAX_Result ResetFieldData (AAX_CFieldIndex index, void* inData, uint32_t inDataSize) const SMTG_OVERRIDE; AAX_Result NotificationReceived (AAX_CTypeID iNotificationType, const void* iNotificationData, uint32_t iNotificationDataSize) SMTG_OVERRIDE; /* Parameter information */ AAX_Result GetNumberOfParameters (int32_t* oNumControls) const SMTG_OVERRIDE; AAX_Result GetMasterBypassParameter (AAX_IString* oIDString) const SMTG_OVERRIDE; AAX_Result GetParameterIsAutomatable (AAX_CParamID iParameterID, AAX_CBoolean* oAutomatable) const SMTG_OVERRIDE; AAX_Result GetParameterNumberOfSteps (AAX_CParamID iParameterID, int32_t* oNumSteps) const SMTG_OVERRIDE; AAX_Result GetParameterName (AAX_CParamID iParameterID, AAX_IString* oName) const SMTG_OVERRIDE; AAX_Result GetParameterNameOfLength (AAX_CParamID iParameterID, AAX_IString* oName, int32_t iNameLength) const SMTG_OVERRIDE; AAX_Result GetParameterDefaultNormalizedValue (AAX_CParamID iParameterID, double* oValue) const SMTG_OVERRIDE; AAX_Result SetParameterDefaultNormalizedValue (AAX_CParamID iParameterID, double iValue) SMTG_OVERRIDE; AAX_Result GetParameterType (AAX_CParamID iParameterID, AAX_EParameterType* oParameterType) const SMTG_OVERRIDE; AAX_Result GetParameterOrientation (AAX_CParamID iParameterID, AAX_EParameterOrientation* oParameterOrientation) const SMTG_OVERRIDE; AAX_Result GetParameter (AAX_CParamID iParameterID, AAX_IParameter** oParameter) SMTG_OVERRIDE; AAX_Result GetParameterIndex (AAX_CParamID iParameterID, int32_t* oControlIndex) const SMTG_OVERRIDE; AAX_Result GetParameterIDFromIndex (int32_t iControlIndex, AAX_IString* oParameterIDString) const SMTG_OVERRIDE; AAX_Result GetParameterValueInfo (AAX_CParamID iParameterID, int32_t iSelector, int32_t* oValue) const SMTG_OVERRIDE; /** Parameter setters and getters */ AAX_Result GetParameterValueFromString (AAX_CParamID iParameterID, double* oValue, const AAX_IString& iValueString) const SMTG_OVERRIDE; AAX_Result GetParameterStringFromValue (AAX_CParamID iParameterID, double iValue, AAX_IString* oValueString, int32_t maxLength) const SMTG_OVERRIDE; AAX_Result GetParameterValueString (AAX_CParamID iParameterID, AAX_IString* oValueString, int32_t iMaxLength) const SMTG_OVERRIDE; AAX_Result GetParameterNormalizedValue (AAX_CParamID iParameterID, double* oValuePtr) const SMTG_OVERRIDE; AAX_Result SetParameterNormalizedValue (AAX_CParamID iParameterID, double iValue) SMTG_OVERRIDE; AAX_Result SetParameterNormalizedRelative (AAX_CParamID iParameterID, double iValue) SMTG_OVERRIDE; /* Automated parameter helpers */ AAX_Result TouchParameter (AAX_CParamID iParameterID) SMTG_OVERRIDE; AAX_Result ReleaseParameter (AAX_CParamID iParameterID) SMTG_OVERRIDE; AAX_Result UpdateParameterTouch (AAX_CParamID iParameterID, AAX_CBoolean iTouchState) SMTG_OVERRIDE; /* Asynchronous parameter update methods */ AAX_Result UpdateParameterNormalizedValue (AAX_CParamID iParameterID, double iValue, AAX_EUpdateSource iSource) SMTG_OVERRIDE; AAX_Result _UpdateParameterNormalizedRelative (AAX_CParamID iParameterID, double iValue); AAX_Result _GenerateCoefficients (); /* Chunk methods */ AAX_Result GetNumberOfChunks (int32_t* numChunks) const SMTG_OVERRIDE; AAX_Result GetChunkIDFromIndex (int32_t index, AAX_CTypeID* chunkID) const SMTG_OVERRIDE; AAX_Result GetChunkSize (AAX_CTypeID chunkID, uint32_t* oSize) const SMTG_OVERRIDE; AAX_Result GetChunk (AAX_CTypeID chunkID, AAX_SPlugInChunk* oChunk) const SMTG_OVERRIDE; AAX_Result SetChunk (AAX_CTypeID chunkID, const AAX_SPlugInChunk* iChunk) SMTG_OVERRIDE; AAX_Result CompareActiveChunk (const AAX_SPlugInChunk* iChunk, AAX_CBoolean* oIsEqual) const SMTG_OVERRIDE; AAX_Result _GetNumberOfChanges (int32_t* oValue) const; // Override this method to receive MIDI // packets for the described MIDI nodes AAX_Result UpdateMIDINodes (AAX_CFieldIndex inFieldIndex, AAX_CMidiPacket& inPacket) SMTG_OVERRIDE; AAXWrapper* getWrapper () { return mWrapper; } void setDirty (bool state); private: Steinberg::int32 getParameterInfo (AAX_CParamID aaxId, Steinberg::Vst::ParameterInfo& paramInfo) const; AAXWrapper* mWrapper = nullptr; std::vector mParamNames; AAX_Plugin_Desc* mPluginDesc = nullptr; std::string mChannelName; std::string mSessionPath; bool mSimulateBypass = false; bool mPresetOpened = false; }; #ifdef __clang__ #pragma clang diagnostic pop #endif #include "AAX_PopStructAlignment.h" /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/aaxwrapper_parameters.cpp0000644000000000000000000000013215124701711027626 xustar0030 mtime=1767080905.277413924 30 atime=1767080905.277413924 30 ctime=1767080905.277413924 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/aaxwrapper_parameters.cpp0000644000175000001440000006701415124701711027626 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format auto // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_parameters.cpp // Created by : Steinberg, 08/2017 // Description : VST 3 -> AAX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #include "aaxwrapper_parameters.h" #include "aaxwrapper.h" #include "aaxwrapper_description.h" #include "AAX_CBinaryDisplayDelegate.h" #include "AAX_CBinaryTaperDelegate.h" #include "AAX_CLinearTaperDelegate.h" #include "AAX_CNumberDisplayDelegate.h" #include "AAX_CUnitDisplayDelegateDecorator.h" #include "../hosting/hostclasses.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/base/futils.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivstchannelcontextinfo.h" using namespace Steinberg; using namespace Steinberg::Vst; using namespace Steinberg::Base::Thread; #define USE_TRACE 1 #if USE_TRACE #define HAPI AAX_eTracePriorityHost_Normal #define HLOG AAX_TRACE #else #define HAPI 0 #if SMTG_OS_WINDOWS #define HLOG __noop #else #define HLOG(...) \ do \ { \ } while (false) #endif #endif SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory (); const char* kBypassId = "Byp"; ParameterInfo kParamInfoBypass = {CCONST ('B', 'y', 'p', 0), STR ("Bypass"), STR ("Bypass"), STR (""), 1, 0, -1, Vst::ParameterInfo::kCanAutomate | Vst::ParameterInfo::kIsBypass}; //------------------------------------------------------------------------ // AAXWrapper_Parameters //------------------------------------------------------------------------ AAXWrapper_Parameters::AAXWrapper_Parameters (int32_t plugIndex) : AAX_CEffectParameters (), mSimulateBypass (false) { HLOG (HAPI, "%s", __FUNCTION__); AAX_Effect_Desc* effDesc = AAXWrapper_GetDescription (); mPluginDesc = effDesc->mPluginDesc + plugIndex; mWrapper = AAXWrapper::create (GetPluginFactory (), effDesc->mVST3PluginID, mPluginDesc, this); if (!mWrapper) return; #if DEVELOPMENT static bool writePagetableFile; if (writePagetableFile) // use debugger to set variable or jump into function mWrapper->generatePageTables ("c:/tmp/pagetable.xml"); #endif // if no VST3 Bypass found then simulate it mSimulateBypass = (mWrapper->mBypassParameterID == Vst::kNoParamId); mWrapper->mSimulateBypass = mSimulateBypass; if (mParamNames.size () < (size_t)mWrapper->mNumParams) { mParamNames.resize (mWrapper->mNumParams); for (size_t i = 0; i < mParamNames.size (); i++) mParamNames[i].set (mWrapper->mParameterMap[i].vst3ID); } } //------------------------------------------------------------------------ AAXWrapper_Parameters::~AAXWrapper_Parameters () { delete mWrapper; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::EffectInit () { HLOG (HAPI, "%s", __FUNCTION__); if (AAX_IController* ctrl = Controller ()) { AAX_CSampleRate sampleRate; if (ctrl->GetSampleRate (&sampleRate) == AAX_SUCCESS) mWrapper->_setSampleRate (sampleRate); if (mWrapper->mProcessor) ctrl->SetSignalLatency ( static_cast (mWrapper->mProcessor->getLatencySamples ())); } for (uint32 i = 0; i < static_cast (mWrapper->mNumParams); i++) { AAX_CParamID iParameterID = mParamNames[i]; ParameterInfo paramInfo = {}; if (AAX_Result result = mWrapper->getParameterInfo (iParameterID, paramInfo)) return result; String title = paramInfo.title; AAX_IParameter* param = nullptr; param = NEW AAX_CParameter ( iParameterID, AAX_CString (title), paramInfo.defaultNormalizedValue, AAX_CLinearTaperDelegate (0, 1), AAX_CUnitDisplayDelegateDecorator (AAX_CNumberDisplayDelegate (), AAX_CString (title)), true); mParameterManager.AddParameter (param); } if (mSimulateBypass) { AAX_IParameter* param = NEW AAX_CParameter ( kBypassId, AAX_CString ("Bypass"), false, AAX_CBinaryTaperDelegate (), AAX_CBinaryDisplayDelegate ("off", "on"), true); mParameterManager.AddParameter (param); } return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::ResetFieldData (AAX_CFieldIndex index, void* inData, uint32_t inDataSize) const { HLOG (HAPI, "%s", __FUNCTION__); return mWrapper->ResetFieldData (index, inData, inDataSize); } //------------------------------------------------------------------------ // METHOD: AAX_UpdateMIDINodes // This will be called by the host if there are MIDI packets that need // to be handled in the Data Model. //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::UpdateMIDINodes (AAX_CFieldIndex inFieldIndex, AAX_CMidiPacket& inPacket) { HLOG (HAPI, "%s", __FUNCTION__); AAX_Result result; result = AAX_SUCCESS; inFieldIndex; inPacket; // Do some MIDI work if necessary. return result; } //------------------------------------------------------------------------ int32 AAXWrapper_Parameters::getParameterInfo (AAX_CParamID aaxId, Vst::ParameterInfo& paramInfo) const { AAX_Result result = mWrapper->getParameterInfo (aaxId, paramInfo); if (result != AAX_SUCCESS) { if (mSimulateBypass && strcmp (aaxId, kBypassId) == 0) { paramInfo = kParamInfoBypass; result = AAX_SUCCESS; } } return result; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetNumberOfParameters (int32_t* oNumControls) const { HLOG (HAPI, "%s", __FUNCTION__); *oNumControls = mWrapper->mNumParams; if (mSimulateBypass) *oNumControls += 1; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetMasterBypassParameter (AAX_IString* oIDString) const { HLOG (HAPI, "%s", __FUNCTION__); *oIDString = mSimulateBypass ? kBypassId : AAX_CID (mWrapper->mBypassParameterID); return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterIsAutomatable (AAX_CParamID iParameterID, AAX_CBoolean* oAutomatable) const { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); ParameterInfo paramInfo = {}; if (AAX_Result result = getParameterInfo (iParameterID, paramInfo)) return result; *oAutomatable = (paramInfo.flags & ParameterInfo::kCanAutomate) != 0; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterNumberOfSteps (AAX_CParamID iParameterID, int32_t* oNumSteps) const { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); ParameterInfo paramInfo = {}; if (AAX_Result result = getParameterInfo (iParameterID, paramInfo)) return result; if (paramInfo.stepCount == 0) *oNumSteps = 1024; else *oNumSteps = paramInfo.stepCount + 1; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterName (AAX_CParamID iParameterID, AAX_IString* oName) const { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); ParameterInfo paramInfo = {}; if (AAX_Result result = getParameterInfo (iParameterID, paramInfo)) return result; *oName = String (paramInfo.title); return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterNameOfLength (AAX_CParamID iParameterID, AAX_IString* oName, int32_t iNameLength) const { HLOG (HAPI, "%s(id=%s, len=%d)", __FUNCTION__, iParameterID, iNameLength); ParameterInfo paramInfo = {}; if (AAX_Result result = getParameterInfo (iParameterID, paramInfo)) return result; if (iNameLength >= tstrlen (paramInfo.title)) *oName = String (paramInfo.title); else { if (iNameLength < tstrlen (paramInfo.shortTitle)) paramInfo.shortTitle[iNameLength] = 0; *oName = String (paramInfo.shortTitle); } return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterDefaultNormalizedValue (AAX_CParamID iParameterID, double* oValue) const { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); ParameterInfo paramInfo = {}; if (AAX_Result result = getParameterInfo (iParameterID, paramInfo)) return result; *oValue = paramInfo.defaultNormalizedValue; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::SetParameterDefaultNormalizedValue (AAX_CParamID iParameterID, double iValue) { iParameterID; iValue; HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue); return AAX_ERROR_UNIMPLEMENTED; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterType (AAX_CParamID iParameterID, AAX_EParameterType* oParameterType) const { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); ParameterInfo paramInfo = {}; if (AAX_Result result = getParameterInfo (iParameterID, paramInfo)) return result; *oParameterType = paramInfo.stepCount == 0 ? AAX_eParameterType_Continuous : AAX_eParameterType_Discrete; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterOrientation ( AAX_CParamID iParameterID, AAX_EParameterOrientation* oParameterOrientation) const { iParameterID; HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); *oParameterOrientation = AAX_eParameterOrientation_BottomMinTopMax; // we don't care return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameter (AAX_CParamID iParameterID, AAX_IParameter** /*oParameter*/) { iParameterID; HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); SMTG_ASSERT (!"the host is not supposed to retrieve the AAX_IParameter interface"); return AAX_ERROR_UNIMPLEMENTED; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterIndex (AAX_CParamID iParameterID, int32_t* oControlIndex) const { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); Vst::ParamID id = getVstParamID (iParameterID); if (id == -1) { if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0) { *oControlIndex = mWrapper->mNumParams; return AAX_SUCCESS; } return AAX_ERROR_INVALID_PARAMETER_ID; } *oControlIndex = -1; int32_t idx = 0; for (auto& item : mParamNames) { if (strcmp (item, iParameterID) == 0) { *oControlIndex = idx; return AAX_SUCCESS; } idx++; } return AAX_ERROR_INVALID_PARAMETER_ID; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterIDFromIndex (int32_t iControlIndex, AAX_IString* oParameterIDString) const { HLOG (HAPI, "%s(idx=%x)", __FUNCTION__, iControlIndex); if ((size_t)iControlIndex >= mWrapper->mParameterMap.size ()) { if (mSimulateBypass && iControlIndex == mWrapper->mNumParams) { oParameterIDString->Set (kBypassId); return AAX_SUCCESS; } return AAX_ERROR_INVALID_PARAMETER_INDEX; } *oParameterIDString = mParamNames[iControlIndex]; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterValueInfo (AAX_CParamID iParameterID, int32_t /*iSelector*/, int32_t* oValue) const { iParameterID; HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); *oValue = 0; return AAX_ERROR_UNIMPLEMENTED; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterValueFromString ( AAX_CParamID iParameterID, double* oValue, const AAX_IString& iValueString) const { HLOG (HAPI, "%s(id=%s, string=%s)", __FUNCTION__, iParameterID, iValueString.Get ()); Vst::ParamID id = getVstParamID (iParameterID); if (id == -1) { if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0) { *oValue = strcmp (iValueString.Get (), "on") == 0; return AAX_SUCCESS; } return AAX_ERROR_INVALID_PARAMETER_ID; } String tmp (iValueString.Get ()); if (mWrapper->mController->getParamValueByString (id, (Vst::TChar*)tmp.text16 (), *oValue) != kResultTrue) return AAX_ERROR_INVALID_PARAMETER_ID; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterStringFromValue (AAX_CParamID iParameterID, double iValue, AAX_IString* oValueString, int32_t maxLength) const { HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue); Vst::ParamID id = getVstParamID (iParameterID); if (id == -1) { if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0) { oValueString->Set (iValue >= 0.5 ? "on" : "off"); return AAX_SUCCESS; } return AAX_ERROR_INVALID_PARAMETER_ID; } String128 tmp = {0}; if (mWrapper->mController->getParamStringByValue (id, iValue, tmp) != kResultTrue) return AAX_ERROR_INVALID_PARAMETER_ID; if (maxLength < tstrlen (tmp)) tmp[maxLength] = 0; *oValueString = String (tmp); // String str (tmp); // str.copyTo8 (text, 0, kVstMaxParamStrLen); return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterValueString (AAX_CParamID iParameterID, AAX_IString* oValueString, int32_t iMaxLength) const { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); double value; if (AAX_Result result = GetParameterNormalizedValue (iParameterID, &value)) return result; return GetParameterStringFromValue (iParameterID, value, oValueString, iMaxLength); } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetParameterNormalizedValue (AAX_CParamID iParameterID, double* oValuePtr) const { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); Vst::ParamID id = getVstParamID (iParameterID); if (id == -1) { if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0) { *oValuePtr = (mWrapper->mBypass ? 1 : 0); return AAX_SUCCESS; } return AAX_ERROR_INVALID_PARAMETER_ID; } ParamValue value = 0; if (!mWrapper->getLastParamChange (id, value)) value = mWrapper->mController->getParamNormalized (id); *oValuePtr = value; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::SetParameterNormalizedValue (AAX_CParamID iParameterID, double iValue) { HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue); Vst::ParamID id = getVstParamID (iParameterID); if (id == -1) { if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0) return AAX_SUCCESS; return AAX_ERROR_INVALID_PARAMETER_ID; } // mWrapper->addParameterChange (id, iValue, 0); if (auto ad = AutomationDelegate ()) { // Touch the control, Send that token, Release the control ad->PostTouchRequest (iParameterID); ad->PostSetValueRequest (iParameterID, iValue); ad->PostReleaseRequest (iParameterID); } return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::SetParameterNormalizedRelative (AAX_CParamID iParameterID, double iValue) { HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue); Vst::ParamID id = getVstParamID (iParameterID); if (id == -1) { if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0) { mWrapper->mBypass = (mWrapper->mBypass + iValue >= 0.5); mWrapper->_setBypass (mWrapper->mBypass); return AAX_SUCCESS; } return AAX_ERROR_INVALID_PARAMETER_ID; } ParamValue value = 0; if (!mWrapper->getLastParamChange (id, value)) value = mWrapper->mController->getParamNormalized (id); value = value + iValue; if (value < 0) value = 0; else if (value > 1) value = 1; SetParameterNormalizedValue (iParameterID, value); return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::TouchParameter (AAX_CParamID iParameterID) { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); if (auto ad = AutomationDelegate ()) return ad->PostTouchRequest (iParameterID); return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::ReleaseParameter (AAX_CParamID iParameterID) { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); if (auto ad = AutomationDelegate ()) return ad->PostReleaseRequest (iParameterID); return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::UpdateParameterTouch (AAX_CParamID iParameterID, AAX_CBoolean /*iTouchState*/) { iParameterID; HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID); return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::UpdateParameterNormalizedValue (AAX_CParamID iParameterID, double iValue, AAX_EUpdateSource iSource) { HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue); Vst::ParamID id = getVstParamID (iParameterID); if (id == -1) { if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0) { mWrapper->mBypass = iValue >= 0.5; mWrapper->_setBypass (mWrapper->mBypass); } else return AAX_ERROR_INVALID_PARAMETER_ID; } else mWrapper->addParameterChange (id, iValue, 0); #if 1 return AAX_CEffectParameters::UpdateParameterNormalizedValue (iParameterID, iValue, iSource); #else if (AutomationDelegate ()) AutomationDelegate ()->PostCurrentValue (iParameterID, iValue); // if (AAX_Result result = SetParameterNormalizedValue (iParameterID, iValue)) // return result; // Now the control has changed AAX_Result result = mPacketDispatcher.SetDirty (iParameterID); ++mNumPlugInChanges; return result; #endif } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::_UpdateParameterNormalizedRelative (AAX_CParamID iParameterID, double iValue) { HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue); if (AAX_Result result = SetParameterNormalizedRelative (iParameterID, iValue)) return result; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::_GenerateCoefficients () { HLOG (HAPI, "%s", __FUNCTION__); AAX_Result result = mPacketDispatcher.Dispatch (); return result; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetNumberOfChunks (int32_t* numChunks) const { HLOG (HAPI, "%s", __FUNCTION__); *numChunks = 1; return AAX_SUCCESS; } const AAX_CTypeID AAXWRAPPER_CONTROLS_CHUNK_ID = CCONST ('a', 'w', 'c', 'k'); const char AAXWRAPPER_CONTROLS_CHUNK_DESCRIPTION[] = "AAXWrapper State"; //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetChunkIDFromIndex (int32_t index, AAX_CTypeID* chunkID) const { HLOG (HAPI, "%s", __FUNCTION__); if (index != 0) return AAX_ERROR_INVALID_CHUNK_INDEX; *chunkID = AAXWRAPPER_CONTROLS_CHUNK_ID; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetChunkSize (AAX_CTypeID chunkID, uint32_t* oSize) const { HLOG (HAPI, "%s", __FUNCTION__); if (chunkID != AAXWRAPPER_CONTROLS_CHUNK_ID) return AAX_ERROR_INVALID_CHUNK_ID; FGuard guard (mWrapper->mSyncCalls); bool isPreset = false; void* data; *oSize = static_cast (mWrapper->_getChunk (&data, isPreset)); return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::GetChunk (AAX_CTypeID chunkID, AAX_SPlugInChunk* oChunk) const { HLOG (HAPI, "%s", __FUNCTION__); if (chunkID != AAXWRAPPER_CONTROLS_CHUNK_ID) return AAX_ERROR_INVALID_CHUNK_ID; FGuard guard (mWrapper->mSyncCalls); // assume GetChunkSize called before and size of oChunk correct oChunk->fVersion = 1; oChunk->fSize = static_cast (mWrapper->mChunk.getSize ()); memcpy (oChunk->fData, mWrapper->mChunk.getData (), static_cast (mWrapper->mChunk.getSize ())); strncpy (reinterpret_cast (oChunk->fName), AAXWRAPPER_CONTROLS_CHUNK_DESCRIPTION, 31); return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::SetChunk (AAX_CTypeID chunkID, const AAX_SPlugInChunk* iChunk) { HLOG (HAPI, "%s", __FUNCTION__); if (chunkID != AAXWRAPPER_CONTROLS_CHUNK_ID) return AAX_ERROR_INVALID_CHUNK_ID; FGuard guard (mWrapper->mSyncCalls); bool isPreset = mPresetOpened; mWrapper->_setChunk (const_cast (iChunk->fData), iChunk->fSize, isPreset); mPresetOpened = false; return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::CompareActiveChunk (const AAX_SPlugInChunk* /*iChunk*/, AAX_CBoolean* /*oIsEqual*/) const { HLOG (HAPI, "%s", __FUNCTION__); return AAX_ERROR_UNIMPLEMENTED; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::_GetNumberOfChanges (int32_t* oValue) const { HLOG (HAPI, "%s", __FUNCTION__); *oValue = mNumPlugInChanges; return AAX_SUCCESS; } //------------------------------------------------------------------------ void AAXWrapper_Parameters::setDirty (bool state) { if (state) mNumPlugInChanges++; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_Parameters::NotificationReceived (AAX_CTypeID iNotificationType, const void* iNotificationData, uint32_t iNotificationDataSize) { switch (iNotificationType) { //--- Tell the plug-in about connection of the sidechain input case AAX_eNotificationEvent_SideChainBeingConnected: mWrapper->setSideChainEnable (true); break; //--- Tell the plug-in about disconnection of the sidechain case AAX_eNotificationEvent_SideChainBeingDisconnected: mWrapper->setSideChainEnable (false); break; //--- The host has changed its latency compensation for this plug-in instance. case AAX_eNotificationEvent_SignalLatencyChanged: { int32_t outSample; Controller ()->GetSignalLatency (&outSample); if (mPluginDesc) mPluginDesc->mLatency = static_cast (outSample); if (mWrapper->isActive ()) { mWrapper->_suspend (); mWrapper->_resume (); } break; } //--- Tell the plug-in that chunk data is coming from a TFX case AAX_eNotificationEvent_PresetOpened: { // do not wanted to overwrite the bypass state when loading preset double value; if (GetParameterNormalizedValue ( mSimulateBypass ? kBypassId : AAX_CID (mWrapper->mBypassParameterID), &value) == AAX_SUCCESS) mWrapper->mBypassBeforePresetChanged = (value >= 0.5); mWrapper->mPresetChanged = true; mPresetOpened = true; break; } //--- Tell the plug-in that chunk data is coming from a PTX case AAX_eNotificationEvent_SessionBeingOpened: { mPresetOpened = false; break; } //--- Entering offline processing mode (i.e.offline bounce) case AAX_eNotificationEvent_EnteringOfflineMode: { mWrapper->setRenderingOffline (true); break; } //--- Exiting offline processing mode (i.e. offline bounce) case AAX_eNotificationEvent_ExitingOfflineMode: { mWrapper->setRenderingOffline (false); break; } //--- A string representing the path of the current session case AAX_eNotificationEvent_SessionPathChanged: { AAX_CString str (*reinterpret_cast (iNotificationData)); mSessionPath = str.StdString (); break; } //--- The current name of this plug-in instance's track case AAX_eNotificationEvent_TrackNameChanged: { AAX_CString str (*reinterpret_cast (iNotificationData)); mChannelName = str.StdString (); if (mWrapper->mController) { if (auto iChannelContextInfoListener = U::cast (mWrapper->mController)) { auto list = Vst::HostAttributeList::make (); String string; string.fromUTF8 (mChannelName.data ()); list->setString (Vst::ChannelContext::kChannelNameKey, string); list->setInt (Vst::ChannelContext::kChannelNameLengthKey, string.length ()); iChannelContextInfoListener->setChannelContextInfos (list); } } break; } //--- The zero-indexed insert position of this plug-in instance within its track case AAX_eNotificationEvent_InsertPositionChanged: { // auto tmp = *reinterpret_cast (iNotificationData); break; } //--- Tell the plug-in the maximum allowed GUI dimensions case AAX_eNotificationEvent_MaxViewSizeChanged: { // auto tmp = *reinterpret_cast (iNotificationData); break; } } return AAX_CEffectParameters::NotificationReceived (iNotificationType, iNotificationData, iNotificationDataSize); } /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/aaxwrapper.h0000644000000000000000000000013215124701711025050 xustar0030 mtime=1767080905.277413924 30 atime=1767080905.277413924 30 ctime=1767080905.277413924 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/aaxwrapper.h0000644000175000001440000001535415124701711025050 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper.h // Created by : Steinberg, 08/2017 // Description : VST 3 -> AAX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma once #include "public.sdk/source/vst/basewrapper/basewrapper.h" #include "base/thread/include/flock.h" #include #include #include struct AAX_Plugin_Desc; struct AAX_Effect_Desc; class AAX_IComponentDescriptor; class AAXWrapper_Parameters; class AAXWrapper_GUI; namespace Steinberg { namespace Vst { class IAudioProcessor; class IEditController; } } struct AAXWrapper_Context { void* ptr[1]; // array of numDataPointers pointers }; //------------------------------------------------------------------------ class AAXWrapper : public Steinberg::Vst::BaseWrapper, public Steinberg::Vst::IComponentHandler2, public Steinberg::Vst::IVst3ToAAXWrapper { public: // static creation method (will owned factory) static AAXWrapper* create (Steinberg::IPluginFactory* factory, const Steinberg::TUID vst3ComponentID, AAX_Plugin_Desc* desc, AAXWrapper_Parameters* p); AAXWrapper (Steinberg::Vst::BaseWrapper::SVST3Config& config, AAXWrapper_Parameters* p, AAX_Plugin_Desc* desc); ~AAXWrapper (); //--- VST 3 Interfaces ------------------------------------------------------ // IHostApplication Steinberg::tresult PLUGIN_API getName (Steinberg::Vst::String128 name) SMTG_OVERRIDE; // IComponentHandler Steinberg::tresult PLUGIN_API beginEdit (Steinberg::Vst::ParamID tag) SMTG_OVERRIDE; Steinberg::tresult PLUGIN_API performEdit ( Steinberg::Vst::ParamID tag, Steinberg::Vst::ParamValue valueNormalized) SMTG_OVERRIDE; Steinberg::tresult PLUGIN_API endEdit (Steinberg::Vst::ParamID tag) SMTG_OVERRIDE; Steinberg::tresult PLUGIN_API restartComponent (Steinberg::int32 flags) SMTG_OVERRIDE; // IComponentHandler2 Steinberg::tresult PLUGIN_API setDirty (Steinberg::TBool state) SMTG_OVERRIDE; Steinberg::tresult PLUGIN_API requestOpenEditor ( Steinberg::FIDString name = Steinberg::Vst::ViewType::kEditor) SMTG_OVERRIDE; Steinberg::tresult PLUGIN_API startGroupEdit () SMTG_OVERRIDE; Steinberg::tresult PLUGIN_API finishGroupEdit () SMTG_OVERRIDE; // FUnknown DEF_INTERFACES_2 (Steinberg::Vst::IComponentHandler2, Steinberg::Vst::IVst3ToAAXWrapper, BaseWrapper); REFCOUNT_METHODS (BaseWrapper); // AAXWrapper_Parameters callbacks void setGUI (AAXWrapper_GUI* gui) { mAAXGUI = gui; } Steinberg::int32 /*AAX_Result*/ getParameterInfo (const char* aaxId, Steinberg::Vst::ParameterInfo& paramInfo); Steinberg::int32 /*AAX_Result*/ ResetFieldData (Steinberg::int32 index, void* inData, Steinberg::uint32 inDataSize); Steinberg::int32 Process (AAXWrapper_Context* instance); Steinberg::uint32 getNumMIDIports () const { return mCountMIDIports; } void setSideChainEnable (bool enable); bool generatePageTables (const char* outputFile); void setRenderingOffline (bool val); static void DescribeAlgorithmComponent (AAX_IComponentDescriptor* outDesc, const AAX_Effect_Desc* desc, const AAX_Plugin_Desc* pdesc); //--- --------------------------------------------------------------------- Steinberg::uint32 getNumAAXOutputs () const { return mAAXOutputs; } //------------------------------------------------------------------------ // BaseWrapper overrides --------------------------------- //------------------------------------------------------------------------ bool init () SMTG_OVERRIDE; bool _sizeWindow (Steinberg::int32 width, Steinberg::int32 height) SMTG_OVERRIDE; void onTimer (Steinberg::Timer* timer) SMTG_OVERRIDE; Steinberg::int32 _getChunk (void** data, bool isPreset) SMTG_OVERRIDE; Steinberg::int32 _setChunk (void* data, Steinberg::int32 byteSize, bool isPreset) SMTG_OVERRIDE; void setupProcessTimeInfo () SMTG_OVERRIDE; //------------------------------------------------------------------------ private: void processOutputParametersChanges () SMTG_OVERRIDE; Steinberg::tresult setupBusArrangements (AAX_Plugin_Desc* desc); Steinberg::int32 countSidechainBusChannels (Steinberg::Vst::BusDirection dir, Steinberg::uint64& scBusBitset); void guessActiveOutputs (float** out, Steinberg::uint32 num); void updateActiveOutputState (); AAXWrapper_Parameters* mAAXParams = nullptr; AAXWrapper_GUI* mAAXGUI = nullptr; Steinberg::uint32 mAAXOutputs = 0; Steinberg::Base::Thread::FLock mSyncCalls; // synchronize calls expected in the same thread in VST3 AAX_Plugin_Desc* mPluginDesc = nullptr; Steinberg::uint32 mCountMIDIports = 0; // as of ProTools 12 (?) the context struct does no longer allow unused slots, // so we have to generate indices into the context struct dynamically // context pointer to AAXWrapper always first static const Steinberg::int32 idxContext = 0; static const Steinberg::int32 idxBufferSize = 1; Steinberg::int32 idxInputChannels = -1; Steinberg::int32 idxOutputChannels = -1; Steinberg::int32 idxSideChainInputChannels = -1; Steinberg::int32 idxMidiPorts = -1; Steinberg::int32 idxAuxOutputs = -1; Steinberg::int32 idxMeters = -1; Steinberg::int32 numDataPointers = 0; static const Steinberg::int32 maxActiveChannels = 128; std::bitset mActiveChannels; std::bitset mPropagatedChannels; Steinberg::uint32 mCntMeters = 0; std::unique_ptr mMeterIds; struct GetChunkMessage; void* mainThread = nullptr; Steinberg::Base::Thread::FLock msgQueueLock; std::list msgQueue; float mBypassGain = 1.0; float* mMetersTmp = nullptr; Steinberg::Vst::TQuarterNotes mLastPpqPos = 0; Steinberg::Vst::TQuarterNotes mNextPpqPos = 0; bool mWantsSetChunk = false; bool mSettingChunk = false; bool mSimulateBypass = false; bool mBypass = false; bool mPresetChanged = false; bool mBypassBeforePresetChanged = false; bool mWantsSetChunkIsPreset = false; friend class AAXWrapper_Parameters; friend class AAXWrapper_GUI; }; /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/aaxentry.cpp0000644000000000000000000000013215124701711025064 xustar0030 mtime=1767080905.277413924 30 atime=1767080905.277379783 30 ctime=1767080905.277413924 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/aaxentry.cpp0000644000175000001440000001244015124701711025055 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/aaxwrapper/aaxentry.h // Created by : Steinberg, 08/2017 // Description : VST 3 -> AAX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore /** * plugin entry from AAX_Exports.cpp */ //----------------------------------------------------------------------------- #include "pluginterfaces/base/fplatform.h" // change names to avoid different linkage #define ACFRegisterPlugin ACFRegisterPlugin_ #define ACFRegisterComponent ACFRegisterComponent_ #define ACFGetClassFactory ACFGetClassFactory_ #define ACFCanUnloadNow ACFCanUnloadNow_ #define ACFStartup ACFStartup_ #define ACFShutdown ACFShutdown_ //#define INITACFIDS // Make sure all of the AVX2 uids are defined. #include "AAX.h" #include "AAX_Init.h" #include "acfresult.h" #include "acfunknown.h" #undef ACFRegisterPlugin #undef ACFRegisterComponent #undef ACFGetClassFactory #undef ACFCanUnloadNow #undef ACFStartup #undef ACFShutdown // defined in basewrapper.cpp extern bool _InitModule (); extern bool _DeinitModule (); // reference this in the plugin to force inclusion of the wrapper in the link int AAXWrapper_linkAnchor; //------------------------------------------------------------------------ #if defined(__GNUC__) #define AAX_EXPORT extern "C" __attribute__ ((visibility ("default"))) ACFRESULT #else #define AAX_EXPORT extern "C" __declspec (dllexport) ACFRESULT __stdcall #endif AAX_EXPORT ACFRegisterPlugin (IACFUnknown* pUnkHost, IACFPluginDefinition** ppPluginDefinition); AAX_EXPORT ACFRegisterComponent (IACFUnknown* pUnkHost, acfUInt32 index, IACFComponentDefinition** ppComponentDefinition); AAX_EXPORT ACFGetClassFactory (IACFUnknown* pUnkHost, const acfCLSID& clsid, const acfIID& iid, void** ppOut); AAX_EXPORT ACFCanUnloadNow (IACFUnknown* pUnkHost); AAX_EXPORT ACFStartup (IACFUnknown* pUnkHost); AAX_EXPORT ACFShutdown (IACFUnknown* pUnkHost); AAX_EXPORT ACFGetSDKVersion (acfUInt64* oSDKVersion); //------------------------------------------------------------------------ // \func ACFRegisterPlugin // \brief Determines the number of components defined in the dll. // ACFAPI ACFRegisterPlugin (IACFUnknown* pUnkHostVoid, IACFPluginDefinition** ppPluginDefinitionVoid) { ACFRESULT result = ACF_OK; try { result = AAXRegisterPlugin (pUnkHostVoid, ppPluginDefinitionVoid); } catch (...) { result = ACF_E_UNEXPECTED; } return result; } //------------------------------------------------------------------------ // \func ACFRegisterComponent // \brief Registers a specific component in the DLL. // ACFAPI ACFRegisterComponent (IACFUnknown* pUnkHost, acfUInt32 index, IACFComponentDefinition** ppComponentDefinition) { ACFRESULT result = ACF_OK; try { result = AAXRegisterComponent (pUnkHost, index, ppComponentDefinition); } catch (...) { result = ACF_E_UNEXPECTED; } return result; } //------------------------------------------------------------------------ // \func ACFGetClassFactory // \brief Gets the factory for a given class ID. // ACFAPI ACFGetClassFactory (IACFUnknown* pUnkHost, const acfCLSID& clsid, const acfIID& iid, void** ppOut) { ACFRESULT result = ACF_OK; try { result = AAXGetClassFactory (pUnkHost, clsid, iid, ppOut); } catch (...) { result = ACF_E_UNEXPECTED; } return result; } //------------------------------------------------------------------------ // \func ACFCanUnloadNow // \brief Figures out if all objects are released so we can unload. // ACFAPI ACFCanUnloadNow (IACFUnknown* pUnkHost) { ACFRESULT result = ACF_OK; try { result = AAXCanUnloadNow (pUnkHost); } catch (...) { result = ACF_E_UNEXPECTED; } return result; } //------------------------------------------------------------------------ // \func ACFStartup // \brief Called once at init time. // ACFAPI ACFStartup (IACFUnknown* pUnkHost) { ACFRESULT result = ACF_OK; try { result = AAXStartup (pUnkHost); if (result == ACF_OK) { if (!_InitModule ()) { AAXShutdown (pUnkHost); result = ACF_E_UNEXPECTED; } } } catch (...) { result = ACF_E_UNEXPECTED; } return result; } //------------------------------------------------------------------------ // \func ACFShutdown // \brief Called once at termination of dll. // ACFAPI ACFShutdown (IACFUnknown* pUnkHost) { ACFRESULT result = ACF_OK; try { _DeinitModule (); result = AAXShutdown (pUnkHost); } catch (...) { result = ACF_E_UNEXPECTED; } return result; } //------------------------------------------------------------------------ ACFAPI ACFGetSDKVersion (acfUInt64* oSDKVersion) { return AAXGetSDKVersion (oSDKVersion); } /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/aaxwrapper_gui.cpp0000644000000000000000000000013215124701711026247 xustar0030 mtime=1767080905.277413924 30 atime=1767080905.277413924 30 ctime=1767080905.277413924 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/aaxwrapper_gui.cpp0000644000175000001440000001012015124701711026231 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_gui.cpp // Created by : Steinberg, 08/2017 // Description : VST 3 -> AAX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundef-prefix" #endif #include "aaxwrapper_gui.h" #include "aaxwrapper.h" #include "aaxwrapper_parameters.h" #include "AAX_IViewContainer.h" using namespace Steinberg; using namespace Steinberg::Vst; using namespace Steinberg::Base::Thread; //------------------------------------------------------------------------ void AAXWrapper_GUI::CreateViewContainer () { if (GetViewContainerType () == AAX_eViewContainer_Type_HWND || GetViewContainerType () == AAX_eViewContainer_Type_NSView) { mHWND = this->GetViewContainerPtr (); AAXWrapper* wrapper = static_cast (GetEffectParameters ())->getWrapper (); FGuard guard (wrapper->mSyncCalls); wrapper->setGUI (this); mInOpen = true; if (auto* editor = wrapper->getEditor ()) editor->_open (mHWND); mInOpen = false; } } //------------------------------------------------------------------------ AAX_Result AAXWrapper_GUI::GetViewSize (AAX_Point* oEffectViewSize) const { oEffectViewSize->horz = 1024; oEffectViewSize->vert = 768; auto* that = const_cast (this); auto* params = static_cast (that->GetEffectParameters ()); int32 width, height; if (params->getWrapper ()->getEditorSize (width, height)) { oEffectViewSize->horz = static_cast (width); oEffectViewSize->vert = static_cast (height); } return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_GUI::SetControlHighlightInfo (AAX_CParamID iParameterID, AAX_CBoolean /*iIsHighlighted*/, AAX_EHighlightColor /*iColor*/) { AAXWrapper* wrapper = static_cast (GetEffectParameters ())->getWrapper (); Vst::ParamID id = getVstParamID (iParameterID); if (id == kNoParamId) return AAX_ERROR_INVALID_PARAMETER_ID; // TODO wrapper; return AAX_SUCCESS; } //------------------------------------------------------------------------ void AAXWrapper_GUI::DeleteViewContainer () { AAXWrapper* wrapper = static_cast (GetEffectParameters ())->getWrapper (); wrapper->setGUI (nullptr); if (auto* editor = wrapper->getEditor ()) editor->_close (); } //------------------------------------------------------------------------ // METHOD: CreateViewContents //------------------------------------------------------------------------ void AAXWrapper_GUI::CreateViewContents () { } //------------------------------------------------------------------------ bool AAXWrapper_GUI::setWindowSize (AAX_Point& size) { if (mInOpen) mRefreshSize = true; // redo later, resizing might silently not work during opening the UI if (AAX_IViewContainer* vc = GetViewContainer ()) if (vc->SetViewSize (size) == AAX_SUCCESS) return true; return false; } //------------------------------------------------------------------------ AAX_Result AAXWrapper_GUI::TimerWakeup () { if (mRefreshSize) { mRefreshSize = false; AAX_Point size; if (GetViewSize (&size) == AAX_SUCCESS) if (!setWindowSize (size)) mRefreshSize = true; } return AAX_CEffectGUI::TimerWakeup (); } /// \endcond #ifdef __clang__ #pragma clang diagnostic pop #endif qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/aaxlibrary.cpp0000644000000000000000000000013215124701711025367 xustar0030 mtime=1767080905.277413924 30 atime=1767080905.277413924 30 ctime=1767080905.277413924 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/aaxlibrary.cpp0000644000175000001440000000734515124701711025370 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/aaxwrapper/aaxlibrary.cpp // Created by : Steinberg, 08/2017 // Description : VST 3 -> AAX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore // instead of linking to a library, we just include the sources here to have // full control over compile settings #define I18N_LIB 1 #define PLUGIN_SDK_BUILD 1 #define DPA_PLUGIN_BUILD 1 #define INITACFIDS // Make sure all of the AVX2 uids are defined. #define UNICODE 1 #ifdef _WIN32 #ifndef WIN32 #define WIN32 // for CMutex.cpp #endif #define WINDOWS_VERSION 1 // for AAXWrapper_GUI.h #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreorder" #pragma clang diagnostic ignored "-Wundef-prefix" #endif #include "AAX_Atomic.h" #include "../Interfaces/ACF/CACFClassFactory.cpp" #include "../Libs/AAXLibrary/source/AAX_CACFUnknown.cpp" #include "../Libs/AAXLibrary/source/AAX_CChunkDataParser.cpp" #include "../Libs/AAXLibrary/source/AAX_CEffectDirectData.cpp" #include "../Libs/AAXLibrary/source/AAX_CEffectGUI.cpp" #include "../Libs/AAXLibrary/source/AAX_CEffectParameters.cpp" #include "../Libs/AAXLibrary/source/AAX_CHostProcessor.cpp" #include "../Libs/AAXLibrary/source/AAX_CHostServices.cpp" #include "../Libs/AAXLibrary/source/AAX_CMutex.cpp" #include "../Libs/AAXLibrary/source/AAX_CPacketDispatcher.cpp" #include "../Libs/AAXLibrary/source/AAX_CParameter.cpp" #include "../Libs/AAXLibrary/source/AAX_CParameterManager.cpp" #include "../Libs/AAXLibrary/source/AAX_CString.cpp" #include "../Libs/AAXLibrary/source/AAX_CUIDs.cpp" #include "../Libs/AAXLibrary/source/AAX_CommonConversions.cpp" #include "../Libs/AAXLibrary/source/AAX_IEffectDirectData.cpp" #include "../Libs/AAXLibrary/source/AAX_IEffectGUI.cpp" #include "../Libs/AAXLibrary/source/AAX_IEffectParameters.cpp" #include "../Libs/AAXLibrary/source/AAX_IHostProcessor.cpp" #include "../Libs/AAXLibrary/source/AAX_Init.cpp" #include "../Libs/AAXLibrary/source/AAX_Properties.cpp" #include "../Libs/AAXLibrary/source/AAX_VAutomationDelegate.cpp" #include "../Libs/AAXLibrary/source/AAX_VCollection.cpp" #include "../Libs/AAXLibrary/source/AAX_VComponentDescriptor.cpp" #include "../Libs/AAXLibrary/source/AAX_VController.cpp" #include "../Libs/AAXLibrary/source/AAX_VDescriptionHost.cpp" #include "../Libs/AAXLibrary/source/AAX_VEffectDescriptor.cpp" #include "../Libs/AAXLibrary/source/AAX_VFeatureInfo.cpp" #include "../Libs/AAXLibrary/source/AAX_VHostProcessorDelegate.cpp" #include "../Libs/AAXLibrary/source/AAX_VHostServices.cpp" #include "../Libs/AAXLibrary/source/AAX_VPageTable.cpp" #include "../Libs/AAXLibrary/source/AAX_VPrivateDataAccess.cpp" #include "../Libs/AAXLibrary/source/AAX_VPropertyMap.cpp" #include "../Libs/AAXLibrary/source/AAX_VTransport.cpp" #include "../Libs/AAXLibrary/source/AAX_VViewContainer.cpp" #ifdef _WIN32 #include "../Libs/AAXLibrary/source/AAX_CAutoreleasePool.Win.cpp" #else //#include "../Libs/AAXLibrary/source/AAX_CAutoreleasePool.OSX.mm" #endif #undef min #undef max // put at the very end, uses "using namespace std" #include "../Libs/AAXLibrary/source/AAX_SliderConversions.cpp" #ifdef __clang__ #pragma clang diagnostic pop #endif /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/aaxwrapper_gui.h0000644000000000000000000000013215124701711025714 xustar0030 mtime=1767080905.277413924 30 atime=1767080905.277413924 30 ctime=1767080905.277413924 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/aaxwrapper_gui.h0000644000175000001440000000340215124701711025703 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_gui.h // Created by : Steinberg, 08/2017 // Description : VST 3 -> AAX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #pragma once #include "AAX_CEffectGUI.h" #include "pluginterfaces/base/fplatform.h" //============================================================================== class AAXWrapper_GUI : public AAX_CEffectGUI { public: static AAX_IEffectGUI* AAX_CALLBACK Create (); AAXWrapper_GUI () = default; virtual ~AAXWrapper_GUI () = default; void CreateViewContents () SMTG_OVERRIDE; void CreateViewContainer () SMTG_OVERRIDE; void DeleteViewContainer () SMTG_OVERRIDE; AAX_Result GetViewSize (AAX_Point* oEffectViewSize) const SMTG_OVERRIDE; AAX_Result SetControlHighlightInfo (AAX_CParamID /* iParameterID */, AAX_CBoolean /* iIsHighlighted */, AAX_EHighlightColor /* iColor */) SMTG_OVERRIDE; AAX_Result TimerWakeup () SMTG_OVERRIDE; bool setWindowSize (AAX_Point& size); // calback from AAXWrapper private: bool mInOpen = false; bool mRefreshSize = false; void* mHWND = nullptr; }; /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/aaxwrapper.cpp0000644000000000000000000000013215124701711025403 xustar0030 mtime=1767080905.277413924 30 atime=1767080905.277413924 30 ctime=1767080905.277413924 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/aaxwrapper.cpp0000644000175000001440000012332215124701711025376 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format auto // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper.cpp // Created by : Steinberg, 08/2017 // Description : VST 3 -> AAX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #include "aaxwrapper.h" #include "AAX.h" #include "AAX_Assert.h" #include "AAX_Errors.h" #include "AAX_ICollection.h" #include "AAX_IComponentDescriptor.h" #include "AAX_IEffectDescriptor.h" #include "AAX_IMIDINode.h" #include "AAX_IPropertyMap.h" #include "AAX_Version.h" static_assert (AAX_SDK_CURRENT_REVISION >= AAX_SDK_2p3p2_REVISION, "VST3 SDK requires AAX SDK version 2.3.2 or higher"); #include "aaxwrapper_description.h" #include "aaxwrapper_gui.h" #include "aaxwrapper_parameters.h" #include "base/thread/include/fcondition.h" #include "pluginterfaces/base/funknownimpl.h" #define USE_TRACE 1 #if USE_TRACE #define HAPI AAX_eTracePriorityHost_Normal #define HLOG AAX_TRACE #else #define HAPI 0 #if SMTG_OS_WINDOWS #define HLOG __noop #else #define HLOG(...) \ do \ { \ } while (false) #endif #endif #if SMTG_OS_WINDOWS #include #define getCurrentThread() ((void*)(size_t)GetCurrentThreadId ()) #else #include #define getCurrentThread() (pthread_self ()) #endif using namespace Steinberg; using namespace Steinberg::Vst; using namespace Steinberg::Base::Thread; //------------------------------------------------------------------------ class AAXEditorWrapper : public BaseEditorWrapper { public: AAXEditorWrapper (AAXWrapper* wrapper, IEditController* controller) : BaseEditorWrapper (controller), mAAXWrapper (wrapper) { } tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) SMTG_OVERRIDE; private: AAXWrapper* mAAXWrapper; }; //------------------------------------------------------------------------ tresult PLUGIN_API AAXEditorWrapper::resizeView (IPlugView* view, ViewRect* newSize) { tresult result = kResultFalse; if (view && newSize) { if (mAAXWrapper->_sizeWindow (newSize->getWidth (), newSize->getHeight ())) { result = view->onSize (newSize); } } return result; } //------------------------------------------------------------------------ //------------------------------------------------------------------------ AAXWrapper::AAXWrapper (BaseWrapper::SVST3Config& config, AAXWrapper_Parameters* p, AAX_Plugin_Desc* desc) : BaseWrapper (config), mAAXParams (p), mPluginDesc (desc) { HLOG (HAPI, "%s", __FUNCTION__); mBlockSize = 1024; // never explicitly changed by Pro Tools, so assume the maximum mUseExportedBypass = true; mUseIncIndex = false; mainThread = getCurrentThread (); // must be in lock step with DescribeAlgorithmComponent int32 idx = idxBufferSize + 1; if (desc->mInputChannels || desc->mOutputChannels) idxInputChannels = idx++; if (desc->mOutputChannels) idxOutputChannels = idx++; if (desc->mSideChainInputChannels) idxSideChainInputChannels = idx++; if (desc->mMIDIports) { for (AAX_MIDI_Desc* mdesc = desc->mMIDIports; mdesc->mName; mdesc++) mCountMIDIports++; if (mCountMIDIports > 0) { idxMidiPorts = idx; idx += mCountMIDIports; } } int32 numAuxOutputs = 0; mAAXOutputs = static_cast (desc->mOutputChannels); if (desc->mAuxOutputChannels) { for (AAX_Aux_Desc *adesc = desc->mAuxOutputChannels; adesc->mName; adesc++, numAuxOutputs++) mAAXOutputs += adesc->mChannels < 0 ? desc->mOutputChannels : adesc->mChannels; if (numAuxOutputs > 0) { idxAuxOutputs = idx; idx += numAuxOutputs; } } if (desc->mMeters) { idxMeters = idx++; for (auto mdesc = desc->mMeters; mdesc->mName; mdesc++) mCntMeters++; mMeterIds.reset (NEW Steinberg::Vst::ParamID (mCntMeters)); mCntMeters = 0; for (auto mdesc = desc->mMeters; mdesc->mName; mdesc++) mMeterIds[mCntMeters++] = static_cast (mdesc->mID); } numDataPointers = idx; } //------------------------------------------------------------------------ AAXWrapper::~AAXWrapper () { HLOG (HAPI, "%s", __FUNCTION__); } //------------------------------------------------------------------------ tresult PLUGIN_API AAXWrapper::getName (String128 name) { String str ("AAXWrapper"); str.copyTo16 (name, 0, 127); return kResultTrue; } //------------------------------------------------------------------------ Vst::ParamID getVstParamID (AAX_CParamID aaxid) { if (aaxid[0] != 'p') return kNoParamId; long id; if (sscanf (aaxid + 1, "%lx", &id) != 1) return kNoParamId; return static_cast (id); } //------------------------------------------------------------------------ // IComponentHandler //------------------------------------------------------------------------ tresult PLUGIN_API AAXWrapper::beginEdit (ParamID tag) { HLOG (HAPI, "%s(tag=%x)", __FUNCTION__, tag); AAX_CID aaxid (tag); mAAXParams->TouchParameter (aaxid); return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API AAXWrapper::performEdit (ParamID tag, ParamValue valueNormalized) { HLOG (HAPI, "%s(tag=%x, value=%lf)", __FUNCTION__, tag, valueNormalized); AAX_CID aaxid (tag); mAAXParams->SetParameterNormalizedValue (aaxid, valueNormalized); return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API AAXWrapper::endEdit (ParamID tag) { HLOG (HAPI, "%s(tag=%x)", __FUNCTION__, tag); AAX_CID aaxid (tag); mAAXParams->ReleaseParameter (aaxid); return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API AAXWrapper::setDirty (TBool state) { mAAXParams->setDirty (state != 0); return kResultOk; } //------------------------------------------------------------------------ tresult PLUGIN_API AAXWrapper::requestOpenEditor (FIDString /*name*/) { return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API AAXWrapper::startGroupEdit () { return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API AAXWrapper::finishGroupEdit () { return kResultFalse; } //------------------------------------------------------------------------ bool AAXWrapper::init () { bool res = BaseWrapper::init (); if (mController && BaseEditorWrapper::hasEditor (mController)) _setEditor (NEW AAXEditorWrapper (this, mController)); return res; } //------------------------------------------------------------------------ void AAXWrapper::setupProcessTimeInfo () { mProcessContext.state = 0; mProcessContext.sampleRate = mSampleRate; if (AAX_ITransport* transport = mAAXParams->Transport ()) { int64_t splPos, ppqPos, loopStart, loopEnd; bool playing, looping; if (transport->GetCurrentNativeSampleLocation (&splPos) == AAX_SUCCESS) mProcessContext.projectTimeSamples = (TSamples)splPos; if (transport->GetCurrentTickPosition (&ppqPos) == AAX_SUCCESS) { mProcessContext.projectTimeMusic = (double)ppqPos / 960000.0; mProcessContext.state |= ProcessContext::kProjectTimeMusicValid; } else mProcessContext.projectTimeMusic = 0; if (transport->GetCurrentTempo (&mProcessContext.tempo) == AAX_SUCCESS) mProcessContext.state |= ProcessContext::kTempoValid; if (transport->GetCurrentLoopPosition (&looping, &loopStart, &loopEnd) == AAX_SUCCESS) { mProcessContext.cycleStartMusic = (double)loopStart / 960000.0; mProcessContext.cycleEndMusic = (double)loopEnd / 960000.0; mProcessContext.state |= ProcessContext::kCycleValid; if (looping) mProcessContext.state |= ProcessContext::kCycleActive; } if (transport->IsTransportPlaying (&playing) == AAX_SUCCESS) { mProcessContext.state |= (playing ? ProcessContext::kPlaying : 0); } // workaround ppqPos not updating for every 2nd audio blocks @ 96 kHz (and more for higher // frequencies) // and while the UI is frozen, e.g. during save static const int32 playflags = ProcessContext::kPlaying | ProcessContext::kProjectTimeMusicValid | ProcessContext::kTempoValid; if ((playflags & mProcessContext.state) == playflags && mSampleRate != 0) { TQuarterNotes ppq = mProcessContext.projectTimeMusic; if (ppq == mLastPpqPos && mLastPpqPos != 0 && mNextPpqPos != 0) { TQuarterNotes nextppq = mNextPpqPos; if (mProcessContext.state & ProcessContext::kCycleActive) if (nextppq >= mProcessContext.cycleEndMusic) nextppq += mProcessContext.cycleStartMusic - mProcessContext.cycleEndMusic; mProcessContext.projectTimeMusic = nextppq; } mLastPpqPos = ppq; mNextPpqPos = mProcessContext.projectTimeMusic + mProcessContext.tempo / 60 * mProcessData.numSamples / mSampleRate; } else { mLastPpqPos = mNextPpqPos = 0; } int32_t num, den; if (transport->GetCurrentMeter (&num, &den) == AAX_SUCCESS) { mProcessContext.timeSigNumerator = num; mProcessContext.timeSigDenominator = den; mProcessContext.state |= ProcessContext::kTimeSigValid; } else mProcessContext.timeSigNumerator = mProcessContext.timeSigDenominator = 4; AAX_EFrameRate frameRate; int32_t offset; if (transport->GetTimeCodeInfo (&frameRate, &offset) == AAX_SUCCESS) { mProcessContext.state |= ProcessContext::kSmpteValid; mProcessContext.smpteOffsetSubframes = offset; switch (frameRate) { case AAX_eFrameRate_24Frame: mProcessContext.frameRate.framesPerSecond = 24; break; case AAX_eFrameRate_25Frame: mProcessContext.frameRate.framesPerSecond = 25; break; case AAX_eFrameRate_2997NonDrop: mProcessContext.frameRate.framesPerSecond = 30; mProcessContext.frameRate.flags = FrameRate::kPullDownRate; break; case AAX_eFrameRate_2997DropFrame: mProcessContext.frameRate.framesPerSecond = 30; mProcessContext.frameRate.flags = FrameRate::kDropRate | FrameRate::kPullDownRate; break; case AAX_eFrameRate_30NonDrop: mProcessContext.frameRate.framesPerSecond = 30; break; case AAX_eFrameRate_30DropFrame: mProcessContext.frameRate.framesPerSecond = 30; mProcessContext.frameRate.flags = FrameRate::kDropRate; break; case AAX_eFrameRate_23976: mProcessContext.frameRate.framesPerSecond = 24; mProcessContext.frameRate.flags = FrameRate::kPullDownRate; break; case AAX_eFrameRate_Undeclared: default: mProcessContext.state &= ~ProcessContext::kSmpteValid; } } mProcessData.processContext = &mProcessContext; } else mProcessData.processContext = nullptr; } //------------------------------------------------------------------------ bool AAXWrapper::_sizeWindow (int32 width, int32 height) { HLOG (HAPI, "%s(width=%x, height=%x)", __FUNCTION__, width, height); AAX_ASSERT (mainThread == getCurrentThread ()); if (!mAAXGUI) return false; AAX_Point size (static_cast (height), static_cast (width)); return mAAXGUI->setWindowSize (size); } //------------------------------------------------------------------------ struct AAXWrapper::GetChunkMessage : public FCondition { void* mData = nullptr; int32 mDataSize = 0; int32 mResult = 0; }; //------------------------------------------------------------------------ int32 AAXWrapper::_getChunk (void** data, bool isPreset) { if (mWantsSetChunk) { // isPreset is always false for AAX, so we can ignore it *data = mChunk.getData (); return static_cast (mChunk.getSize ()); } if (mainThread == getCurrentThread ()) return BaseWrapper::_getChunk (data, isPreset); auto* msg = NEW GetChunkMessage; { FGuard guard (msgQueueLock); msgQueue.push_back (msg); } msg->wait (); *data = msg->mData; return msg->mResult; } //------------------------------------------------------------------------ int32 AAXWrapper::_setChunk (void* data, int32 byteSize, bool isPreset) { if (mainThread == getCurrentThread ()) return BaseWrapper::_setChunk (data, byteSize, isPreset); FGuard guard (msgQueueLock); mChunk.setSize (byteSize); memcpy (mChunk.getData (), data, static_cast (byteSize)); mWantsSetChunk = true; mWantsSetChunkIsPreset = isPreset; return 0; } //------------------------------------------------------------------------ void AAXWrapper::onTimer (Timer* timer) { BaseWrapper::onTimer (timer); AAX_ASSERT (mainThread == getCurrentThread ()); if (mWantsSetChunk && !mSettingChunk) { mSettingChunk = true; FGuard guard (msgQueueLock); BaseWrapper::_setChunk (mChunk.getData (), static_cast (mChunk.getSize ()), mWantsSetChunkIsPreset); mWantsSetChunk = false; mSettingChunk = false; mWantsSetChunkIsPreset = false; if (mPresetChanged) { int32_t numParams; if (mAAXParams->GetNumberOfParameters (&numParams) == AAX_SUCCESS) { AAX_CID bypassId (mBypassParameterID); for (auto i = 0; i < numParams; i++) { AAX_CString id; if (mAAXParams->GetParameterIDFromIndex (i, &id) == AAX_SUCCESS) { if (id == (const char*)bypassId) { mAAXParams->SetParameterNormalizedValue (id.CString (), mBypassBeforePresetChanged); } else { double value; if (mAAXParams->GetParameterNormalizedValue (id.CString (), &value) == AAX_SUCCESS) { mAAXParams->SetParameterNormalizedValue (id.CString (), value); } } } } } mPresetChanged = false; } } updateActiveOutputState (); while (!msgQueue.empty ()) { GetChunkMessage* msg = nullptr; { FGuard guard (msgQueueLock); if (!msgQueue.empty ()) { msg = msgQueue.front (); msgQueue.pop_front (); } } if (msg) { msg->mResult = BaseWrapper::_getChunk (&msg->mData, false); msg->signal (); } } } //------------------------------------------------------------------------ int32 AAXWrapper::getParameterInfo (AAX_CParamID aaxId, Vst::ParameterInfo& paramInfo) { HLOG (HAPI, "%s(id=%s)", __FUNCTION__, aaxId); Vst::ParamID id = getVstParamID (aaxId); if (id == -1) return AAX_ERROR_INVALID_PARAMETER_ID; std::map::iterator iter = mParamIndexMap.find (id); if (iter == mParamIndexMap.end ()) return AAX_ERROR_INVALID_PARAMETER_ID; if (mController->getParameterInfo (iter->second, paramInfo) != kResultTrue) return AAX_ERROR_INVALID_PARAMETER_ID; return AAX_SUCCESS; } //------------------------------------------------------------------------ bool AAXWrapper::generatePageTables (const char* outputFile) { #if 0 && DEVELOPMENT FILE* fh = fopen (outputFile, "w"); if (!fh) return false; fprintf (fh, "\n" "\n" "\t\n"); AAX_Effect_Desc* effDesc = AAXWrapper_GetDescription (); for (const AAX_Plugin_Desc* pdesc = effDesc->mPluginDesc; pdesc->mEffectID; pdesc++) { uint32_t manId = effDesc->mManufacturerID; SWAP_32 (manId); uint32_t prodId = effDesc->mProductID; SWAP_32 (prodId); uint32_t plugId = pdesc->mPlugInIDNative; SWAP_32 (plugId); fprintf (fh, "\t\t\n" "\t\t\t%s\n" "\t\t\tPageTable 1\n" "\t\t", &manId, &prodId, &plugId, pdesc->mName); fprintf (fh, "\t\t\t\n"); int32 cntParams = controller->getParameterCount (); for (int32 i = 0; i < cntParams; i++) { Vst::ParameterInfo info; if (controller->getParameterInfo (i, info) == kResultTrue && (info.flags & ParameterInfo::kCanAutomate)) { AAX_CID id (info.id); String stitle (info.shortTitle); fprintf (fh, "\t\t\t\t%s\n", i + 1, (const char*) id, stitle.text8 ()); } } fprintf (fh, "\t\t\t\n"); } fprintf (fh, "\t\n"); fprintf (fh, "\t\n"); fprintf (fh, "\t\n" "\t\t\n" "\t\t\t\n"); for (const AAX_Plugin_Desc* pdesc = effDesc->mPluginDesc; pdesc->mEffectID; pdesc++) { uint32_t manId = effDesc->mManufacturerID; SWAP_32 (manId); uint32_t prodId = effDesc->mProductID; SWAP_32 (prodId); uint32_t plugId = pdesc->mPlugInIDNative; SWAP_32 (plugId); fprintf (fh, "\t\t\t\t\n" "\t\t\t\t\t%s\n" "\t\t\t\t\n", &manId, &prodId, &plugId, pdesc->mName); } fprintf (fh, "\t\t\t\n" "\t\t\n" "\t\n"); fprintf (fh, "\n"); fclose (fh); #else outputFile; #endif return true; } //------------------------------------------------------------------------ static int32 getChannelsStem (int32 channels) { switch (channels) { case 1: return AAX_eStemFormat_Mono; case 2: return AAX_eStemFormat_Stereo; case 3: return AAX_eStemFormat_LCR; case 4: return AAX_eStemFormat_Ambi_1_ACN; // AAX_eStemFormat_Quad case 5: return AAX_eStemFormat_5_0; case 6: return AAX_eStemFormat_5_1; // AAX_eStemFormat_6_0 case 7: return AAX_eStemFormat_6_1; // AAX_eStemFormat_7_0_DTS case 8: return AAX_eStemFormat_7_1_DTS; case 9: return AAX_eStemFormat_Ambi_2_ACN; // AAX_eStemFormat_7_0_2; case 10: return AAX_eStemFormat_7_1_2; case 16: return AAX_eStemFormat_Ambi_3_ACN; } return AAX_eStemFormat_None; } //------------------------------------------------------------------------------- static SpeakerArrangement numChannelsToSpeakerArrangement (int32 numChannels) { switch (numChannels) { case 1: return SpeakerArr::kMono; case 2: return SpeakerArr::kStereo; case 3: return SpeakerArr::k30Cine; case 4: return SpeakerArr::kAmbi1stOrderACN; case 5: return SpeakerArr::k50; case 6: return SpeakerArr::k51; case 7: return SpeakerArr::k61Cine; case 8: return SpeakerArr::k71Cine; case 9: return SpeakerArr::kAmbi2cdOrderACN; case 10: return SpeakerArr::k71_2; case 16: return SpeakerArr::kAmbi3rdOrderACN; } return 0; } //------------------------------------------------------------------------ // static //------------------------------------------------------------------------ AAXWrapper* AAXWrapper::create (IPluginFactory* factory, const TUID vst3ComponentID, AAX_Plugin_Desc* desc, AAXWrapper_Parameters* params) { // mostly a copy of BaseWrapper::create if (!factory) return nullptr; BaseWrapper::SVST3Config config; config.processor = nullptr; config.factory = factory; FReleaser factoryReleaser (factory); factory->createInstance (vst3ComponentID, IAudioProcessor::iid, (void**)&config.processor); if (!config.processor) return nullptr; config.controller = nullptr; if (config.processor->queryInterface (IEditController::iid, (void**)&config.controller) != kResultTrue) { if (auto component = U::cast (config.processor)) { TUID editorCID; if (component->getControllerClassId (editorCID) == kResultTrue) { factory->createInstance (editorCID, IEditController::iid, (void**)&config.controller); } } } config.vst3ComponentID = FUID::fromTUID (vst3ComponentID); auto* wrapper = NEW AAXWrapper (config, params, desc); if (wrapper->init () == false || wrapper->setupBusArrangements (desc) != kResultOk) { wrapper->release (); return nullptr; } wrapper->setupBuses (); // again to adjust to changes done by setupBusArrangements // vstwrapper ignores side chain channels, pretend they are main inputs uint64 scBusChannels; wrapper->countSidechainBusChannels (kInput, scBusChannels); wrapper->mMainAudioInputBuses |= scBusChannels; if (auto factory2 = U::cast (factory)) { PFactoryInfo factoryInfo; if (factory2->getFactoryInfo (&factoryInfo) == kResultTrue) wrapper->setVendorName (factoryInfo.vendor); for (int32 i = 0; i < factory2->countClasses (); i++) { Steinberg::PClassInfo2 classInfo2; if (factory2->getClassInfo2 (i, &classInfo2) == Steinberg::kResultTrue) { if (memcmp (classInfo2.cid, vst3ComponentID, sizeof (TUID)) == 0) { wrapper->setSubCategories (classInfo2.subCategories); wrapper->setEffectName (classInfo2.name); if (classInfo2.vendor[0] != 0) wrapper->setVendorName (classInfo2.vendor); break; } } } } return wrapper; } //------------------------------------------------------------------------------- int32 AAXWrapper::countSidechainBusChannels (BusDirection dir, uint64& scBusBitset) { int32 result = 0; scBusBitset = 0; int32 busCount = mComponent->getBusCount (kAudio, dir); for (int32 i = 0; i < busCount; i++) { BusInfo busInfo = {}; if (mComponent->getBusInfo (kAudio, dir, i, busInfo) == kResultTrue) { if (busInfo.busType == kAux) { result += busInfo.channelCount; scBusBitset |= (uint64 (1) << i); // no longer activate side chains by default, use the host // notifications instead // mComponent->activateBus (kAudio, dir, i, true); } } } return result; } //------------------------------------------------------------------------ tresult AAXWrapper::setupBusArrangements (AAX_Plugin_Desc* desc) { uint32 inputBusCount = (desc->mInputChannels > 0 ? 1u : 0u) + (desc->mSideChainInputChannels > 0 ? 1u : 0u); uint32 outputBusCount = (desc->mOutputChannels > 0 ? 1u : 0u); if (AAX_Aux_Desc* aux = desc->mAuxOutputChannels) while ((aux++)->mName) outputBusCount++; std::vector inputs (inputBusCount); std::vector outputs (outputBusCount); uint32 in = 0; if (desc->mInputChannels) inputs[in++] = numChannelsToSpeakerArrangement (desc->mInputChannels); if (desc->mSideChainInputChannels) inputs[in++] = numChannelsToSpeakerArrangement (desc->mSideChainInputChannels); if (desc->mOutputChannels) outputs[0] = numChannelsToSpeakerArrangement (desc->mOutputChannels); if (AAX_Aux_Desc* aux = desc->mAuxOutputChannels) for (uint32 i = 0; aux[i].mName; i++) outputs[i + 1u] = numChannelsToSpeakerArrangement (aux[i].mChannels); return mProcessor->setBusArrangements (inputs.data (), static_cast (inputBusCount), outputs.data (), static_cast (outputBusCount)); } //------------------------------------------------------------------------ void AAXWrapper::guessActiveOutputs (float** out, uint32 num) { // a channel is considered inactive if the output pointer is to the // same location as one of it's neighboring channels (ProTools seems // to direct all inactive channels to the same output). // FIXME: this heuristic fails for mono outputs std::bitset active; for (uint32 i = 0; i < num; i++) { auto prev = (i > 0 ? out[i - 1] : nullptr); auto next = (i + 1 < num ? out[i + 1] : nullptr); active[i] = (out[i] != prev && out[i] != next); } mActiveChannels = active; } //------------------------------------------------------------------------ void AAXWrapper::updateActiveOutputState () { // some additional copying to avoid missing updates std::bitset channels = mActiveChannels; if (channels == mPropagatedChannels) return; mPropagatedChannels = channels; uint32 channelPos = 0; int32 busCount = mComponent->getBusCount (kAudio, kOutput); for (int32 i = 0; i < busCount; i++) { BusInfo busInfo = {}; if (mComponent->getBusInfo (kAudio, kOutput, i, busInfo) == kResultTrue) { bool active = false; for (uint32 c = 0; c < static_cast (busInfo.channelCount); c++) if (channels[channelPos + c]) active = true; channelPos += busInfo.channelCount; mComponent->activateBus (kAudio, kOutput, i, active); } } } //------------------------------------------------------------------------ void AAXWrapper::setSideChainEnable (bool enable) { int32 busCount = mComponent->getBusCount (kAudio, kInput); for (int32 i = 0; i < busCount; i++) { BusInfo busInfo = {}; if (mComponent->getBusInfo (kAudio, kInput, i, busInfo) == kResultTrue) { if (busInfo.busType == kAux) { mComponent->activateBus (kAudio, kInput, i, enable); break; } } } } //----------------------------------------------------------------------------- void AAXWrapper::setRenderingOffline (bool val) { if (val) { if (mVst3processMode == kOffline) return; mVst3processMode = kOffline; } else { if (mVst3processMode == kRealtime) return; mVst3processMode = kRealtime; } bool callStartStop = mProcessing; if (callStartStop) BaseWrapper::_stopProcess (); setupProcessing (); if (callStartStop) BaseWrapper::_startProcess (); } //------------------------------------------------------------------------ // Context structure //------------------------------------------------------------------------ //------------------------------------------------------------------------ using fnCreateParameters = AAX_IEffectParameters* AAX_CALLBACK (); template struct CP { static AAX_IEffectParameters* AAX_CALLBACK Create_Parameters () { auto p = NEW AAXWrapper_Parameters (pluginIndex); if (!p->getWrapper ()) { delete p; p = nullptr; } return p; } }; //------------------------------------------------------------------------ AAX_IEffectGUI* AAX_CALLBACK Create_GUI () { return NEW AAXWrapper_GUI (); } //------------------------------------------------------------------------ int32_t AAX_CALLBACK AlgorithmInitFunction (const AAXWrapper_Context* /*inInstance*/, AAX_EComponentInstanceInitAction /*inAction*/) { return AAX_SUCCESS; } //------------------------------------------------------------------------ Steinberg::int32 AAXWrapper::ResetFieldData (Steinberg::int32 index, void* inData, Steinberg::uint32 inDataSize) { if (index == idxContext && inDataSize == sizeof (AAXWrapper*)) { _suspend (); _resume (); *(AAXWrapper**)inData = this; } else // Default implementation is just to zero out all data. memset (inData, 0, inDataSize); return AAX_SUCCESS; } //------------------------------------------------------------------------ Steinberg::int32 AAXWrapper::Process (AAXWrapper_Context* instance) { //--- ------ Retrieve instance-specific information --------- // Memory blocks const int32_t bufferSize = *static_cast (instance->ptr[idxBufferSize]); AAX_ASSERT (bufferSize <= 1024); uint32 cntMidiPorts = getNumMIDIports (); for (uint32 m = 0; m < cntMidiPorts; m++) { auto* midiNode = static_cast (instance->ptr[idxMidiPorts + m]); AAX_CMidiStream* midiBuffer = midiNode->GetNodeBuffer (); //- Check incoming MIDI packets () // for (uint32_t i = 0; i < midiBuffer->mBufferSize; i++) { AAX_CMidiPacket& buf = midiBuffer->mBuffer[i]; if (buf.mLength > 0) { // skip note-on events if bypassed to reduce processor load for instruments, // but let everything else through to avoid hanging notes if (mSimulateBypass && mBypass) if ((buf.mData[0] & Vst::kStatusMask) == Vst::kNoteOn && buf.mData[2] != 0) continue; Event toAdd = {static_cast (m), static_cast (buf.mTimestamp), 0}; bool isLive = buf.mIsImmediate || buf.mTimestamp == 0; processMidiEvent (toAdd, (char*)&buf.mData[0], isLive); } } } auto pdI = idxInputChannels < 0 ? nullptr : static_cast (instance->ptr[idxInputChannels]); // First input float* inputs[16] = {nullptr}; if (pdI && idxSideChainInputChannels >= 0) { if (auto psc = instance->ptr[idxSideChainInputChannels]) { int32 scChannel = *static_cast (psc); int32 idx = mPluginDesc->mInputChannels; memcpy (inputs, pdI, idx * sizeof (inputs[0])); for (int32 i = 0; i < mPluginDesc->mSideChainInputChannels; i++) inputs[idx + i] = pdI[scChannel]; pdI = inputs; } } // First output float** const AAX_RESTRICT pdO = static_cast (instance->ptr[idxOutputChannels]); if (pdO == nullptr) return AAX_ERROR_NULL_ARGUMENT; uint32 cntOut = getNumOutputs (); uint32 aaxOut = getNumAAXOutputs (); float* outputs[maxActiveChannels]; uint32 mainOuts = static_cast (mPluginDesc->mOutputChannels); if (mainOuts == 6) { // sort Surround channels from AAX (L C R Ls Rs LFE) to VST (L R C LFE Ls Rs) outputs[0] = pdO[0]; outputs[1] = pdO[2]; outputs[2] = pdO[1]; outputs[3] = pdO[5]; outputs[4] = pdO[3]; outputs[5] = pdO[4]; } else mainOuts = 0; for (uint32 i = mainOuts; i < aaxOut; i++) outputs[i] = pdO[i]; float buf[1024]; for (uint32 i = aaxOut; i < cntOut; i++) outputs[i] = buf; guessActiveOutputs (outputs, cntOut); mMetersTmp = (mCntMeters > 0) ? *static_cast (instance->ptr[idxMeters]) : nullptr; _processReplacing (pdI, outputs, bufferSize); mMetersTmp = nullptr; // apply bypass if not supported (only without inputs for now, i.e. instruments) // ---------------------- if (mSimulateBypass && mPluginDesc->mInputChannels == 0) { static const float kDiffGain = 0.001f; if (mBypass) { int32 bufPos = 0; while (mBypassGain > 0 && bufPos < bufferSize) { for (uint32 i = 0; i < cntOut; i++) outputs[i][bufPos] *= mBypassGain; mBypassGain -= kDiffGain; bufPos++; } for (uint32 i = 0; i < cntOut; i++) memset (&outputs[i][bufPos], 0, (bufferSize - bufPos) * sizeof (float)); } else if (mBypassGain < 1) { int32 bufPos = 0; while (mBypassGain < 1 && bufPos < bufferSize) { for (uint32 i = 0; i < cntOut; i++) outputs[i][bufPos] *= mBypassGain; mBypassGain += kDiffGain; bufPos++; } } } return AAX_SUCCESS; } //------------------------------------------------------------------------ void AAXWrapper::processOutputParametersChanges () { if (!mMetersTmp) return; uint32 found = 0; // VU Meter readout for (int32 i = 0, count = mOutputChanges.getParameterCount (); i < count; i++) { auto queue = mOutputChanges.getParameterData (i); if (!queue) break; for (uint32 m = 0; m < mCntMeters; m++) { if (mMeterIds[m] == queue->getParameterId ()) { int32 sampleOffset; ParamValue value; queue->getPoint (queue->getPointCount () - 1, sampleOffset, value); mMetersTmp[m] = static_cast (value); found++; break; } } if (found == mCntMeters) break; } } //----------------------------------------------------------------------------- Steinberg::tresult PLUGIN_API AAXWrapper::restartComponent (Steinberg::int32 flags) { tresult result = BaseWrapper::restartComponent (flags); //--- ---------------------- if (flags & kLatencyChanged) { if (mAAXParams && mProcessor) { AAX_IController* ctrler = mAAXParams->Controller (); if (ctrler) ctrler->SetSignalLatency (static_cast (mProcessor->getLatencySamples ())); } result = kResultTrue; } return result; } //------------------------------------------------------------------------ static void AAX_CALLBACK AlgorithmProcessFunction (AAXWrapper_Context* const inInstancesBegin[], const void* inInstancesEnd) { //--- ------ Iterate over plug-in instances ---------// for (AAXWrapper_Context* const* walk = inInstancesBegin; walk < inInstancesEnd; ++walk) { AAXWrapper_Context* AAX_RESTRICT instance = *walk; // first element is Context AAXWrapper* wrapper = *reinterpret_cast (instance->ptr[0]); if (!wrapper) continue; wrapper->Process (instance); } // End instance-iteration loop } //------------------------------------------------------------------------ static uint32 vst3Category2AAXPlugInCategory (const char* cat) { static const uint32 PDA_ePlugInCategory_Effect = AAX_ePlugInCategory_None; // does not exist anymore? uint32 result = AAX_ePlugInCategory_None; if (strstr (cat, "Fx") != nullptr) { result = PDA_ePlugInCategory_Effect; } if (strstr (cat, "Instrument") != nullptr || strstr (cat, "Generator") != nullptr) { if (strstr (cat, "External") != nullptr) result |= AAX_ePlugInCategory_HWGenerators; else result |= AAX_ePlugInCategory_SWGenerators; } if (strstr (cat, "Delay") != nullptr) result |= AAX_ePlugInCategory_Delay; if (strstr (cat, "Distortion") != nullptr) result |= AAX_ePlugInCategory_Harmonic; if (strstr (cat, "Dynamics") != nullptr) result |= AAX_ePlugInCategory_Dynamics; if (strstr (cat, "EQ") != nullptr) result |= AAX_ePlugInCategory_EQ; if (strstr (cat, "Mastering") != nullptr) result |= AAX_ePlugInCategory_Dither; if (strstr (cat, "Modulation") != nullptr) result |= AAX_ePlugInCategory_Modulation; if (strstr (cat, "Pitch Shift") != nullptr) result |= AAX_ePlugInCategory_PitchShift; if (strstr (cat, "Restoration") != nullptr) result |= AAX_ePlugInCategory_NoiseReduction; if (strstr (cat, "Reverb") != nullptr) result |= AAX_ePlugInCategory_Reverb; if (strstr (cat, "Spatial") != nullptr || strstr (cat, "Surround") != nullptr || strstr (cat, "Up-Downmix") != nullptr) { result |= AAX_ePlugInCategory_SoundField; } return result; } //------------------------------------------------------------------------ // ROUTINE: DescribeAlgorithmComponent // Algorithm component description //------------------------------------------------------------------------ void AAXWrapper::DescribeAlgorithmComponent (AAX_IComponentDescriptor* outDesc, const AAX_Effect_Desc* desc, const AAX_Plugin_Desc* pdesc) { // Describe algorithm's context structure // // Subscribe context fields to host-provided services or information HLOG (HAPI, "%s", __FUNCTION__); AAX_Result err = AAX_SUCCESS; // must be in lock step with AAXWrapper ctor int32 idx = idxBufferSize + 1; // ProTools does not like instruments without inputs (maybe because they are treated as // inserts?) int32 inChannels = pdesc->mInputChannels; if (inChannels == 0) inChannels = pdesc->mOutputChannels; if (inChannels) err = outDesc->AddAudioIn (idx++); AAX_ASSERT (err == AAX_SUCCESS); if (pdesc->mOutputChannels) err = outDesc->AddAudioOut (idx++); AAX_ASSERT (err == AAX_SUCCESS); err = outDesc->AddAudioBufferLength (AAXWrapper::idxBufferSize); AAX_ASSERT (err == AAX_SUCCESS); if (pdesc->mSideChainInputChannels) err = outDesc->AddSideChainIn (idx++); // max 1 side chain!? AAX_ASSERT (err == AAX_SUCCESS); if (pdesc->mMIDIports) { for (AAX_MIDI_Desc* mdesc = pdesc->mMIDIports; mdesc->mName; mdesc++) { err = outDesc->AddMIDINode (idx++, AAX_eMIDINodeType_LocalInput, mdesc->mName, mdesc->mMask); AAX_ASSERT (err == 0); } } if (pdesc->mAuxOutputChannels) { for (AAX_Aux_Desc* auxdesc = pdesc->mAuxOutputChannels; auxdesc->mName; auxdesc++) { int32 ch = (auxdesc->mChannels < 0 ? pdesc->mOutputChannels : auxdesc->mChannels); err = outDesc->AddAuxOutputStem (idx++, getChannelsStem (ch), auxdesc->mName); AAX_ASSERT (err == 0); } } if (pdesc->mMeters) { uint32 cntMeters = 0; for (AAX_Meter_Desc* mdesc = pdesc->mMeters; mdesc->mName; mdesc++) cntMeters++; std::unique_ptr meterIds; meterIds.reset (NEW Steinberg::Vst::ParamID (cntMeters)); cntMeters = 0; for (AAX_Meter_Desc* mdesc = pdesc->mMeters; mdesc->mName; mdesc++) meterIds[cntMeters++] = mdesc->mID; err = outDesc->AddMeters (idx++, (AAX_CTypeID*)meterIds.get (), cntMeters); AAX_ASSERT (err == AAX_SUCCESS); } // Register context fields as private data err = outDesc->AddPrivateData (AAXWrapper::idxContext, sizeof (void*), AAX_ePrivateDataOptions_DefaultOptions); AAX_ASSERT (err == AAX_SUCCESS); // Register processing callbacks // // Create a property map AAX_IPropertyMap* properties = outDesc->NewPropertyMap (); AAX_ASSERT (properties); if (!properties) return; // // Generic properties properties->AddProperty (AAX_eProperty_ManufacturerID, static_cast (desc->mManufacturerID)); properties->AddProperty (AAX_eProperty_ProductID, static_cast (desc->mProductID)); properties->AddProperty (AAX_eProperty_CanBypass, true); properties->AddProperty (AAX_eProperty_LatencyContribution, static_cast (pdesc->mLatency)); // properties->AddProperty (AAX_eProperty_UsesClientGUI, true); // Uses auto-GUI // // Stem format -specific properties // AAX_IPropertyMap* propertiesMono = outDesc->NewPropertyMap(); // AAX_ASSERT (propertiesMono); // if ( !propertiesMono ) return; // properties->Clone(propertiesMono); // if (pdesc->mInputChannels) properties->AddProperty (AAX_eProperty_InputStemFormat, getChannelsStem (pdesc->mInputChannels)); else if (pdesc->mOutputChannels) properties->AddProperty (AAX_eProperty_InputStemFormat, getChannelsStem (pdesc->mOutputChannels)); if (pdesc->mOutputChannels) properties->AddProperty (AAX_eProperty_OutputStemFormat, getChannelsStem (pdesc->mOutputChannels)); if (pdesc->mSideChainInputChannels) { properties->AddProperty (AAX_eProperty_SupportsSideChainInput, true); #if 0 // only mono supported, setting stem format causes plugin load failure properties->AddProperty (AAX_eProperty_SideChainStemFormat, getChannelsStem (pdesc->mSideChainInputChannels)); #endif } properties->AddProperty (AAX_eProperty_PlugInID_Native, static_cast (pdesc->mPlugInIDNative)); properties->AddProperty (AAX_eProperty_PlugInID_AudioSuite, static_cast (pdesc->mPlugInIDAudioSuite)); // Register callbacks // Native (Native and AudioSuite) err = outDesc->AddProcessProc_Native ( AlgorithmProcessFunction, properties, AlgorithmInitFunction, nullptr /*AlgorithmBackgroundFunction*/); AAX_ASSERT (err == AAX_SUCCESS); } //------------------------------------------------------------------------ static AAX_Result GetPlugInDescription (AAX_IEffectDescriptor* outDescriptor, const AAX_Effect_Desc* desc, const AAX_Plugin_Desc* pdesc) { HLOG (HAPI, "%s", __FUNCTION__); int err = AAX_SUCCESS; AAX_IComponentDescriptor* compDesc = outDescriptor->NewComponentDescriptor (); if (!compDesc) return AAX_ERROR_NULL_OBJECT; // Effect identifiers outDescriptor->AddName (pdesc->mName); outDescriptor->AddCategory (vst3Category2AAXPlugInCategory (desc->mCategory)); // Effect components // // Algorithm component AAXWrapper::DescribeAlgorithmComponent (compDesc, desc, pdesc); err = outDescriptor->AddComponent (compDesc); AAX_ASSERT (err == AAX_SUCCESS); // Data model int32 plugIndex = static_cast (pdesc - desc->mPluginDesc); fnCreateParameters* fn = nullptr; switch (plugIndex) { #define ADDPROCPTR(N) \ case N: fn = &CP::Create_Parameters; break; ADDPROCPTR (0) ADDPROCPTR (1) ADDPROCPTR (2) ADDPROCPTR (3) ADDPROCPTR (4) ADDPROCPTR (5) ADDPROCPTR (6) ADDPROCPTR (7) ADDPROCPTR (8) ADDPROCPTR (9) ADDPROCPTR (10) ADDPROCPTR (11) ADDPROCPTR (12) ADDPROCPTR (13) ADDPROCPTR (14) ADDPROCPTR (15) } AAX_ASSERT (fn); err = outDescriptor->AddProcPtr ((void*)fn, kAAX_ProcPtrID_Create_EffectParameters); AAX_ASSERT (err == AAX_SUCCESS); if (desc->mPageFile) outDescriptor->AddResourceInfo (AAX_eResourceType_PageTable, desc->mPageFile); // Effect's meter display properties if (pdesc->mMeters) { for (AAX_Meter_Desc* mdesc = pdesc->mMeters; mdesc->mName; mdesc++) { AAX_IPropertyMap* meterProperties = outDescriptor->NewPropertyMap (); if (!meterProperties) return AAX_ERROR_NULL_OBJECT; // Support different meter types offered by AAX here meterProperties->AddProperty (AAX_eProperty_Meter_Type, static_cast (mdesc->mType)); meterProperties->AddProperty (AAX_eProperty_Meter_Orientation, static_cast (mdesc->mOrientation)); outDescriptor->AddMeterDescription (mdesc->mID, mdesc->mName, meterProperties); } } // plugin supplied GUI err = outDescriptor->AddProcPtr ((void*)Create_GUI, kAAX_ProcPtrID_Create_EffectGUI); AAX_ASSERT (err == AAX_SUCCESS); return AAX_SUCCESS; } //------------------------------------------------------------------------ AAX_Result GetEffectDescriptions (AAX_ICollection* outCollection) { HLOG (HAPI, "%s", __FUNCTION__); AAX_Result result = AAX_ERROR_NULL_OBJECT; AAX_Effect_Desc* effDesc = AAXWrapper_GetDescription (); for (const AAX_Plugin_Desc* pdesc = effDesc->mPluginDesc; pdesc->mEffectID; pdesc++) { if (AAX_IEffectDescriptor* plugInDescriptor = outCollection->NewDescriptor ()) { result = GetPlugInDescription (plugInDescriptor, effDesc, pdesc); if (result == AAX_SUCCESS) result = outCollection->AddEffect (pdesc->mEffectID, plugInDescriptor); AAX_ASSERT (result == AAX_SUCCESS); } } outCollection->SetManufacturerName (effDesc->mManufacturer); /* needed ? char packageName[512]; sprintf (packageName, "%s AAX Plug-In", effDesc->mProduct); outCollection->AddPackageName (packageName); sprintf (packageName, "%s Plug-In", effDesc->mProduct); outCollection->AddPackageName (packageName);*/ outCollection->AddPackageName (effDesc->mProduct); if (strlen (effDesc->mProduct) > 16) { char packageShortName[17] = {0}; strncpy (packageShortName, effDesc->mProduct, 16); outCollection->AddPackageName (packageShortName); } outCollection->SetPackageVersion (effDesc->mVersion); return result; } /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215124701711025265 xustar0030 mtime=1767080905.277379783 30 atime=1767080905.277379783 30 ctime=1767080905.277379783 qtractor-1.5.11/src/vst3/public.sdk/source/vst/aaxwrapper/CMakeLists.txt0000644000175000001440000000266015124701711025261 0ustar00rncbcusers if(NOT SMTG_LINUX) set(target aax_wrapper) set(${target}_sources ${SDK_ROOT}/public.sdk/source/vst/basewrapper/basewrapper.sdk.cpp aaxentry.cpp aaxlibrary.cpp aaxwrapper.cpp aaxwrapper.h aaxwrapper_description.h aaxwrapper_gui.cpp aaxwrapper_gui.h aaxwrapper_parameters.cpp aaxwrapper_parameters.h resource/PlugIn.ico ) add_library(${target} STATIC ${${target}_sources}) target_include_directories(${target} PRIVATE "${SMTG_AAX_SDK_PATH}/Interfaces" "${SMTG_AAX_SDK_PATH}/Interfaces/ACF" "${SMTG_AAX_SDK_PATH}/Libs/AAXLibrary/Include" ) target_link_libraries(${target} PRIVATE base ) smtg_target_setup_universal_binary(${target}) target_compile_features(aax_wrapper PUBLIC cxx_std_17 ) if(XCODE) add_compile_options(-Wno-incompatible-ms-struct) elseif(SMTG_WIN) # too much warnings in the AAX SDK!! add_compile_options(/wd4996) add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_compile_options(/GR) if(MSVC) target_compile_options(${target} PRIVATE /wd4127 # conditional expression is constant /wd5033 # 'register' is no longer a supported storage class ) endif(MSVC) endif(XCODE) endif() qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstgui_win32_bundle_support.cpp0000644000000000000000000000013215124701711026547 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstgui_win32_bundle_support.cpp0000644000175000001440000000470515124701711026545 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstgui_win32_bundle_support.cpp // Created by : Steinberg, 10/2018 // Description : VSTGUI Win32 Bundle Support // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstgui_win32_bundle_support.h" #include "vstgui/lib/platform/platform_win32.h" #include "vstgui/lib/platform/win32/win32support.h" #if ((VSTGUI_VERSION_MAJOR == 4) && (VSTGUI_VERSION_MINOR >= 10)) || (VSTGUI_VERSION_MAJOR > 4) #include "vstgui/lib/platform/win32/win32factory.h" #endif #include "vstgui/lib/optional.h" #include //----------------------------------------------------------------------------- namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ static VSTGUI::Optional ascend (std::string& path, char delimiter = '\\') { auto index = path.find_last_of (delimiter); if (index == std::string::npos) return {}; path.erase (index); return VSTGUI::Optional (std::move (path)); } //----------------------------------------------------------------------------- void setupVSTGUIBundleSupport (void* hInstance) { using namespace VSTGUI; WCHAR path[MAX_PATH]; if (SUCCEEDED (GetModuleFileNameW (static_cast (hInstance), path, MAX_PATH))) { UTF8StringHelper helper (path); auto utf8Path = std::string (helper.getUTF8String ()); if (auto p = ascend (utf8Path)) { if (p = ascend (*p)) { *p += "\\Resources"; #if ((VSTGUI_VERSION_MAJOR == 4) && (VSTGUI_VERSION_MINOR >= 10)) || (VSTGUI_VERSION_MAJOR > 4) if (auto winFactory = VSTGUI::getPlatformFactory ().asWin32Factory ()) { winFactory->setResourceBasePath (VSTGUI::UTF8String (*p)); } #else IWin32PlatformFrame::setResourceBasePath (UTF8String (*p)); #endif } } } } //----------------------------------------------------------------------------- } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vsteditcontroller.h0000644000000000000000000000013215124701711024312 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vsteditcontroller.h0000644000175000001440000003312315124701711024304 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vsteditcontroller.h // Created by : Steinberg, 04/2005 // Description : VST Edit Controller Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "public.sdk/source/vst/vstcomponentbase.h" #include "public.sdk/source/vst/vstparameters.h" #include "public.sdk/source/common/pluginview.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstunits.h" #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { class EditorView; //------------------------------------------------------------------------ /** Default implementation for a VST 3 edit controller. \ingroup vstClasses Can be used as base class for a specific controller implementation */ class EditController : public ComponentBase, public IEditController, public IEditController2 { public: //------------------------------------------------------------------------ EditController (); //---from IEditController------- tresult PLUGIN_API setComponentState (IBStream* state) SMTG_OVERRIDE; tresult PLUGIN_API setState (IBStream* state) SMTG_OVERRIDE; tresult PLUGIN_API getState (IBStream* state) SMTG_OVERRIDE; int32 PLUGIN_API getParameterCount () SMTG_OVERRIDE; tresult PLUGIN_API getParameterInfo (int32 paramIndex, ParameterInfo& info) SMTG_OVERRIDE; tresult PLUGIN_API getParamStringByValue (ParamID tag, ParamValue valueNormalized, String128 string) SMTG_OVERRIDE; tresult PLUGIN_API getParamValueByString (ParamID tag, TChar* string, ParamValue& valueNormalized) SMTG_OVERRIDE; ParamValue PLUGIN_API normalizedParamToPlain (ParamID tag, ParamValue valueNormalized) SMTG_OVERRIDE; ParamValue PLUGIN_API plainParamToNormalized (ParamID tag, ParamValue plainValue) SMTG_OVERRIDE; ParamValue PLUGIN_API getParamNormalized (ParamID tag) SMTG_OVERRIDE; tresult PLUGIN_API setParamNormalized (ParamID tag, ParamValue value) SMTG_OVERRIDE; tresult PLUGIN_API setComponentHandler (IComponentHandler* handler) SMTG_OVERRIDE; IPlugView* PLUGIN_API createView (FIDString /*name*/) SMTG_OVERRIDE {return nullptr;} //---from IEditController2------- tresult PLUGIN_API setKnobMode (KnobMode mode) SMTG_OVERRIDE { hostKnobMode = mode; return kResultTrue; } tresult PLUGIN_API openHelp (TBool /*onlyCheck*/) SMTG_OVERRIDE {return kResultFalse;} tresult PLUGIN_API openAboutBox (TBool /*onlyCheck*/) SMTG_OVERRIDE {return kResultFalse;} //---from ComponentBase--------- tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE; tresult PLUGIN_API terminate () SMTG_OVERRIDE; //---Internal Methods------- virtual tresult beginEdit (ParamID tag); ///< to be called before a serie of performEdit virtual tresult performEdit (ParamID tag, ParamValue valueNormalized); ///< will inform the host about the value change virtual tresult endEdit (ParamID tag); ///< to be called after a serie of performEdit virtual tresult startGroupEdit (); ///< calls IComponentHandler2::startGroupEdit() if host supports it virtual tresult finishGroupEdit (); ///< calls IComponentHandler2::finishGroupEdit() if host supports it virtual void editorDestroyed (EditorView* /*editor*/) {} ///< called from EditorView if it was destroyed virtual void editorAttached (EditorView* /*editor*/) {} ///< called from EditorView if it was attached to a parent virtual void editorRemoved (EditorView* /*editor*/) {} ///< called from EditorView if it was removed from a parent static KnobMode getHostKnobMode () { return hostKnobMode; } ///< return host knob mode /** Gets for a given tag the parameter object. */ virtual Parameter* getParameterObject (ParamID tag) { return parameters.getParameter (tag); } /** Gets for a given tag the parameter information. */ virtual tresult getParameterInfoByTag (ParamID tag, ParameterInfo& info); /** Calls IComponentHandler2::setDirty (state) if host supports it. */ virtual tresult setDirty (TBool state); /** Calls IComponentHandler2::requestOpenEditor (name) if host supports it. */ virtual tresult requestOpenEditor (FIDString name = ViewType::kEditor); //---Accessor Methods------- IComponentHandler* getComponentHandler () const { return componentHandler; } //---Interface--------- OBJ_METHODS (EditController, ComponentBase) DEFINE_INTERFACES DEF_INTERFACE (IEditController) DEF_INTERFACE (IEditController2) END_DEFINE_INTERFACES (ComponentBase) REFCOUNT_METHODS (ComponentBase) //------------------------------------------------------------------------ protected: IPtr componentHandler; IPtr componentHandler2; ParameterContainer parameters; static KnobMode hostKnobMode; }; //------------------------------------------------------------------------ /** View related to an edit controller. \ingroup vstClasses */ class EditorView : public CPluginView { public: //------------------------------------------------------------------------ EditorView (EditController* controller, ViewRect* size = nullptr); ~EditorView () override; /** Gets its controller part. */ EditController* getController () const { return controller; } //---from CPluginView------------- void attachedToParent () SMTG_OVERRIDE; void removedFromParent () SMTG_OVERRIDE; //------------------------------------------------------------------------ protected: IPtr controller; }; //------------------------------------------------------------------------ /** Unit element. \ingroup vstClasses */ class Unit : public FObject { public: //------------------------------------------------------------------------ Unit (const String128 name, UnitID unitId, UnitID parentUnitId = kRootUnitId, ProgramListID programListId = kNoProgramListId); Unit (const UnitInfo& unit); /** Returns its info. */ const UnitInfo& getInfo () const { return info; } /** Returns its Unit ID. */ UnitID getID () const { return info.id; } /** Sets a new Unit ID. */ void setID (UnitID newID) { info.id = newID; } /** Returns its Unit Name. */ const TChar* getName () const { return info.name; } /** Sets a new Unit Name. */ void setName (const String128 newName); /** Returns its ProgramList ID. */ ProgramListID getProgramListID () const { return info.programListId; } /** Sets a new ProgramList ID. */ void setProgramListID (ProgramListID newID) { info.programListId = newID; } OBJ_METHODS (Unit, FObject) //------------------------------------------------------------------------ protected: Unit (); UnitInfo info; }; //------------------------------------------------------------------------ /** ProgramList element. \ingroup vstClasses */ class ProgramList : public FObject { public: //------------------------------------------------------------------------ ProgramList (const String128 name, ProgramListID listId, UnitID unitId); ProgramList (const ProgramList& programList); const ProgramListInfo& getInfo () const { return info; } ProgramListID getID () const { return info.id; } const TChar* getName () const { return info.name; } int32 getCount () const { return info.programCount; } virtual tresult getProgramName (int32 programIndex, String128 name /*out*/); virtual tresult setProgramName (int32 programIndex, const String128 name /*in*/); virtual tresult getProgramInfo (int32 programIndex, CString attributeId, String128 value /*out*/); virtual tresult hasPitchNames (int32 programIndex) { (void)programIndex; return kResultFalse; } virtual tresult getPitchName (int32 programIndex, int16 midiPitch, String128 name /*out*/) { (void)programIndex; (void)midiPitch; (void)name; return kResultFalse; } /** Adds a program to the end of the list. returns the index of the program. */ virtual int32 addProgram (const String128 name); /** Sets a program attribute value. */ virtual bool setProgramInfo (int32 programIndex, CString attributeId, const String128 value); /** Creates and returns the program parameter. */ virtual Parameter* getParameter (); /** Clear all programs. */ virtual void clearPrograms (); OBJ_METHODS (ProgramList, FObject) //------------------------------------------------------------------------ protected: using StringMap = std::map; using StringVector = std::vector; using ProgramInfoVector = std::vector; ProgramListInfo info {}; UnitID unitId; StringVector programNames; ProgramInfoVector programInfos; Parameter* parameter {nullptr}; }; //------------------------------------------------------------------------ /** ProgramListWithPitchNames element. \ingroup vstClasses */ class ProgramListWithPitchNames : public ProgramList { public: ProgramListWithPitchNames (const String128 name, ProgramListID listId, UnitID unitId); /** Sets a name for the given program index and a given pitch. */ bool setPitchName (int32 programIndex, int16 pitch, const String128 pitchName); /** Removes the PitchName entry for the given program index and a given pitch. Returns true if * it was found and removed. */ bool removePitchName (int32 programIndex, int16 pitch); //---from ProgramList--------- int32 addProgram (const String128 name) SMTG_OVERRIDE; tresult hasPitchNames (int32 programIndex) SMTG_OVERRIDE; tresult getPitchName (int32 programIndex, int16 midiPitch, String128 name /*out*/) SMTG_OVERRIDE; OBJ_METHODS (ProgramListWithPitchNames, ProgramList) protected: using PitchNameMap = std::map; using PitchNamesVector = std::vector; PitchNamesVector pitchNames; }; //------------------------------------------------------------------------ /** Advanced implementation (support IUnitInfo) for a VST 3 edit controller. \ingroup vstClasses - [extends EditController] */ class EditControllerEx1 : public EditController, public IUnitInfo { public: EditControllerEx1 (); ~EditControllerEx1 () override; //---from ComponentBase--------- tresult PLUGIN_API terminate () SMTG_OVERRIDE; /** Adds a given unit. */ bool addUnit (Unit* unit); /** Adds a given program list. */ bool addProgramList (ProgramList* list); /** Returns the ProgramList associated to a given listId. */ ProgramList* getProgramList (ProgramListID listId) const; /** Notifies the host about program list changes. */ tresult notifyProgramListChange (ProgramListID listId, int32 programIndex = kAllProgramInvalid); //---from IUnitInfo------------------ int32 PLUGIN_API getUnitCount () SMTG_OVERRIDE { return static_cast (units.size ()); } tresult PLUGIN_API getUnitInfo (int32 unitIndex, UnitInfo& info /*out*/) SMTG_OVERRIDE; int32 PLUGIN_API getProgramListCount () SMTG_OVERRIDE; tresult PLUGIN_API getProgramListInfo (int32 listIndex, ProgramListInfo& info /*out*/) SMTG_OVERRIDE; tresult PLUGIN_API getProgramName (ProgramListID listId, int32 programIndex, String128 name /*out*/) SMTG_OVERRIDE; tresult PLUGIN_API getProgramInfo (ProgramListID listId, int32 programIndex, CString attributeId /*in*/, String128 attributeValue /*out*/) SMTG_OVERRIDE; tresult PLUGIN_API hasProgramPitchNames (ProgramListID listId, int32 programIndex) SMTG_OVERRIDE; tresult PLUGIN_API getProgramPitchName (ProgramListID listId, int32 programIndex, int16 midiPitch, String128 name /*out*/) SMTG_OVERRIDE; virtual tresult setProgramName (ProgramListID listId, int32 programIndex, const String128 name /*in*/); // units selection -------------------- UnitID PLUGIN_API getSelectedUnit () SMTG_OVERRIDE { return selectedUnit; } tresult PLUGIN_API selectUnit (UnitID unitId) SMTG_OVERRIDE { selectedUnit = unitId; return kResultTrue; } tresult PLUGIN_API getUnitByBus (MediaType /*type*/, BusDirection /*dir*/, int32 /*busIndex*/, int32 /*channel*/, UnitID& /*unitId*/ /*out*/) SMTG_OVERRIDE { return kResultFalse; } tresult PLUGIN_API setUnitProgramData (int32 /*listOrUnitId*/, int32 /*programIndex*/, IBStream* /*data*/) SMTG_OVERRIDE { return kResultFalse; } /** Notifies the host about the selected Unit. */ virtual tresult notifyUnitSelection (); //---from IDependent------------------ void PLUGIN_API update (FUnknown* changedUnknown, int32 message) SMTG_OVERRIDE; //---Interface--------- OBJ_METHODS (EditControllerEx1, EditController) DEFINE_INTERFACES DEF_INTERFACE (IUnitInfo) END_DEFINE_INTERFACES (EditController) REFCOUNT_METHODS (EditController) protected: using ProgramListVector = std::vector>; using ProgramIndexMap = std::map; using UnitVector = std::vector>; UnitVector units; ProgramListVector programLists; ProgramIndexMap programIndexMap; UnitID selectedUnit {kRootUnitId}; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstinitiids.cpp0000644000000000000000000000012715124701711023434 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstinitiids.cpp0000644000175000001440000001174415124701711023427 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstinitiids.cpp // Created by : Steinberg, 10/2009 // Description : Interface symbols file // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/base/iplugincompatibility.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivstautomationstate.h" #include "pluginterfaces/vst/ivstchannelcontextinfo.h" #include "pluginterfaces/vst/ivstcontextmenu.h" #include "pluginterfaces/vst/ivstdataexchange.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivstevents.h" #include "pluginterfaces/vst/ivsthostapplication.h" #include "pluginterfaces/vst/ivstinterappaudio.h" #include "pluginterfaces/vst/ivstmessage.h" #include "pluginterfaces/vst/ivstmidimapping2.h" #include "pluginterfaces/vst/ivstmidilearn.h" #include "pluginterfaces/vst/ivstparameterchanges.h" #include "pluginterfaces/vst/ivstparameterfunctionname.h" #include "pluginterfaces/vst/ivstphysicalui.h" #include "pluginterfaces/vst/ivstpluginterfacesupport.h" #include "pluginterfaces/vst/ivstplugview.h" #include "pluginterfaces/vst/ivstprefetchablesupport.h" #include "pluginterfaces/vst/ivstremapparamid.h" #include "pluginterfaces/vst/ivstrepresentation.h" #include "pluginterfaces/vst/ivsttestplugprovider.h" #include "pluginterfaces/vst/ivstunits.h" //------------------------------------------------------------------------ namespace Steinberg { //----VST 3.0-------------------------------- DEF_CLASS_IID (Vst::IComponent) DEF_CLASS_IID (Vst::IAudioProcessor) DEF_CLASS_IID (Vst::IUnitData) DEF_CLASS_IID (Vst::IProgramListData) DEF_CLASS_IID (Vst::IEditController) DEF_CLASS_IID (Vst::IUnitInfo) DEF_CLASS_IID (Vst::IConnectionPoint) DEF_CLASS_IID (Vst::IComponentHandler) DEF_CLASS_IID (Vst::IUnitHandler) DEF_CLASS_IID (Vst::IParamValueQueue) DEF_CLASS_IID (Vst::IParameterChanges) DEF_CLASS_IID (Vst::IEventList) DEF_CLASS_IID (Vst::IMessage) DEF_CLASS_IID (Vst::IHostApplication) DEF_CLASS_IID (Vst::IAttributeList) //----VST 3.0.1-------------------------------- DEF_CLASS_IID (Vst::IMidiMapping) //----VST 3.0.2-------------------------------- DEF_CLASS_IID (Vst::IParameterFinder) //----VST 3.1---------------------------------- DEF_CLASS_IID (Vst::IComponentHandler2) DEF_CLASS_IID (Vst::IEditController2) DEF_CLASS_IID (Vst::IAudioPresentationLatency) DEF_CLASS_IID (Vst::IVst3ToVst2Wrapper) DEF_CLASS_IID (Vst::IVst3ToAUWrapper) //----VST 3.5---------------------------------- DEF_CLASS_IID (Vst::INoteExpressionController) DEF_CLASS_IID (Vst::IKeyswitchController) DEF_CLASS_IID (Vst::IContextMenuTarget) DEF_CLASS_IID (Vst::IContextMenu) DEF_CLASS_IID (Vst::IComponentHandler3) DEF_CLASS_IID (Vst::IEditControllerHostEditing) DEF_CLASS_IID (Vst::IXmlRepresentationController) //----VST 3.6---------------------------------- DEF_CLASS_IID (Vst::IInterAppAudioHost) DEF_CLASS_IID (Vst::IInterAppAudioConnectionNotification) DEF_CLASS_IID (Vst::IInterAppAudioPresetManager) DEF_CLASS_IID (Vst::IStreamAttributes) //----VST 3.6.5-------------------------------- DEF_CLASS_IID (Vst::ChannelContext::IInfoListener) DEF_CLASS_IID (Vst::IPrefetchableSupport) DEF_CLASS_IID (Vst::IUnitHandler2) DEF_CLASS_IID (Vst::IAutomationState) //----VST 3.6.8-------------------------------- DEF_CLASS_IID (Vst::IComponentHandlerBusActivation) DEF_CLASS_IID (Vst::IVst3ToAAXWrapper) //----VST 3.6.11-------------------------------- DEF_CLASS_IID (Vst::INoteExpressionPhysicalUIMapping) //----VST 3.6.12-------------------------------- DEF_CLASS_IID (Vst::IMidiLearn) DEF_CLASS_IID (Vst::IPlugInterfaceSupport) DEF_CLASS_IID (Vst::IVst3WrapperMPESupport) //----VST 3.6.13-------------------------------- DEF_CLASS_IID (Vst::ITestPlugProvider) //----VST 3.7----------------------------------- DEF_CLASS_IID (Vst::IParameterFunctionName) DEF_CLASS_IID (Vst::IProcessContextRequirements) DEF_CLASS_IID (Vst::IProgress) DEF_CLASS_IID (Vst::ITestPlugProvider2) //----VST 3.7.5--------------------------------- DEF_CLASS_IID (IPluginCompatibility) //----VST 3.7.9--------------------------------- DEF_CLASS_IID (Vst::IComponentHandlerSystemTime) DEF_CLASS_IID (Vst::IDataExchangeHandler) DEF_CLASS_IID (Vst::IDataExchangeReceiver) //----VST 3.7.11--------------------------------- DEF_CLASS_IID (Vst::IRemapParamID) //----VST 3.8------------------------------------ DEF_CLASS_IID (Vst::IMidiMapping2) DEF_CLASS_IID (Vst::IMidiLearn2) } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstspeakerarray.h0000644000000000000000000000012715124701711023756 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstspeakerarray.h0000644000175000001440000000443615124701711023751 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstspeakerarray,.h // Created by : Steinberg, 04/2015 // Description : Helper class representing speaker arrangement as array of speaker types. // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/vsttypes.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // SpeakerArray /** Helper class representing speaker arrangement as array of speaker types. */ class SpeakerArray { public: //------------------------------------------------------------------------ SpeakerArray (SpeakerArrangement arr = 0) { setArrangement (arr); } enum { kMaxSpeakers = 64 }; typedef uint64 SpeakerType; int32 total () const { return count; } SpeakerType at (int32 index) const { return speaker[index]; } void setArrangement (SpeakerArrangement arr) { count = 0; memset (speaker, 0, sizeof (speaker)); for (int32 i = 0; i < kMaxSpeakers; i++) { SpeakerType mask = 1ll << i; if (arr & mask) speaker[count++] = mask; } } SpeakerArrangement getArrangement () const { SpeakerArrangement arr = 0; for (int32 i = 0; i < count; i++) arr |= speaker[i]; return arr; } int32 getSpeakerIndex (SpeakerType which) const { for (int32 i = 0; i < count; i++) if (speaker[i] == which) return i; return -1; } //------------------------------------------------------------------------ protected: int32 count; SpeakerType speaker[kMaxSpeakers]; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/auv3wrapper0000644000000000000000000000013015124701711022553 xustar0029 mtime=1767080905.27906427 30 atime=1767080905.278210878 29 ctime=1767080905.27906427 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/0000755000175000001440000000000015124701711022622 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/PaxHeaders/Shared0000644000000000000000000000013115124701711023762 xustar0030 mtime=1767080905.279210874 29 atime=1767080905.27906427 30 ctime=1767080905.279210874 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/0000755000175000001440000000000015124701711024030 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/PaxHeaders/AUv3WrapperFactory.mm0000644000000000000000000000013215124701711030042 xustar0030 mtime=1767080905.279210874 30 atime=1767080905.279210874 30 ctime=1767080905.279210874 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/AUv3WrapperFactory.mm0000644000175000001440000000250015124701711030027 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : // Created by : Steinberg, 07/2017. // Description : VST 3 AUv3Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "AUv3WrapperFactory.h" @implementation AUv3WrapperViewController (AUAudioUnitFactory) - (AUv3Wrapper *) createAudioUnitWithComponentDescription:(AudioComponentDescription) desc error:(NSError **)error { @synchronized (self) { if (!self.audioUnit) { if (![NSThread isMainThread]) { dispatch_sync(dispatch_get_main_queue(), [&]{ self.audioUnit = [[AUv3Wrapper alloc] initWithComponentDescription:desc error:error]; }); } else { self.audioUnit = [[AUv3Wrapper alloc] initWithComponentDescription:desc error:error]; } } } return self.audioUnit; } @end qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/PaxHeaders/AUv3Wrapper.mm0000644000000000000000000000013215124701711026512 xustar0030 mtime=1767080905.279210874 30 atime=1767080905.279201476 30 ctime=1767080905.279210874 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/AUv3Wrapper.mm0000644000175000001440000023325615124701711026515 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : AUv3Wrapper.mm // Created by : Steinberg, 07/2017. // Description : VST 3 AUv3Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "AUv3Wrapper.h" #import #import "public.sdk/source/vst/auwrapper/NSDataIBStream.h" #import "public.sdk/source/vst/hosting/eventlist.h" #import "public.sdk/source/vst/hosting/parameterchanges.h" #import "public.sdk/source/vst/hosting/processdata.h" #import "public.sdk/source/vst/utility/mpeprocessor.h" #import "public.sdk/source/vst/utility/stringconvert.h" #import "pluginterfaces/base/funknownimpl.h" #import "pluginterfaces/gui/iplugview.h" #import "pluginterfaces/vst/ivsteditcontroller.h" #import "pluginterfaces/vst/ivstmidicontrollers.h" #import "pluginterfaces/vst/ivstphysicalui.h" #import "pluginterfaces/vst/ivstpluginterfacesupport.h" #import "pluginterfaces/vst/ivstunits.h" #import "pluginterfaces/vst/vstpresetkeys.h" #import "pluginterfaces/vst/vsttypes.h" #import #import #import #import #import #if TARGET_OS_IPHONE #define SMTG_IOS_MAC_PLATFORMTYPE kPlatformTypeUIView #define SMTG_IOS_MAC_VIEW UIView #define SMTG_IOS_MAC_AutoresizingMask_NotSizable UIViewAutoresizingNone #else #define SMTG_IOS_MAC_VIEW NSView #define SMTG_IOS_MAC_PLATFORMTYPE kPlatformTypeNSView #define SMTG_IOS_MAC_AutoresizingMask_NotSizable NSViewNotSizable #endif extern "C" { //------------------------------------------------------------------------ #if SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY // PluginFactory will be provided externally Steinberg::IPluginFactory* PLUGIN_API GetPluginFactory (); #else // Generate our own factory wrapping the VST3 variant struct VSTBundle { using EntryPtr = bool (*) (CFBundleRef); using ExitPtr = bool (*) (void); VSTBundle () { initialized = init (); } bool loadBundle (CFURLRef bundleUrl) { bundle = CFBundleCreate (kCFAllocatorDefault, bundleUrl); if (!bundle) return false; if (!CFBundleLoadExecutable (bundle)) return false; bundleEntry = (EntryPtr)CFBundleGetFunctionPointerForName (bundle, CFSTR ("bundleEntry")); if (!bundleEntry) return false; bundleExit = (ExitPtr)CFBundleGetFunctionPointerForName (bundle, CFSTR ("bundleExit")); if (!bundleExit) return false; getFactory = (GetFactoryProc)CFBundleGetFunctionPointerForName (bundle, CFSTR ("GetPluginFactory")); if (!bundleExit) return false; if (!bundleEntry (bundle)) return false; return true; } bool init () { NSURL* url = NSBundle.mainBundle.builtInPlugInsURL; url = [url URLByAppendingPathComponent:@"plugin.vst3"]; if (!url) return false; CFURLRef bundleUrl = (__bridge CFURLRef)url; if (!loadBundle (bundleUrl)) { url = NSBundle.mainBundle.builtInPlugInsURL; url = [url URLByAppendingPathComponent:@"auv3.appex"]; NSBundle* appexBundle = [NSBundle bundleWithURL:url]; if (!appexBundle) return false; url = appexBundle.builtInPlugInsURL; url = [url URLByAppendingPathComponent:@"plugin.vst3"]; if (!url) return false; bundleUrl = (__bridge CFURLRef)url; if (!loadBundle (bundleUrl)) return false; } return true; } bool initialized {false}; CFBundleRef bundle {nullptr}; EntryPtr bundleEntry {nullptr}; ExitPtr bundleExit {nullptr}; GetFactoryProc getFactory {nullptr}; }; //------------------------------------------------------------------------ Steinberg::IPluginFactory* PLUGIN_API GetPluginFactory () { static VSTBundle vstBundle; if (vstBundle.initialized) { return vstBundle.getFactory (); } return nullptr; } #endif // SMTG_AUV3_WRAPPER_CUSTOM_PLUGIN_FACTORY } // extern "C" #pragma mark - Helpers from AUv2Wrapper / aucocoaview //------------------------------------------------------------------------ namespace Steinberg { //------------------------------------------------------------------------ template bool executeOnMainThreadSync (Proc p) { if (pthread_main_np () == 0) { dispatch_sync (dispatch_get_main_queue (), p); return true; } return false; } //------------------------------------------------------------------------ class AUPlugFrame final : public IPlugFrame { public: using OnResizeCallback = std::function; AUPlugFrame (OnResizeCallback&& callback) : callback (std::move (callback)) {} tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* vr) SMTG_OVERRIDE { callback (vr); return kResultTrue; } tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) override { QUERY_INTERFACE (_iid, obj, IPlugFrame::iid, IPlugFrame); QUERY_INTERFACE (_iid, obj, ::Steinberg::FUnknown::iid, IPlugFrame); *obj = nullptr; return kNoInterface; } uint32 PLUGIN_API addRef () override { return ++refCount; } uint32 PLUGIN_API release () override { if (--refCount == 0) { delete this; return 0; } return refCount; } private: std::atomic refCount {1}; OnResizeCallback callback; }; namespace Vst { //------------------------------------------------------------------------ static NSString* createCFStringFromString128 (const String128& string) { NSUInteger length = tstrlen (string); return [[NSString alloc] initWithCharacters:reinterpret_cast (string) length:length]; } //------------------------------------------------------------------------ static SpeakerArrangement numChannelsToSpeakerArrangement (UInt32 numChannels) { switch (numChannels) { case 1: return SpeakerArr::kMono; case 2: return SpeakerArr::kStereo; case 6: return SpeakerArr::k51; } return 0; } //------------------------------------------------------------------------ class AUHostApplication : public HostApplication, public IVst3ToAUWrapper, public IVst3WrapperMPESupport { public: __weak AUv3Wrapper* wrapper {nil}; AUHostApplication () { auto pis = getPlugInterfaceSupport (); pis->addPlugInterfaceSupported (INoteExpressionController::iid); pis->addPlugInterfaceSupported (INoteExpressionPhysicalUIMapping::iid); } tresult PLUGIN_API getName (String128 name) SMTG_OVERRIDE { return Vst::StringConvert::convert ("VST3-AU Wrapper", name) ? kResultTrue : kResultFalse; } tresult PLUGIN_API enableMPEInputProcessing (TBool state) SMTG_OVERRIDE { if (!wrapper) return kInternalError; return [wrapper enableMPESupport:state != 0 ? YES : NO] ? kResultTrue : kResultFalse; } tresult PLUGIN_API setMPEInputDeviceSettings (int32 masterChannel, int32 memberBeginChannel, int32 memberEndChannel) SMTG_OVERRIDE { if (!wrapper) return kInternalError; return [wrapper setMPEInputDeviceMasterChannel:masterChannel memberBeginChannel:memberBeginChannel memberEndChannel:memberEndChannel] ? kResultTrue : kResultFalse; } FUnknown* unknownCast () { return static_cast (this); } private: tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) override { QUERY_INTERFACE (_iid, obj, IVst3ToAUWrapper::iid, IVst3ToAUWrapper); QUERY_INTERFACE (_iid, obj, IVst3WrapperMPESupport::iid, IVst3WrapperMPESupport); *obj = nullptr; return HostApplication::queryInterface(_iid, obj); } // this class is always used as singelton and thus does not need ref counting uint32 PLUGIN_API addRef () override { return 100; } uint32 PLUGIN_API release () override { return 100; } }; //------------------------------------------------------------------------ typedef std::map UnitInfoMap; typedef std::vector ParameterGroupVector; //------------------------------------------------------------------------ static void buildUnitInfos (IUnitInfo* unitInfoController, UnitInfoMap& units) { units.clear (); if (unitInfoController) { int32 numUnits = unitInfoController->getUnitCount (); for (int32 i = 0; i < numUnits; i++) { UnitInfo ui; if (unitInfoController->getUnitInfo (i, ui) == kResultTrue) units[ui.id] = ui; } } } //------------------------------------------------------------------------ struct BufferedAudioBus { AUAudioUnitBus* bus = nullptr; AUAudioFrameCount maxFrames = 0; AVAudioPCMBuffer* pcmBuffer = nullptr; AudioBufferList const* originalAudioBufferList = nullptr; AudioBufferList* mutableAudioBufferList = nullptr; void init (AVAudioFormat* defaultFormat, AVAudioChannelCount maxChannels) { maxFrames = 0; pcmBuffer = nullptr; originalAudioBufferList = nullptr; mutableAudioBufferList = nullptr; bus = [[AUAudioUnitBus alloc] initWithFormat:defaultFormat error:nil]; bus.maximumChannelCount = maxChannels; } void allocateRenderResources (AUAudioFrameCount inMaxFrames) { maxFrames = inMaxFrames; pcmBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:bus.format frameCapacity:maxFrames]; originalAudioBufferList = pcmBuffer.audioBufferList; mutableAudioBufferList = pcmBuffer.mutableAudioBufferList; } void deallocateRenderResources () { pcmBuffer = nullptr; originalAudioBufferList = nullptr; mutableAudioBufferList = nullptr; } }; //------------------------------------------------------------------------ // This struct provides a prepareOutputBufferList method to copy the internal buffer pointers to the // output buffer list in case the client passed in null buffer pointers. struct BufferedOutputBus : BufferedAudioBus { void prepareOutputBufferList (AudioBufferList* outBufferList, AVAudioFrameCount frameCount, bool zeroFill) { UInt32 byteSize = frameCount * sizeof (float); for (UInt32 i = 0; i < outBufferList->mNumberBuffers; ++i) { outBufferList->mBuffers[i].mNumberChannels = originalAudioBufferList->mBuffers[i].mNumberChannels; outBufferList->mBuffers[i].mDataByteSize = byteSize; if (outBufferList->mBuffers[i].mData == nullptr) outBufferList->mBuffers[i].mData = originalAudioBufferList->mBuffers[i].mData; if (zeroFill) memset (outBufferList->mBuffers[i].mData, 0, byteSize); } } }; //------------------------------------------------------------------------ // This struct manages a buffer into which an audio unit with input busses can pull its input data. struct BufferedInputBus : BufferedAudioBus { AUAudioUnitStatus pullInput (AudioUnitRenderActionFlags* actionFlags, AudioTimeStamp const* timestamp, AVAudioFrameCount frameCount, NSInteger inputBusNumber, AURenderPullInputBlock pullInputBlock) { if (pullInputBlock == nullptr) return kAudioUnitErr_NoConnection; prepareInputBufferList (); return pullInputBlock (actionFlags, timestamp, frameCount, inputBusNumber, mutableAudioBufferList); } void prepareInputBufferList () { UInt32 byteSize = maxFrames * sizeof (float); mutableAudioBufferList->mNumberBuffers = originalAudioBufferList->mNumberBuffers; for (UInt32 i = 0; i < originalAudioBufferList->mNumberBuffers; ++i) { mutableAudioBufferList->mBuffers[i].mNumberChannels = originalAudioBufferList->mBuffers[i].mNumberChannels; mutableAudioBufferList->mBuffers[i].mData = originalAudioBufferList->mBuffers[i].mData; mutableAudioBufferList->mBuffers[i].mDataByteSize = byteSize; } } }; #pragma mark - ComponentHelper class (implementing IComponentHandler) //------------------------------------------------------------------------ class ComponentHelper : public IComponentHandler { public: ComponentHelper () = default; virtual ~ComponentHelper (); void setWrapper (AUv3Wrapper* wrapper) { auv3Wrapper = wrapper; } tresult PLUGIN_API performEdit (ParamID tag, ParamValue valueNormalized) SMTG_OVERRIDE; tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) SMTG_OVERRIDE; uint32 PLUGIN_API addRef () SMTG_OVERRIDE { return 100; } uint32 PLUGIN_API release () SMTG_OVERRIDE { return 100; } protected: tresult PLUGIN_API beginEdit (ParamID tag) SMTG_OVERRIDE; tresult PLUGIN_API endEdit (ParamID tag) SMTG_OVERRIDE; tresult PLUGIN_API restartComponent (int32 flags) SMTG_OVERRIDE; private: __weak AUv3Wrapper* auv3Wrapper = nil; }; //------------------------------------------------------------------------ ComponentHelper::~ComponentHelper () { auv3Wrapper = nil; } IMPLEMENT_QUERYINTERFACE (ComponentHelper, IComponentHandler, IComponentHandler::iid) //------------------------------------------------------------------------ tresult PLUGIN_API ComponentHelper::beginEdit (ParamID tag) { [auv3Wrapper beginEdit:tag]; return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API ComponentHelper::performEdit (ParamID tag, ParamValue valueNormalized) { [auv3Wrapper performEdit:tag value:valueNormalized]; return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API ComponentHelper::endEdit (ParamID tag) { [auv3Wrapper endEdit:tag]; return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API ComponentHelper::restartComponent (int32 flags) { tresult result = kNotImplemented; // this method informs the host/the AU that something in the VST has changed (latency, parameter // titles, etc.) if (flags & kParamValuesChanged) { // vst plugin parameter values have changed [auv3Wrapper syncParameterValues]; result = kResultTrue; } if (flags & kParamTitlesChanged) { // vst plugin parameter titles, default values or flags (ParameterFlags) have changed [auv3Wrapper onParamTitlesChanged]; result = kResultTrue; } if (flags & kNoteExpressionChanged) { [auv3Wrapper onNoteExpressionChanged]; } if (flags & kLatencyChanged) { [auv3Wrapper onLatencyChanged]; result = kResultTrue; } // TODO: finish restartComponent implementation return result; } #pragma mark - Render Helper structs //------------------------------------------------------------------------ struct MPEHandler : public MPE::Handler { struct Context { IEventList* inputEvents {nullptr}; IMidiMapping* midiMapping {nullptr}; IParameterChanges* outputParamChanges {nullptr}; IComponentHandler* editPerformer {nullptr}; MPE::Processor* mpeProcessor {nullptr}; std::atomic_bool enableMPEProcessing {true}; std::array programChangeParameters {{kNoParamId}}; std::array programChangeParameterStepCount {{0}}; std::array mpeMap {{kInvalidTypeID}}; std::array noteExpValueDescMap {}; }; void process (Context& c, const AUMIDIEvent* event, const AudioTimeStamp* timeStamp) { context = &c; currentSampleOffset = event->eventSampleTime - timeStamp->mSampleTime; if (c.enableMPEProcessing.load ()) { mpeProcssingEnabled = true; c.mpeProcessor->processMIDIInput (event->data, event->length); } else { if (mpeProcssingEnabled) { mpeProcssingEnabled = false; c.mpeProcessor->reset (); } onOtherInput (event->data, event->length); } context = nullptr; } private: Context* context {nullptr}; int32 currentSampleOffset {0}; MPE::NoteID noteIDCounter {0}; bool mpeProcssingEnabled {false}; using Pitch2ParamID = std::array; std::array pitchToParamIDMap {{{0}}}; bool generateNewNoteID (MPE::NoteID& outNoteID) override { if (!context) return false; outNoteID = ++noteIDCounter; return true; } void releaseNoteID (MPE::NoteID noteID) override {} void onMPENoteOn (MPE::NoteID noteID, MPE::Pitch pitch, MPE::Velocity velocity) override { if (!context) return; sendNoteOn (noteID, pitch, velocity); } void onMPENoteOff (MPE::NoteID noteID, MPE::Pitch pitch, MPE::Velocity velocity) override { if (!context) return; sendNoteOff (noteID, pitch, velocity); } void onMPEControllerChange (MPE::NoteID noteID, MPE::Controller cc, MPE::NormalizedValue value) override { if (!context || cc > MPE::Controller::Y) return; auto index = static_cast (cc); if (context->mpeMap[index] != kInvalidTypeID) { Event e {}; e.type = Event::kNoteExpressionValueEvent; e.noteExpressionValue.noteId = noteID; e.noteExpressionValue.typeId = context->mpeMap[index]; e.noteExpressionValue.value = (value * (context->noteExpValueDescMap[index].maximum - context->noteExpValueDescMap[index].minimum)) + context->noteExpValueDescMap[index].minimum; e.sampleOffset = currentSampleOffset; context->inputEvents->addEvent (e); } } void sendNoteOn (MPE::NoteID noteID, MPE::Pitch pitch, MPE::Velocity velocity, int16 channel = 0) { if (!context->inputEvents) return; Event e {}; e.type = Event::kNoteOnEvent; e.noteOn.pitch = pitch; e.noteOn.velocity = velocity; e.noteOn.noteId = noteID; e.noteOn.channel = channel; e.sampleOffset = currentSampleOffset; context->inputEvents->addEvent (e); } void sendNoteOff (MPE::NoteID noteID, MPE::Pitch pitch, MPE::Velocity velocity, int16 channel = 0) { if (!context->inputEvents) return; Event e {}; e.type = Event::kNoteOffEvent; e.noteOff.pitch = pitch; e.noteOff.velocity = velocity; e.noteOff.noteId = noteID; e.noteOff.channel = channel; e.sampleOffset = currentSampleOffset; context->inputEvents->addEvent (e); } void sendPolyPressure (MPE::NoteID noteID, MPE::Pitch pitch, MPE::NormalizedValue pressure, int16 channel) { if (!context->inputEvents) return; Event e {}; e.type = Event::kPolyPressureEvent; e.polyPressure.channel = channel; e.polyPressure.pitch = pitch; e.polyPressure.pressure = pressure; e.polyPressure.noteId = noteID; e.sampleOffset = currentSampleOffset; context->inputEvents->addEvent (e); } void sendControllerChange (CtrlNumber controller, MPE::NormalizedValue value, int16 channel) { if (!context->midiMapping) return; ParamID outParamID; if (context->midiMapping->getMidiControllerAssignment (0, channel, controller, outParamID) != kResultTrue) return; context->editPerformer->performEdit (outParamID, value); int32 index; if (auto vq = context->outputParamChanges->addParameterData (outParamID, index)) vq->addPoint (currentSampleOffset, value, index); } void sendProgramChange (int16 channel, uint8 midiValue) { auto prgChangeParamID = context->programChangeParameters[channel]; if (prgChangeParamID == kNoParamId) return; auto value = -1.; auto stepCount = context->programChangeParameterStepCount[channel]; if (stepCount = 0) value = static_cast (midiValue) / 127.; else if (midiValue <= stepCount) value = static_cast (midiValue) / static_cast (stepCount); if (value == -1.) return; context->editPerformer->performEdit (prgChangeParamID, value); int32 index; if (auto vq = context->outputParamChanges->addParameterData (prgChangeParamID, index)) { vq->addPoint (currentSampleOffset, value, index); } } void onOtherInput (const uint8_t* data, size_t dataSize) override { if (!context || dataSize < 2) return; constexpr auto NoteOff = 0x80; constexpr auto NoteOn = 0x90; constexpr auto Aftertouch = 0xa0; constexpr auto Controller = 0xb0; constexpr auto ProgramChange = 0xc0; constexpr auto ChannelPressure = 0xd0; constexpr auto PitchWheel = 0xe0; auto status = data[0] & 0xF0; auto channel = data[0] & 0x0F; switch (status) { case NoteOn: { auto pitch = data[1]; if (data[2] != 0) { auto velocity = static_cast (data[2]) / 127.; if (pitchToParamIDMap[channel][pitch] != 0) sendNoteOff (pitchToParamIDMap[channel][pitch], pitch, velocity); MPE::NoteID noteID; if (generateNewNoteID (noteID)) { pitchToParamIDMap[channel][pitch] = noteID; sendNoteOn (noteID, pitch, velocity, channel); } } else { if (pitchToParamIDMap[channel][pitch] != 0) { sendNoteOff (pitchToParamIDMap[channel][pitch], pitch, 0, channel); pitchToParamIDMap[channel][pitch] = 0; } } break; } case NoteOff: { auto pitch = data[1]; if (pitchToParamIDMap[channel][pitch] != 0) { auto velocity = static_cast (data[2]) / 127.; sendNoteOff (pitchToParamIDMap[channel][pitch], pitch, velocity, channel); pitchToParamIDMap[channel][pitch] = 0; } break; } case Aftertouch: { auto value = static_cast (data[1]) / 127.; sendControllerChange (ControllerNumbers::kAfterTouch, value, channel); break; } case Controller: { auto value = static_cast (data[2]) / 127.; sendControllerChange (data[1], value, channel); break; } case ProgramChange: { sendProgramChange (channel, data[1]); break; } case ChannelPressure: { sendPolyPressure (-1, data[1], static_cast (data[2]) / 127., channel); break; } case PitchWheel: { auto value = static_cast ((data[1] & 0x7F) + ((data[2] & 0x7F) << 7)) / 16383.; sendControllerChange (ControllerNumbers::kPitchBend, value, channel); break; } } } void onSysexInput (const uint8_t* data, size_t dataSize) override { // no sysex handling } // error handling void errorNoteDroppedBecauseNoNoteID (MPE::Pitch pitch) override {} void errorNoteDroppedBecauseNoteStackFull (MPE::Channel channel, MPE::Pitch pitch) override {} void errorNoteForNoteOffNotFound (MPE::Channel channel, MPE::Pitch pitch) override {} void errorProgramChangeReceivedInMPEZone () override {} }; struct RenderProcessContext { RenderProcessContext () {} AUHostMusicalContextBlock musicalContext; AUHostTransportStateBlock transportContext; const AudioTimeStamp* timestamp; ProcessContext* processContext; double sampleRate; void updateProcessContext (const RenderProcessContext& renderContext) { Float64 currentTempo = 0; double timeSignatureNumerator = 0; NSInteger timeSignatureDenominator = 0; Float64 currentBeatPosition = 0; NSInteger sampleOffsetToNextBeat = 0; Float64 currentMeasureDownbeatPosition = 0; AUHostTransportStateFlags transportStateFlags; double currentSamplePosition = 0; double cycleStartBeatPosition = 0; double cycleEndBeatPosition = 0; if (musicalContext) { musicalContext (¤tTempo, &timeSignatureNumerator, &timeSignatureDenominator, ¤tBeatPosition, &sampleOffsetToNextBeat, ¤tMeasureDownbeatPosition); processContext->state |= ProcessContext::kTempoValid | ProcessContext::kTimeSigValid | ProcessContext::kProjectTimeMusicValid | ProcessContext::kBarPositionValid | ProcessContext::kClockValid; processContext->tempo = currentTempo; processContext->projectTimeMusic = currentBeatPosition; processContext->timeSigNumerator = timeSignatureNumerator; processContext->timeSigDenominator = (int32)timeSignatureDenominator; processContext->samplesToNextClock = (int32)sampleOffsetToNextBeat; processContext->barPositionMusic = currentMeasureDownbeatPosition; } if (transportContext) { transportContext (&transportStateFlags, ¤tSamplePosition, &cycleStartBeatPosition, &cycleEndBeatPosition); processContext->state |= ProcessContext::kCycleValid; processContext->cycleStartMusic = cycleStartBeatPosition; processContext->cycleEndMusic = cycleEndBeatPosition; processContext->projectTimeSamples = currentSamplePosition; switch (transportStateFlags) { case AUHostTransportStateMoving: processContext->state |= ProcessContext::kPlaying; break; case AUHostTransportStateRecording: processContext->state |= ProcessContext::kRecording; break; case AUHostTransportStateCycling: processContext->state |= ProcessContext::kCycleActive; break; } } processContext->sampleRate = sampleRate; processContext->systemTime = timestamp->mHostTime; } }; } // Vst } // Steinberg //------------------------------------------------------------------------ // AUv3Wrapper main class //------------------------------------------------------------------------ #pragma mark - AUv3Wrapper main class (implementing AUAudioUnit) using namespace Steinberg; using namespace Vst; @interface AUv3Wrapper () @property AUAudioUnitBusArray* inputBusArray; @property AUAudioUnitBusArray* outputBusArray; @property (nonatomic, readonly) AUParameterTree* parameterTree; @property IEditController* editcontroller; @end @implementation AUv3Wrapper { std::vector inputBusBuffers; std::vector outputBusBuffers; AUHostMusicalContextBlock musicalContext; AUHostTransportStateBlock transportContext; AUParameterObserverToken parameterObserverToken; NSDictionary* fullStateVar; NSMutableArray* factoryPresetsVar; NSArray* channelCapabilitiesArray; AUParameterTree* parameterTreeVar; IPtr audioProcessor; IMidiMapping* midiMapping; HostProcessData processData; ParameterChanges inputParamChanges; ParameterChanges outputParamChanges; ParameterChangeTransfer transferParamChanges; ParameterChangeTransfer outputParamTransfer; ProcessContext processContext; EventList inputEvents; EventList outputEvents; AUHostApplication hostAppContext; MPEHandler mpeHandler; MPEHandler::Context mpeHandlerContext; std::unique_ptr mpeProcessor; UnitInfoMap unitInfos; ParameterGroupVector parameterGroups; NoteInstanceID noteCounter; double sampleRate; int32 bypassParamID; int32 numPresets; int32 factoryProgramChangedID; bool isInstrument; bool isBypassed; bool canProcessInPlaceValue; bool renderingOfflineValue; // Midi Stuff int32 midiOutCount; // currently only 0 or 1 supported NSMutableArray* overviewParams; ComponentHelper componentHelper; NSTimer* timer; AUAudioUnitPreset* currentPreset; NSInteger currentFactoryPresetIndex; AUParameterObserverToken bypassObserverToken; } //------------------------------------------------------------------------ // MARK: AUAudioUnit init & dealloc //------------------------------------------------------------------------ - (instancetype)initWithComponentDescription:(AudioComponentDescription)componentDescription options:(AudioComponentInstantiationOptions)options error:(NSError**)outError { self = [super initWithComponentDescription:componentDescription options:options error:outError]; if (self == nil) return nil; // set instance variables sampleRate = 44100.0; bypassParamID = -1; factoryProgramChangedID = -1; isInstrument = false; isBypassed = false; outputParamTransfer.setMaxParameters (500); transferParamChanges.setMaxParameters (500); componentHelper.setWrapper (self); timer = [NSTimer timerWithTimeInterval:1./60. repeats:YES block:^(NSTimer * _Nonnull timer) { [self onTimer]; }]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; canProcessInPlaceValue = false; renderingOfflineValue = false; processData.processContext = &processContext; processData.inputParameterChanges = &inputParamChanges; processData.outputParameterChanges = &outputParamChanges; inputEvents.setMaxSize (128); outputEvents.setMaxSize (128); processData.inputEvents = &inputEvents; processData.outputEvents = &outputEvents; hostAppContext.wrapper = self; // create the channel capabilities NSBundle* mainBundle = [NSBundle mainBundle]; NSString* test = [mainBundle objectForInfoDictionaryKey:@"SupportedNumChannels"]; NSMutableArray* supportedNumChannelsArray = [NSMutableArray array]; for (int i = 0; i < test.length; i++) { NSString* newString = [test substringWithRange:NSMakeRange (i, 1)]; [supportedNumChannelsArray addObject:[NSNumber numberWithInt:[newString intValue]]]; } channelCapabilitiesArray = supportedNumChannelsArray; // load VST processor and controller [self loadVst]; // initialize busses [self initializeBusses]; // initialize parameters [self updateParameters]; // initialize presets [self loadPresetList]; // MPE Support mpeProcessor = std::unique_ptr (new MPE::Processor (&mpeHandler)); mpeHandlerContext.mpeProcessor = mpeProcessor.get (); mpeHandlerContext.midiMapping = midiMapping; mpeHandlerContext.inputEvents = &inputEvents; mpeHandlerContext.outputParamChanges = &outputParamChanges; mpeHandlerContext.editPerformer = &componentHelper; [self onNoteExpressionChanged]; return self; } //------------------------------------------------------------------------ - (void)onNoteExpressionChanged { if (!self.supportsMPE) return; if (auto puiMapping = U::cast (_editcontroller)) { PhysicalUIMap map[3]; map[0].physicalUITypeID = kPUIXMovement; map[1].physicalUITypeID = kPUIYMovement; map[2].physicalUITypeID = kPUIPressure; PhysicalUIMapList mapList {3, map}; // We can only map one bus and one channel with MPE in AUv3 if (puiMapping->getPhysicalUIMapping (0, 0, mapList) == kResultTrue) { mpeHandlerContext.mpeMap[0] = map[2].noteExpressionTypeID; mpeHandlerContext.mpeMap[1] = map[0].noteExpressionTypeID; mpeHandlerContext.mpeMap[2] = map[1].noteExpressionTypeID; mpeHandlerContext.noteExpValueDescMap[0] = {}; mpeHandlerContext.noteExpValueDescMap[1] = {}; mpeHandlerContext.noteExpValueDescMap[2] = {}; if (auto nExpCtrler = U::cast (_editcontroller)) { auto count = nExpCtrler->getNoteExpressionCount (0, 0); for (auto i = 0; i < count; ++i) { NoteExpressionTypeInfo info; if (nExpCtrler->getNoteExpressionInfo (0, 0, i, info) != kResultTrue) continue; for (auto index = 0; index < 3; ++index) { if (mpeHandlerContext.mpeMap[index] != info.typeId) continue; mpeHandlerContext.noteExpValueDescMap[index] = info.valueDesc; break; } } } } } } //------------------------------------------------------------------------ - (BOOL)enableMPESupport:(BOOL)state { if (!mpeProcessor) return NO; mpeHandlerContext.enableMPEProcessing.store (state); return YES; } //------------------------------------------------------------------------ - (BOOL)setMPEInputDeviceMasterChannel:(NSInteger)masterChannel memberBeginChannel:(NSInteger)memberBeginChannel memberEndChannel:(NSInteger)memberEndChannel { if (!mpeProcessor) return NO; auto setup = mpeProcessor->getSetup (); setup.masterChannel = static_cast (masterChannel); setup.memberChannelBegin = static_cast (memberBeginChannel); setup.memberChannelEnd = static_cast (memberEndChannel); mpeProcessor->changeSetup (setup); return YES; } //------------------------------------------------------------------------ - (void)dealloc { if (audioProcessor) { auto combined = U::cast (audioProcessor); if (!combined) { auto controllerConnectionPoint = U::cast (_editcontroller); auto processorConnectionPoint = U::cast (audioProcessor); if (controllerConnectionPoint && processorConnectionPoint) { controllerConnectionPoint->disconnect (processorConnectionPoint); processorConnectionPoint->disconnect (controllerConnectionPoint); } } } if (midiMapping) { midiMapping->release (); midiMapping = nullptr; } if (_editcontroller) { _editcontroller->setComponentHandler (nullptr); uint32 refCount = _editcontroller->addRef (); if (refCount == 2) _editcontroller->terminate (); _editcontroller->release (); _editcontroller->release (); _editcontroller = nil; } if (audioProcessor) { FUnknownPtr (audioProcessor)->terminate (); audioProcessor = nullptr; } if (parameterObserverToken != nullptr) { [parameterTreeVar removeParameterObserver:parameterObserverToken]; parameterObserverToken = nullptr; } if (timer) { [timer invalidate]; timer = nullptr; } } //------------------------------------------------------------------------ // MARK: Helper Functions //------------------------------------------------------------------------ - (NSString*)getParameterStringFromValue:(int)tag value:(AUValue)value { String128 paramString; if (_editcontroller->getParamStringByValue (tag, value, paramString) == kResultTrue) { return createCFStringFromString128 (paramString); } return @""; } //------------------------------------------------------------------------ - (AUValue)getParameterValueFromString:(int)tag string:(NSString*)nsString { ParamValue outValue; CFStringRef cfString = (__bridge CFStringRef)nsString; String128 string128; CFStringGetCharacters (cfString, CFRangeMake (0, std::max (128, CFStringGetLength (cfString))), (UniChar*)string128); _editcontroller->getParamValueByString (tag, string128, outValue); return (float)outValue; } //------------------------------------------------------------------------ - (void)loadVst { auto factory = U::cast (owned (GetPluginFactory ())); if (!factory) return; // find first audio processor class for (int32 i = 0; i < factory->countClasses (); i++) { PClassInfo2 ci; if (factory->getClassInfo2 (i, &ci) == kResultTrue) { if (strcmp (ci.category, kVstAudioEffectClass) == 0) { IAudioProcessor* proc = nullptr; if (factory->createInstance (ci.cid, IAudioProcessor::iid, (void**)&proc) == kResultTrue) { audioProcessor = owned (proc); std::string_view plugCategory (ci.subCategories); std::string_view instrumentStr ("instrument"); auto it = std::search (plugCategory.begin (), plugCategory.end (), instrumentStr.begin (), instrumentStr.end (), [] (auto ch1, auto ch2) { return std::toupper (ch1) == std::toupper (ch2); }); if (it != plugCategory.end ()) isInstrument = true; break; } } } } if (audioProcessor) { if (FUnknownPtr (audioProcessor)->initialize (hostAppContext.unknownCast ()) != kResultTrue) return; if (audioProcessor->queryInterface (IEditController::iid, (void**)&_editcontroller) != kResultTrue) { if (auto component = U::cast (audioProcessor)) { TUID ccid; if (component->getControllerClassId (ccid) == kResultTrue) { if (factory->createInstance (ccid, IEditController::iid, (void**)&_editcontroller) == kResultTrue) { if (_editcontroller->initialize (hostAppContext.unknownCast ()) != kResultTrue) { _editcontroller->release (); _editcontroller = nullptr; return; } auto controllerConnectionPoint = U::cast (_editcontroller); auto processorConnectionPoint = U::cast (audioProcessor); if (controllerConnectionPoint && processorConnectionPoint) { controllerConnectionPoint->connect (processorConnectionPoint); processorConnectionPoint->connect (controllerConnectionPoint); } NSMutableData* processorData = [[NSMutableData alloc] init]; NSMutableDataIBStream stream (processorData); if (FUnknownPtr (audioProcessor)->getState (&stream) == kResultTrue) { stream.seek (0, IBStream::kIBSeekSet); _editcontroller->setComponentState (&stream); } } } } } } } //------------------------------------------------------------------------ - (void)initializeBusses { NSMutableArray* outBusArray = [[NSMutableArray alloc] init]; NSMutableArray* inBusArray = [[NSMutableArray alloc] init]; if (_editcontroller) { _editcontroller->setComponentHandler (&componentHelper); auto component = U::cast (audioProcessor); int32 inputBusCount = component->getBusCount (kAudio, kInput); int32 outputBusCount = component->getBusCount (kAudio, kOutput); // create input busses [self createBusses:true busCount:inputBusCount busArray:inBusArray component:component]; // create output busses [self createBusses:false busCount:outputBusCount busArray:outBusArray component:component]; processData.prepare (*component, 0, kSample32); midiOutCount = component->getBusCount (kEvent, kOutput); _editcontroller->queryInterface (IMidiMapping::iid, (void**)&midiMapping); } // create the input and output bus arrays (AUAudioUnitBusArray) _inputBusArray = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeInput busses:inBusArray]; // create the input and output bus arrays. _outputBusArray = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeOutput busses:outBusArray]; } //------------------------------------------------------------------------ - (void)createBusses:(bool)isInput busCount:(int)busCount busArray:(NSMutableArray*)busArray component:(IPtr)component { SpeakerArrangement sa; int inputOrOutput = isInput ? kInput : kOutput; for (int32 busNum = 0; busNum < busCount; busNum++) { AUAudioUnitBus* bus; AVAudioFormat* customFormat; if (audioProcessor->getBusArrangement (inputOrOutput, busNum, sa) == kResultTrue) { customFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:sampleRate channels:SpeakerArr::getChannelCount (sa)]; bus = [[AUAudioUnitBus alloc] initWithFormat:customFormat error:nil]; bus.maximumChannelCount = customFormat.channelCount; } BusInfo info {}; if (component->getBusInfo (kAudio, inputOrOutput, busNum, info) == kResultTrue) { bus.name = createCFStringFromString128 (info.name); } component->activateBus (kAudio, isInput ? kInput : kOutput, busNum, true); if (bus != nil) [busArray addObject:bus]; } } //------------------------------------------------------------------------ - (void)allocateBusBuffers { auto component = U::cast (audioProcessor); for (int i = 0; i < component->getBusCount (kAudio, kInput); i++) { BufferedInputBus bus; bus.init (_inputBusArray[i].format, _inputBusArray[i].format.channelCount); bus.allocateRenderResources (self.maximumFramesToRender); inputBusBuffers.push_back (bus); } for (int i = 0; i < component->getBusCount (kAudio, kOutput); i++) { BufferedOutputBus bus; bus.init (_outputBusArray[i].format, _outputBusArray[i].format.channelCount); bus.allocateRenderResources (self.maximumFramesToRender); outputBusBuffers.push_back (bus); } // if no input bus create empty one if (_inputBusArray.count == 0) { AVAudioFormat* defaultFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:sampleRate channels:2]; BufferedInputBus bus; bus.init (defaultFormat, 2); bus.allocateRenderResources (self.maximumFramesToRender); inputBusBuffers.push_back (bus); } } - (void)onLatencyChanged { [self willChangeValueForKey:@"latency"]; [self didChangeValueForKey:@"latency"]; } //------------------------------------------------------------------------ - (void)onParamTitlesChanged { [self willChangeValueForKey:@"parameterTree"]; [self updateParameters]; [self didChangeValueForKey:@"parameterTree"]; } //------------------------------------------------------------------------ - (void)updateParameters { BOOL initParams = overviewParams ? NO : YES; if (parameterTreeVar && parameterObserverToken != nullptr) { [parameterTreeVar removeParameterObserver:parameterObserverToken]; parameterObserverToken = nullptr; } // AUv3 Parameter Initialization overviewParams = [[NSMutableArray alloc] init]; NSMutableArray* paramArray = [[NSMutableArray alloc] init]; NSMutableArray* paramArrayWithHierarchy = [[NSMutableArray alloc] init]; // create parameters [self createParameters:paramArrayWithHierarchy paramArray:paramArray initParams:initParams]; // create the paramArray with AUParameterGroups for (int32 i = 0; i < [paramArrayWithHierarchy count]; i++) { if (i == 0) { for (int32 j = 0; j < [[paramArrayWithHierarchy objectAtIndex:i] count]; j++) [paramArray addObject:paramArrayWithHierarchy[i][j]]; } else { auto& str = parameterGroups.at (i - 1); NSString* groupName = [NSString stringWithCharacters:reinterpret_cast (str.data ()) length:str.size ()]; AUParameterGroup* newGroup = [AUParameterTree createGroupWithIdentifier:groupName name:groupName children:paramArrayWithHierarchy[i]]; [paramArray addObject:newGroup]; } } // create the parameter tree parameterTreeVar = [AUParameterTree createTreeWithChildren:paramArray]; // implementorValueObserver is called when a parameter changes value. __weak AUv3Wrapper* weakSelf = self; parameterTreeVar.implementorValueObserver = ^(AUParameter* param, AUValue value) { [weakSelf setParameter:(int)param.address value:value]; }; // A function to provide string representations of parameter values. parameterTreeVar.implementorStringFromValueCallback = ^(AUParameter* param, const AUValue* __nullable valuePtr) { AUValue value = valuePtr == nil ? param.value : *valuePtr; return [weakSelf getParameterStringFromValue:(int)param.address value:value]; }; parameterObserverToken = [parameterTreeVar tokenByAddingParameterObserver:^(AUParameterAddress address, AUValue value) { dispatch_async (dispatch_get_main_queue (), ^{ __strong AUv3Wrapper* strongSelf = weakSelf; [strongSelf setControllerParameter:(int)address value:value]; }); }]; // A function to provide the parameter values of string representations. parameterTreeVar.implementorValueFromStringCallback = ^(AUParameter* param, NSString* string) { return [weakSelf getParameterValueFromString:(int)param.address string:string]; }; } //------------------------------------------------------------------------ - (void)createParameters:(NSMutableArray*)paramArrayWithHierarchy paramArray:(NSMutableArray*)paramArray initParams:(BOOL)initParams { // for each VST3 parameter int32 parameterCount = _editcontroller->getParameterCount (); for (int32 i = 0; i < parameterCount; i++) { ParameterInfo pi {}; _editcontroller->getParameterInfo (i, pi); if (pi.flags & ParameterInfo::kIsBypass) { bypassParamID = pi.id; } else { // Build parameter group if possible, maps to the AUParameterGroup Node in the // parameterTree int32 groupIdx = [self buildParameterGroup:pi]; // create identifier for parameter NSString* identifier = [NSString stringWithString:[NSString stringWithFormat:@"%d", pi.id]]; NSString* name = createCFStringFromString128 (pi.title); NSString* unitName = createCFStringFromString128 (pi.units); AUParameterAddress address = static_cast (pi.id); // create flags for parameter AudioUnitParameterOptions flagsToSet = kAudioUnitParameterFlag_ValuesHaveStrings | kAudioUnitParameterFlag_IsReadable; if (!(pi.flags & ParameterInfo::kIsReadOnly)) flagsToSet |= kAudioUnitParameterFlag_IsWritable; if (!(pi.flags & ParameterInfo::kCanAutomate)) flagsToSet |= kAudioUnitParameterFlag_NonRealTime; if (pi.stepCount == 1) flagsToSet = kAudioUnitParameterUnit_Boolean; // create the parameter AUParameter* sPar = [AUParameterTree createParameterWithIdentifier:identifier name:name address:address min:0.0 max:1.0 unit:kAudioUnitParameterUnit_CustomUnit unitName:unitName flags:flagsToSet valueStrings:nil dependentParameters:nil]; if (initParams) { // initialize the parameter values sPar.value = pi.defaultNormalizedValue; [self setParameter:pi.id value:pi.defaultNormalizedValue]; [self setControllerParameter:pi.id value:pi.defaultNormalizedValue]; } else { sPar.value = _editcontroller->getParamNormalized (pi.id); } // add parameter to the paramArrayWithHierarchy (with hierarchy/AUParameterGroups) while ([paramArrayWithHierarchy count] <= groupIdx) [paramArrayWithHierarchy addObject:[[NSMutableArray alloc] init]]; NSUInteger groupOfParam = (NSUInteger)groupIdx; [[paramArrayWithHierarchy objectAtIndex:groupOfParam] addObject:sPar]; [overviewParams addObject:[NSNumber numberWithUnsignedLongLong:address]]; } } } //------------------------------------------------------------------------ - (int32)buildParameterGroup:(ParameterInfo)pi { int32 groupIdx = 0; // Zero means parameter is on top level node of parameter tree if (auto unitInfoController = U::cast (_editcontroller)) { if (unitInfos.empty ()) buildUnitInfos (unitInfoController, unitInfos); std::u16string fullParamName; constexpr auto separator = STR ("."); bool insertSeparator = false; UnitInfoMap::const_iterator it = unitInfos.find (pi.unitId); while (it != unitInfos.end () && it->second.id != kRootUnitId) { std::u16string_view unitName (it->second.name); if (unitName.size () > 0) { if (insertSeparator) fullParamName.insert (0, separator); insertSeparator = true; fullParamName.insert (0, unitName); } it = unitInfos.find (it->second.parentUnitId); } if (!fullParamName.empty ()) { bool insertGroup = true; groupIdx = 1; for (int32 i = 0; i < parameterGroups.size (); i++) { const auto& str = parameterGroups.at (i); if (str == fullParamName) { insertGroup = false; break; } ++groupIdx; } if (insertGroup) parameterGroups.push_back (fullParamName); } } return groupIdx; } //------------------------------------------------------------------------ - (void)setupProcessing { if (audioProcessor && _editcontroller) { auto component = U::cast (audioProcessor); // after set Bus Arrangement, the channelbuffers may need to be reallocated -> hence the // second prepare! processData.prepare (*component, 0, kSample32); ProcessSetup ps; ps.sampleRate = sampleRate; ps.maxSamplesPerBlock = self.maximumFramesToRender; ps.symbolicSampleSize = kSample32; ps.processMode = self.isRenderingOffline ? kOffline : kRealtime; audioProcessor->setupProcessing (ps); component->setActive (true); audioProcessor->setProcessing (true); } } //------------------------------------------------------------------------ - (bool)getProgramListAndUnit:(int32)midiChannel unitId:(UnitID&)unitId programListId:(ProgramListID&)programListId { programListId = kNoProgramListId; unitId = -1; IUnitInfo* unitInfo = NULL; if (_editcontroller && _editcontroller->queryInterface (IUnitInfo::iid, (void**)&unitInfo) == kResultTrue && unitInfo) { if (unitInfo->getUnitByBus (kEvent, kInput, 0, midiChannel, unitId) == kResultTrue) { int32 unitCount = unitInfo->getUnitCount (); for (int32 i = 0; i < unitCount; i++) { UnitInfo unitInfoStruct {}; if (unitInfo->getUnitInfo (i, unitInfoStruct) == kResultTrue) { if (unitId == unitInfoStruct.id) { programListId = unitInfoStruct.programListId; unitInfo->release (); return programListId != kNoProgramListId; } } } } unitInfo->release (); } return false; } //------------------------------------------------------------------------ - (void)loadPresetList { std::vector programParameters; bool noPresets = true; // for each VST3 parameter int32 parameterCount = _editcontroller->getParameterCount (); for (int32 i = 0; i < parameterCount; i++) { ParameterInfo pi {}; _editcontroller->getParameterInfo (i, pi); // do not register bypass if ((pi.flags & ParameterInfo::kIsBypass) != 0) { continue; } else if ((pi.flags & ParameterInfo::kIsProgramChange) != 0) { // see if parameter is programChange noPresets = false; programParameters.push_back (pi); } } if (noPresets) return; // assign programChanges for (int32 midiChannel = 0; midiChannel < 16; ++midiChannel) { mpeHandlerContext.programChangeParameters[midiChannel] = kNoParamId; UnitID unitId; ProgramListID programListId; if (![self getProgramListAndUnit:midiChannel unitId:unitId programListId:programListId]) continue; for (int32 i = 0; i < (int32)programParameters.size (); i++) { const ParameterInfo& paramInfo = programParameters.at (i); if (paramInfo.unitId == unitId) { mpeHandlerContext.programChangeParameters[midiChannel] = paramInfo.id; mpeHandlerContext.programChangeParameterStepCount[midiChannel] = paramInfo.stepCount; break; } } } auto unitInfoController = U::cast (_editcontroller); factoryPresetsVar = [[NSMutableArray alloc] init]; // let's see if there's a preset list (take the first one) if (!unitInfoController) return; ProgramListInfo programListInfo; if (unitInfoController->getProgramListInfo (0, programListInfo) != kResultTrue) return; factoryProgramChangedID = -1; numPresets = programListInfo.programCount; if (programListInfo.programCount > 0) { UnitID unitId = -1; // find the unit supporting this ProgramList IUnitInfo* unitInfo = NULL; if (_editcontroller->queryInterface (IUnitInfo::iid, (void**)&unitInfo) == kResultTrue && unitInfo) { int32 unitCount = unitInfo->getUnitCount (); for (int32 i = 0; i < unitCount; i++) { UnitInfo unitInfoStruct {}; if (unitInfo->getUnitInfo (i, unitInfoStruct) == kResultTrue) { if (programListInfo.id == unitInfoStruct.programListId) { unitId = unitInfoStruct.id; break; } } } unitInfo->release (); } if (unitId != -1) { // find the associated ProgramChange parameter ID for (int32 i = 0; i < (int32)programParameters.size (); i++) { const ParameterInfo& paramInfo = programParameters.at (i); if (paramInfo.unitId == unitId) { factoryProgramChangedID = paramInfo.id; break; } } if (factoryProgramChangedID != -1) { for (int32 i = 0; i < programListInfo.programCount; i++) { String128 name; if (unitInfoController->getProgramName (programListInfo.id, i, name) != kResultTrue) { name[0] = 0; } AUAudioUnitPreset* preset = [[AUAudioUnitPreset alloc] init]; [preset setName:createCFStringFromString128 (name)]; [preset setNumber:static_cast (i)]; [factoryPresetsVar addObject:preset]; } } } } } //------------------------------------------------------------------------ - (void)syncParameterValues { if (!_editcontroller) return; int32 parameterCount = _editcontroller->getParameterCount (); for (int32 i = 0; i < parameterCount; i++) { ParameterInfo pi; _editcontroller->getParameterInfo (i, pi); if (pi.flags & ParameterInfo::kIsBypass) { bypassParamID = pi.id; } else { ParamValue value = _editcontroller->getParamNormalized (pi.id); // set the AU parameter value AUParameter* parameterToChange = [parameterTreeVar parameterWithAddress:pi.id]; parameterToChange.value = value; } } } //------------------------------------------------------------------------ - (void)setParameter:(int)tag value:(double)value { if (tag == factoryProgramChangedID && numPresets > 0) { // load factory preset here float normalizedValue = (float)value / (float)numPresets; transferParamChanges.addChange (factoryProgramChangedID, normalizedValue, 0); } transferParamChanges.addChange (tag, value, 0); } //------------------------------------------------------------------------ - (void)setControllerParameter:(int)tag value:(double)value { if (_editcontroller && tag != factoryProgramChangedID) _editcontroller->setParamNormalized (tag, value); } //------------------------------------------------------------------------ - (void)beginEdit:(int32_t)tag { AUParameter* parameterToChange = [parameterTreeVar parameterWithAddress:tag]; [parameterToChange setValue:parameterToChange.value originator:parameterObserverToken atHostTime:0 eventType:AUParameterAutomationEventTypeTouch]; } //------------------------------------------------------------------------ - (void)endEdit:(int32_t)tag { AUParameter* parameterToChange = [parameterTreeVar parameterWithAddress:tag]; [parameterToChange setValue:parameterToChange.value originator:parameterObserverToken atHostTime:0 eventType:AUParameterAutomationEventTypeRelease]; } //------------------------------------------------------------------------ - (void)performEdit:(int32_t)tag value:(double)value { AUParameter* parameterToChange = [parameterTreeVar parameterWithAddress:tag]; [parameterToChange setValue:value originator:parameterObserverToken atHostTime:0 eventType:AUParameterAutomationEventTypeValue]; } //------------------------------------------------------------------------ - (void)onTimer { ParamID pid; ParamValue value; int32 sampleOffset; while (outputParamTransfer.getNextChange (pid, value, sampleOffset)) { _editcontroller->setParamNormalized (pid, value); AUParameter* parameterToChange = [parameterTreeVar parameterWithAddress:pid]; parameterToChange.value = value; } if (!isBypassed) return; ProcessData bypassData = processData; bypassData.numSamples = 0; inputParamChanges.clearQueue (); transferParamChanges.transferChangesTo (inputParamChanges); if (inputParamChanges.getParameterCount () > 0) { audioProcessor->process (bypassData); outputParamTransfer.transferChangesFrom (outputParamChanges); outputParamChanges.clearQueue (); } } //------------------------------------------------------------------------ - (BOOL)validateChannelConfig { auto component = U::cast (audioProcessor); int32 inputBusCount = component->getBusCount (kAudio, kInput); int32 outputBusCount = component->getBusCount (kAudio, kOutput); SpeakerArrangement inputs[inputBusCount]; SpeakerArrangement outputs[outputBusCount]; int32 maxBusCount = std::max (inputBusCount, outputBusCount); BOOL didChannelConfigPass = YES; for (int32 i = 0; i < maxBusCount; i++) { int32 inChannelCount = 0; int32 outChannelCount = 0; if (inputBusCount > i) inChannelCount = _outputBusArray[i].format.channelCount; if (outputBusCount > i) outChannelCount = _outputBusArray[i].format.channelCount; didChannelConfigPass = [self validateChannelPair:inChannelCount inChannelsOut:outChannelCount info:channelCapabilitiesArray numChanInfo:([channelCapabilitiesArray count] / 2)]; } for (int32 element = 0; element < inputBusCount; element++) inputs[element] = numChannelsToSpeakerArrangement (_inputBusArray[element].format.channelCount); for (int32 element = 0; element < outputBusCount; element++) outputs[element] = numChannelsToSpeakerArrangement (_outputBusArray[element].format.channelCount); if (audioProcessor->setBusArrangements (inputs, inputBusCount, outputs, outputBusCount) != kResultTrue) didChannelConfigPass = NO; return didChannelConfigPass; } //------------------------------------------------------------------------ - (BOOL)validateChannelPair:(int)inChannelsIn inChannelsOut:(int)inChannelsOut info:(NSArray*)info numChanInfo:(long)numChanInfo { // we've the following cases (some combinations) to test here: /* >0 An explicit number of channels on either side 0 that side (generally input!) has no elements -1 wild card: -1,-1 any num channels as long as same channels on in and out -1,-2 any num channels channels on in and out - special meaning -2+ indicates total num channs AU can handle - elements configurable to any num channels, - element count in scope must be writable */ // now chan layout can contain -1 for either scope (ie. doesn't care) for (unsigned int i = 0; i < numChanInfo; ++i) { int inputValue = (int)[info[(i * 2)] integerValue]; int outputValue = (int)[info[(i * 2) + 1] integerValue]; // less than zero on both sides - check for special attributes if ((inputValue < 0) && (outputValue < 0)) { // these are our wild card matches if (inputValue == -1 && outputValue == -1) { if (inChannelsIn && inChannelsOut) { if (inChannelsOut == inChannelsIn) return true; } else return true; // if one of these is zero, then a -1 means any } else if ((inputValue == -1 && outputValue == -2) || (inputValue == -2 && outputValue == -1)) { return true; } // these are our total num channels matches // element count MUST be writable else { bool outWrite = false; bool inWrite = false; // IsElementCountWritable (kAudioUnitScope_Output, outWrite); // IsElementCountWritable (kAudioUnitScope_Input, inWrite); if (inWrite && outWrite) { if ((inChannelsOut <= abs (outputValue)) && (inChannelsIn <= abs (inputValue))) return true; } } } // special meaning on input, specific num on output else if (inputValue < 0) { if (outputValue == inChannelsOut) { // can do any in channels if (inputValue == -1) { return true; } // total chans on input else { bool inWrite = false; // IsElementCountWritable (kAudioUnitScope_Input, inWrite); if (inWrite && (inChannelsIn <= abs (inputValue))) return true; } } } // special meaning on output, specific num on input else if (outputValue < 0) { if (inputValue == inChannelsIn) { // can do any out channels if (outputValue == -1) { return true; } // total chans on output else { bool outWrite = false; // IsElementCountWritable (kAudioUnitScope_Output, outWrite); if (outWrite && (inChannelsOut <= abs (outputValue))) { return true; } } } } // both chans in struct >= 0 - thus has to explicitly match else if ((inputValue == inChannelsIn) && (outputValue == inChannelsOut)) { return true; } // now check to see if a wild card on the args (inChannelsIn or inChannelsOut chans is zero) // is found // tells us to match just one side of the scopes else if (inChannelsIn == 0) { if (outputValue == inChannelsOut) return true; } else if (inChannelsOut == 0) { if (inputValue == inChannelsIn) return true; } } return false; } //------------------------------------------------------------------------ // MARK: AUAudioUnit Overrides //------------------------------------------------------------------------ - (AUParameterTree*)parameterTree { return parameterTreeVar; } //------------------------------------------------------------------------ - (NSArray*)factoryPresets { return factoryPresetsVar; } //------------------------------------------------------------------------ - (NSArray*)channelCapabilities { return channelCapabilitiesArray; } //------------------------------------------------------------------------ - (AUAudioUnitBusArray*)inputBusses { return _inputBusArray; } //------------------------------------------------------------------------ - (AUAudioUnitBusArray*)outputBusses { return _outputBusArray; } //------------------------------------------------------------------------ - (BOOL)canProcessInPlace { return canProcessInPlaceValue; } //------------------------------------------------------------------------ - (BOOL)isRenderingOffline { return renderingOfflineValue; } //------------------------------------------------------------------------ - (BOOL)supportsMPE { if (_editcontroller) { if (auto nec = U::cast (_editcontroller)) return YES; } return NO; } //------------------------------------------------------------------------ - (void)setRenderingOffline:(BOOL)renderingOffline { renderingOfflineValue = renderingOffline; } //------------------------------------------------------------------------ - (NSTimeInterval)latency { if (audioProcessor) return (audioProcessor->getLatencySamples () / sampleRate); return 0.0; } //------------------------------------------------------------------------ - (NSTimeInterval)tailTime { if (audioProcessor) return (audioProcessor->getTailSamples () / sampleRate); return 0.0; } //------------------------------------------------------------------------ - (BOOL)shouldChangeToFormat:(AVAudioFormat*)format forBus:(AUAudioUnitBus*)bus { // implement ChangeStreamFormat from AUv2 Wrapper here return [super shouldChangeToFormat:format forBus:bus]; } //------------------------------------------------------------------------ - (NSArray*)parametersForOverviewWithCount:(NSInteger)count { NSInteger n = [overviewParams count]; if (count >= n) return overviewParams; NSMutableArray* overviewParamsWithRange = [[NSMutableArray alloc] initWithArray:overviewParams]; NSRange range = NSMakeRange (count, (n - count)); [overviewParamsWithRange removeObjectsInRange:range]; return overviewParamsWithRange; } //------------------------------------------------------------------------ - (AUAudioUnitPreset*)currentPreset { if (currentPreset.number >= 0) // factory preset return [factoryPresetsVar objectAtIndex:currentFactoryPresetIndex]; else // custom preset return currentPreset; } //------------------------------------------------------------------------ - (void)setCurrentPreset:(AUAudioUnitPreset*)presentPreset { if (nil == presentPreset) return; if (executeOnMainThreadSync (^{ [self setCurrentPreset:presentPreset]; })) return; // if it is a factory preset if (presentPreset.number >= 0) { for (AUAudioUnitPreset* factoryPreset in factoryPresetsVar) { if (presentPreset.number == factoryPreset.number) { if (numPresets > 0) { float normalizedValue = (float)presentPreset.number / (float)numPresets; transferParamChanges.addChange (factoryProgramChangedID, normalizedValue, 0); _editcontroller->setParamNormalized (factoryProgramChangedID, normalizedValue); } // set factory preset as current currentPreset = presentPreset; currentFactoryPresetIndex = factoryPreset.number; NSLog (@"currentPreset Factory: %ld, %@\n", (long)currentFactoryPresetIndex, factoryPreset.name); break; } } } else if (nil != presentPreset.name) // set custom preset as current currentPreset = presentPreset; } //------------------------------------------------------------------------ - (NSDictionary*)fullState { // should flush parameters of VST to save correct state (when not in playback mode) __block NSDictionary* mtResult = nil; if (executeOnMainThreadSync (^{ mtResult = [self fullState]; })) { return mtResult; } auto* result = [NSMutableDictionary new]; if (auto* superDict = [super fullState]) [result addEntriesFromDictionary:superDict]; NSMutableData* processorData = [[NSMutableData alloc] init]; NSMutableData* controllerData = [[NSMutableData alloc] init]; if (audioProcessor) { NSMutableDataIBStream stream (processorData); if (FUnknownPtr (audioProcessor)->getState (&stream) != kResultTrue) [processorData setLength:0]; } if (_editcontroller) { NSMutableDataIBStream stream (controllerData); if (_editcontroller->getState (&stream) != kResultTrue) [controllerData setLength:0]; } [result setObject:processorData forKey:@"Processor State"]; [result setObject:controllerData forKey:@"Controller State"]; return result; } //------------------------------------------------------------------------ - (void)setFullState:(NSDictionary*)newFullState { if (newFullState == nullptr) return; if (executeOnMainThreadSync (^{ [self setFullState:newFullState]; })) return; bool fromProject = false; fullStateVar = newFullState; if (!(audioProcessor && _editcontroller)) return; // Note: the bypass state is not part of the AU state bool wasBypassed = false; if (bypassParamID != -1) wasBypassed = _editcontroller->getParamNormalized (bypassParamID) >= 0.5 ? true : false; NSMutableDictionary* modifiedState = [[NSMutableDictionary alloc] init]; [modifiedState addEntriesFromDictionary:newFullState]; NSString* nsPresetKey = [[NSString alloc] initWithUTF8String:kAUPresetDataKey]; [modifiedState removeObjectForKey:nsPresetKey]; NSData* processorData = [modifiedState valueForKey:@"Processor State"]; int processLen = (int)[processorData length]; if (processLen == 0) return; std::u16string projectStr; if (fromProject) projectStr = Vst::StringConvert::convert (Vst::StateType::kProject); if (processorData) { NSDataIBStream stream (processorData); if (fromProject) { stream.getAttributes ()->setString (Vst::PresetAttributes::kStateType, Vst::toTChar (projectStr)); } FUnknownPtr (audioProcessor)->setState (&stream); } NSData* controllerData = [modifiedState valueForKey:@"Controller State"]; if (controllerData) { NSDataIBStream processorStream (processorData); _editcontroller->setComponentState (&processorStream); NSDataIBStream stream (controllerData); if (fromProject) { stream.getAttributes ()->setString (Vst::PresetAttributes::kStateType, Vst::toTChar (projectStr)); } _editcontroller->setState (&stream); [self syncParameterValues]; } if (bypassParamID != -1) { transferParamChanges.addChange (bypassParamID, wasBypassed ? 1 : 0, 0); _editcontroller->setParamNormalized (bypassParamID, wasBypassed ? 1 : 0); } } //------------------------------------------------------------------------ - (BOOL)allocateRenderResourcesAndReturnError:(NSError* __autoreleasing*)outError { __block BOOL result = NO; __block NSError* errorString = nullptr; if (executeOnMainThreadSync ( ^{ result = [self allocateRenderResourcesAndReturnError:&errorString]; })) { if (outError) *outError = errorString; return result; } if (![super allocateRenderResourcesAndReturnError:outError]) return NO; if (![self validateChannelConfig]) { if (outError) { *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:kAudioUnitErr_FormatNotSupported userInfo:nil]; } // Notify superclass that initialization was not successful self.renderResourcesAllocated = NO; return NO; } [self allocateBusBuffers]; [self setupProcessing]; if (self.musicalContextBlock) musicalContext = self.musicalContextBlock; else musicalContext = nil; if (self.transportStateBlock) transportContext = self.transportStateBlock; else transportContext = nil; return YES; } //------------------------------------------------------------------------ - (void)deallocateRenderResources { if (executeOnMainThreadSync (^{ [self deallocateRenderResources]; })) return; musicalContext = nullptr; transportContext = nullptr; for (int i = 0; i > inputBusBuffers.size (); i++) inputBusBuffers.at (i).deallocateRenderResources (); for (int i = 0; i > outputBusBuffers.size (); i++) outputBusBuffers.at (i).deallocateRenderResources (); inputBusBuffers.clear (); outputBusBuffers.clear (); if (audioProcessor) { audioProcessor->setProcessing (false); if (auto component = U::cast (audioProcessor)) component->setActive (false); } [super deallocateRenderResources]; } //------------------------------------------------------------------------ - (AUInternalRenderBlock)internalRenderBlock { return ^AUAudioUnitStatus ( AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* timestamp, AVAudioFrameCount frameCount, NSInteger outputBusNumber, AudioBufferList* outputData, const AURenderEvent* realtimeEventListHead, AURenderPullInputBlock pullInputBlock) { // process events/params for (const AURenderEvent* event = realtimeEventListHead; event != nullptr; event = event->head.next) { switch (event->head.eventType) { case AURenderEventMIDI: case AURenderEventMIDISysEx: { mpeHandler.process (mpeHandlerContext, &event->MIDI, timestamp); break; } case AURenderEventParameter: case AURenderEventParameterRamp: { componentHelper.performEdit ((int)event->parameter.parameterAddress, event->parameter.value); } break; default: break; } } // prepare contexts RenderProcessContext renderProcessContext = RenderProcessContext (); renderProcessContext.musicalContext = musicalContext; renderProcessContext.transportContext = transportContext; renderProcessContext.processContext = &(processContext); renderProcessContext.sampleRate = sampleRate; renderProcessContext.timestamp = timestamp; renderProcessContext.updateProcessContext (renderProcessContext); inputParamChanges.clearQueue (); transferParamChanges.transferChangesTo (inputParamChanges); processData.numSamples = frameCount; if (frameCount > 0) { auto component = U::cast (audioProcessor); int32 inputBusCount = component->getBusCount (kAudio, kInput); int32 outputBusCount = component->getBusCount (kAudio, kOutput); AudioUnitRenderActionFlags pullFlags = 0; // pull input buffer for (int32 i = 0; i < inputBusCount; i++) { UInt32 byteSize = frameCount * sizeof (float); if (pullInputBlock == nullptr || inputBusBuffers.at (i).pullInput (&pullFlags, timestamp, frameCount, i, pullInputBlock) != noErr) { for (int32 channel = 0; channel < inputBusBuffers.at (i).bus.format.channelCount; channel++) memset ( inputBusBuffers.at (i).mutableAudioBufferList->mBuffers[channel].mData, 0, byteSize); } if (actionFlags != nullptr && (*actionFlags & kAudioUnitRenderAction_OutputIsSilence) != 0) { for (int32 channel = 0; channel < inputBusBuffers.at (i).bus.format.channelCount; channel++) memset ( inputBusBuffers.at (i).mutableAudioBufferList->mBuffers[channel].mData, 0, byteSize); } } // prepare output buffer for (int32 i = 0; i < outputBusCount; i++) outputBusBuffers.at (i).prepareOutputBufferList (outputData, frameCount, i == outputBusNumber ? true : false); if (outputData->mBuffers[0].mData == nullptr) { for (UInt32 channel = 0; channel < outputData->mNumberBuffers; channel++) outputData->mBuffers[channel].mData = inputBusBuffers.at (outputBusNumber) .mutableAudioBufferList->mBuffers[channel] .mData; } // copy input buffers for (int32 i = 0; i < inputBusCount; i++) { AVAudioChannelCount channelCount = inputBusBuffers.at (i).bus.format.channelCount; processData.inputs[i].numChannels = int32 (channelCount); for (int32 channel = 0; channel < channelCount; channel++) processData.inputs[i].channelBuffers32[channel] = (Sample32*)inputBusBuffers.at (i) .mutableAudioBufferList->mBuffers[channel] .mData; } // copy output buffers for (int32 i = 0; i < outputBusCount; i++) { AVAudioChannelCount channelCount = outputBusBuffers.at (i).bus.format.channelCount; processData.outputs[i].numChannels = int32 (channelCount); for (int32 channel = 0; channel < channelCount; channel++) { if (i == outputBusNumber) { processData.outputs[i].channelBuffers32[channel] = (Sample32*)outputData->mBuffers[channel].mData; } else { processData.outputs[i].channelBuffers32[channel] = (Sample32*)outputBusBuffers.at (i) .mutableAudioBufferList->mBuffers[channel] .mData; } } } // process audio audioProcessor->process (processData); outputParamTransfer.transferChangesFrom (outputParamChanges); outputParamChanges.clearQueue (); inputEvents.clear (); pullFlags &= ~kAudioUnitRenderAction_OutputIsSilence; } return noErr; }; } @end //------------------------------------------------------------------------ // AUv3WrapperViewController //------------------------------------------------------------------------ #pragma mark - ViewController (implementing AUViewController and AUAudioUnitFactory) @interface AUv3WrapperViewController () { IPtr plugView; IPtr plugFrame; IPtr editController; BOOL isAttached; } @end //------------------------------------------------------------------------ // AUv3WrapperViewController //------------------------------------------------------------------------ @implementation AUv3WrapperViewController //------------------------------------------------------------------------ - (void)loadView { SMTG_IOS_MAC_VIEW* view = [[SMTG_IOS_MAC_VIEW alloc] initWithFrame:CGRectMake (0, 0, 0, 0)]; view.autoresizingMask = SMTG_IOS_MAC_AutoresizingMask_NotSizable; view.translatesAutoresizingMaskIntoConstraints = YES; [self setView:view]; #if SMTG_OS_OSX //------------------------------------------- // workaround bug of Logic/Garageband to not listen to plug-in editor size changes and using the // initial size and loading the view before creating the audio unit. (FB8971597) // // may just be a misconception of AUv3 not defining editor size and resize behaviour at all // // this also means that it is not supported to change the editor size after creation in these // hosts //------------------------------------------- @synchronized (self) { if (self.audioUnit == nil) { auto infoDict = NSBundle.mainBundle.infoDictionary; if (infoDict = infoDict[@"NSExtension"][@"NSExtensionAttributes"][@"AudioComponents"][0]) { id type = infoDict[@"type"]; id subType = infoDict[@"subtype"]; id manu = infoDict[@"manufacturer"]; AudioComponentDescription desc {}; desc.componentType = UTGetOSTypeFromString ((__bridge CFStringRef)type); desc.componentSubType = UTGetOSTypeFromString ((__bridge CFStringRef)subType); desc.componentManufacturer = UTGetOSTypeFromString ((__bridge CFStringRef)manu); self.audioUnit = [[AUv3Wrapper alloc] initWithComponentDescription:desc error:nil]; } } } #endif } //------------------------------------------------------------------------ - (void)setFrame:(CGRect)newSize { if (CGRectEqualToRect (self.view.frame, newSize)) return; self.view.frame = newSize; self.preferredContentSize = newSize.size; if (plugView) { ViewRect viewRect (0, 0, newSize.size.width, newSize.size.height); plugView->onSize (&viewRect); } } //------------------------------------------------------------------------ - (AUv3Wrapper*)getAudioUnit { return _audioUnit; } //------------------------------------------------------------------------ - (void)makePlugView { assert (pthread_main_np () != 0); if (auto ec2 = U::cast (editController)) ec2->setKnobMode (kLinearMode); // create view plugView = owned (editController->createView (ViewType::kEditor)); if (plugView) { if (plugView->isPlatformTypeSupported (SMTG_IOS_MAC_PLATFORMTYPE) == kResultTrue) { plugFrame = owned (new AUPlugFrame ([self] (ViewRect* vr) { auto viewFrame = self.view.frame; CGRect newSize = CGRectMake (viewFrame.origin.x, viewFrame.origin.y, vr->getWidth (), vr->getHeight ()); [self setFrame:newSize]; })); plugView->setFrame (plugFrame); if (plugView->attached ((__bridge void*)self.view, SMTG_IOS_MAC_PLATFORMTYPE) == kResultTrue) { isAttached = TRUE; ViewRect vr; if (plugView->getSize (&vr) == kResultTrue) { int viewWidth = vr.right - vr.left; int viewHeight = vr.bottom - vr.top; CGRect newSize = CGRectMake (0, 0, viewWidth, viewHeight); [self setFrame:newSize]; } } } else { plugView = nullptr; } } } //------------------------------------------------------------------------ - (void)setAudioUnit:(AUv3Wrapper*)audioUnit { if (audioUnit == nil) return; _audioUnit = audioUnit; editController = [_audioUnit editcontroller]; if (pthread_main_np () != 0) [self makePlugView]; else { dispatch_async (dispatch_get_main_queue (), ^{ [self makePlugView]; }); } } //------------------------------------------------------------------------ - (void)dealloc { if (plugView) { // remove plugFrame from plugView if (isAttached) { plugView->setFrame (0); plugView->removed (); } // release plugView plugView = nullptr; // release plugFrame plugFrame = nullptr; // release editController editController = nullptr; } self.audioUnit = nil; } @end qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/PaxHeaders/AUv3Wrapper.h0000644000000000000000000000013215124701711026330 xustar0030 mtime=1767080905.279201476 30 atime=1767080905.279201476 30 ctime=1767080905.279201476 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/AUv3Wrapper.h0000644000175000001440000000317215124701711026323 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : AUv3Wrapper.h // Created by : Steinberg, 07/2017. // Description : VST 3 AUv3Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #import @class AUv3Wrapper; //------------------------------------------------------------------------ @interface AUv3WrapperViewController : AUViewController @property (nonatomic, strong) AUv3Wrapper* audioUnit; @end //------------------------------------------------------------------------ @interface AUv3Wrapper : AUAudioUnit - (void)beginEdit:(int32_t)tag; - (void)endEdit:(int32_t)tag; - (void)performEdit:(int32_t)tag value:(double)value; - (void)syncParameterValues; - (void)updateParameters; - (void)onTimer; - (void)onParamTitlesChanged; - (void)onNoteExpressionChanged; - (void)onLatencyChanged; - (BOOL)enableMPESupport:(BOOL)state; - (BOOL)setMPEInputDeviceMasterChannel:(NSInteger)masterChannel memberBeginChannel:(NSInteger)memberBeginChannel memberEndChannel:(NSInteger)memberEndChannel; @end qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/PaxHeaders/AUv3WrapperFactory.h0000644000000000000000000000013215124701711027660 xustar0030 mtime=1767080905.279210874 30 atime=1767080905.279210874 30 ctime=1767080905.279210874 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/AUv3WrapperFactory.h0000644000175000001440000000154015124701711027650 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : // Created by : Steinberg, 07/2017. // Description : VST 3 AUv3Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #import "AUv3Wrapper.h" @interface AUv3WrapperViewController (AUAudioUnitFactory) @end qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/PaxHeaders/AUv3AudioEngine.h0000644000000000000000000000012715124701711027103 xustar0029 mtime=1767080905.27906427 29 atime=1767080905.27906427 29 ctime=1767080905.27906427 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/AUv3AudioEngine.h0000644000175000001440000000226715124701711027076 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : AUv3AudioEngine.h // Created by : Steinberg, 07/2017. // Description : VST 3 AUv3Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #import @interface AUv3AudioEngine : NSObject @property (assign) AUAudioUnit* currentAudioUnit; - (NSError*)loadAudioFile:(NSURL*)url; - (instancetype)initWithComponentType:(uint32_t)unitComponentType; - (void)loadAudioUnitWithComponentDescription:(AudioComponentDescription)desc completion:(void (^) (void))completionBlock; - (BOOL)startStop; - (void)shutdown; @end qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/PaxHeaders/AUv3AudioEngine.mm0000644000000000000000000000013115124701711027260 xustar0030 mtime=1767080905.279201476 29 atime=1767080905.27906427 30 ctime=1767080905.279201476 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/Shared/AUv3AudioEngine.mm0000644000175000001440000002624315124701711027260 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : AUv3AudioEngine.mm // Created by : Steinberg, 07/2017. // Description : VST 3 AUv3Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "AUv3AudioEngine.h" #import #import #import #import //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ class MidiIO { public: using ReadCallback = std::function; MidiIO (ReadCallback&& callback) : readCallback (std::move (callback)) { activate (); } ~MidiIO () = default; private: bool activate (); bool deactivate (); void onSourceAdded (MIDIObjectRef source); void onSetupChanged (); void disconnectSources (); void onInput (const MIDIPacketList* pktlist); static void readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon); static void notifyProc (const MIDINotification* message, void* refCon); MIDIClientRef client {0}; MIDIPortRef inputPort {0}; MIDIEndpointRef destPort {0}; ReadCallback readCallback; using ConnectionList = std::vector; ConnectionList connectedSources; }; //------------------------------------------------------------------------ bool MidiIO::activate () { if (client) return true; OSStatus err; NSString* name = [[NSBundle mainBundle] bundleIdentifier]; if ((err = MIDIClientCreate ((__bridge CFStringRef)name, notifyProc, this, &client) != noErr)) return false; if ((err = MIDIInputPortCreate (client, CFSTR ("Input"), readProc, this, &inputPort) != noErr)) { MIDIClientDispose (client); client = 0; return false; } name = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleName"]; if ((err = MIDIDestinationCreate (client, (__bridge CFStringRef)name, readProc, this, &destPort) != noErr)) { MIDIPortDispose (inputPort); inputPort = 0; MIDIClientDispose (client); client = 0; return false; } onSetupChanged (); return true; } //------------------------------------------------------------------------ bool MidiIO::deactivate () { if (client == 0) return true; disconnectSources (); auto status = MIDIEndpointDispose (destPort); destPort = 0; status |= MIDIPortDispose (inputPort); inputPort = 0; status |= MIDIClientDispose (client); client = 0; return status == noErr; } //------------------------------------------------------------------------ void MidiIO::onSourceAdded (MIDIObjectRef source) { connectedSources.push_back ((MIDIEndpointRef)source); MIDIPortConnectSource (inputPort, (MIDIEndpointRef)source, NULL); } //------------------------------------------------------------------------ void MidiIO::onSetupChanged () { disconnectSources (); ItemCount numSources = MIDIGetNumberOfSources (); for (ItemCount i = 0; i < numSources; i++) { onSourceAdded (MIDIGetSource (i)); } } //------------------------------------------------------------------------ void MidiIO::disconnectSources () { for (auto source : connectedSources) MIDIPortDisconnectSource (inputPort, source); connectedSources.clear (); } //------------------------------------------------------------------------ void MidiIO::onInput (const MIDIPacketList* pktlist) { if (readCallback) readCallback (pktlist); } //------------------------------------------------------------------------ void MidiIO::readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon) { MidiIO* io = static_cast (readProcRefCon); io->onInput (pktlist); } //------------------------------------------------------------------------ void MidiIO::notifyProc (const MIDINotification* message, void* refCon) { if (message->messageID == kMIDIMsgSetupChanged) { MidiIO* mio = (MidiIO*)refCon; mio->onSetupChanged (); } } using MidiIOPtr = std::unique_ptr; //------------------------------------------------------------------------ } // Vst } // Steinberg //------------------------------------------------------------------------ @implementation AUv3AudioEngine { AVAudioEngine* audioEngine; AVAudioFile* audioFile; AVAudioPlayerNode* playerNode; AVAudioUnit* avAudioUnit; Steinberg::Vst::MidiIOPtr midi; UInt32 componentType; BOOL playing; BOOL isDone; } //------------------------------------------------------------------------ - (instancetype)initWithComponentType:(uint32_t)unitComponentType { self = [super init]; isDone = false; if (self) { audioEngine = [[AVAudioEngine alloc] init]; componentType = unitComponentType; } #if TARGET_OS_IPHONE NSError* error = nil; BOOL success = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error]; if (NO == success) { NSLog (@"Error setting category: %@", [error localizedDescription]); } #endif playing = false; return self; } //------------------------------------------------------------------------ - (void)shutdown { if (playing) [self stopPlaying]; audioEngine = nil; midi.reset (); } //------------------------------------------------------------------------ - (void)onAudioUnitInstantiated:(AVAudioUnit* __nullable)audioUnit error:(NSError* __nullable)error completion:(void (^) (void))completionBlock { if (audioUnit == nil) return; avAudioUnit = audioUnit; _currentAudioUnit = avAudioUnit.AUAudioUnit; [audioEngine attachNode:avAudioUnit]; [audioEngine connect:avAudioUnit to:audioEngine.outputNode format:audioFile.processingFormat]; completionBlock (); } //------------------------------------------------------------------------ - (void)loadAudioUnitWithComponentDescription:(AudioComponentDescription)desc completion:(void (^) (void))completionBlock { [AVAudioUnit instantiateWithComponentDescription:desc options:0 completionHandler:^(AVAudioUnit* __nullable audioUnit, NSError* __nullable error) { [self onAudioUnitInstantiated:audioUnit error:error completion:completionBlock]; }]; if (componentType == kAudioUnitType_MusicDevice) { midi = Steinberg::Vst::MidiIOPtr ( new Steinberg::Vst::MidiIO ([=] (const MIDIPacketList* pktlist) { [self scheduleMIDIPackets:pktlist]; })); } } //------------------------------------------------------------------------ - (NSError*)loadAudioFile:(NSURL*)url { BOOL isPlaying = playing; if (isPlaying) [self startStop]; if (playerNode) { [playerNode stop]; [audioEngine detachNode:playerNode]; } NSError* error = nil; audioFile = [[AVAudioFile alloc] initForReading:url error:&error]; if (error) return error; [audioEngine detachNode:avAudioUnit]; [audioEngine attachNode:avAudioUnit]; [audioEngine connect:avAudioUnit to:audioEngine.outputNode format:audioFile.processingFormat]; playerNode = [[AVAudioPlayerNode alloc] init]; [audioEngine attachNode:playerNode]; [audioEngine connect:playerNode to:avAudioUnit format:audioFile.processingFormat]; if (isPlaying) [self startStop]; return nil; } //------------------------------------------------------------------------ - (BOOL)startStop { playing = !playing; playing ? ([self startPlaying]) : ([self stopPlaying]); return playing; } //------------------------------------------------------------------------ - (void)startPlaying { [self activateSession:true]; NSError* error = nil; if (![audioEngine startAndReturnError:&error]) { NSLog (@"engine failed to start: %@", error); return; } if (playerNode) { [self loopAudioFile]; [playerNode play]; } } //------------------------------------------------------------------------ - (void)stopPlaying { if (playerNode) [playerNode stop]; [audioEngine stop]; [self activateSession:false]; } //------------------------------------------------------------------------ - (void)loopAudioFile { if (playerNode) { [playerNode scheduleFile:audioFile atTime:nil completionHandler:^{ if (playerNode.playing) [self loopAudioFile]; }]; } } //------------------------------------------------------------------------ - (void)scheduleMIDIPackets:(const MIDIPacketList*) pktlist { if (!_currentAudioUnit || _currentAudioUnit.scheduleMIDIEventBlock == nil) return; auto packet = &pktlist->packet[0]; for (auto i = 0u; i < pktlist->numPackets; i++) { _currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, packet->length, packet->data); packet = MIDIPacketNext (packet); } } //------------------------------------------------------------------------ - (void)loopMIDIsequence { UInt8 cbytes[3], *cbytesPtr; cbytesPtr = cbytes; dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ cbytesPtr[0] = 0xB0; cbytesPtr[1] = 123; cbytesPtr[2] = 0; if (_currentAudioUnit.scheduleMIDIEventBlock == nil) return; _currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3, cbytesPtr); usleep (useconds_t (0.1 * 1e6)); float releaseTime = 0.05; usleep (useconds_t (0.1 * 1e6)); int i = 0; @synchronized (self) { while (playing) { if (releaseTime < 10.0) releaseTime = (releaseTime * 1.05) > 10.0 ? (releaseTime * 1.05) : 10.0; cbytesPtr[0] = 0x90; cbytesPtr[1] = UInt8 (60 + i); cbytesPtr[2] = UInt8 (64); // note on _currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3, cbytesPtr); usleep (useconds_t (0.2 * 1e6)); cbytesPtr[0] = 0x80; cbytesPtr[1] = UInt8 (60 + i); cbytesPtr[2] = UInt8 (0); // note off _currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3, cbytesPtr); i += 2; if (i >= 24) { i = -12; } } cbytesPtr[0] = 0xB0; cbytesPtr[1] = 123; cbytesPtr[2] = 0; _currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3, cbytesPtr); isDone = true; } }); } //------------------------------------------------------------------------ - (void)activateSession:(BOOL)active { #if TARGET_OS_IPHONE NSError* error = nil; BOOL success = [[AVAudioSession sharedInstance] setActive:active error:nil]; if (NO == success) { NSLog (@"Error setting category: %@", [error localizedDescription]); } #endif } @end qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/PaxHeaders/AUv3WrappermacOS0000644000000000000000000000013015124701711025615 xustar0029 mtime=1767080905.27906427 30 atime=1767080905.278210878 29 ctime=1767080905.27906427 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/AUv3WrappermacOS/0000755000175000001440000000000015124701711025664 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/AUv3WrappermacOS/PaxHeaders/main.mm0000644000000000000000000000012715124701711027157 xustar0029 mtime=1767080905.27906427 29 atime=1767080905.27906427 29 ctime=1767080905.27906427 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/AUv3WrappermacOS/main.mm0000644000175000001440000000152015124701711027141 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : // Created by : Steinberg, 03/2017. // Description : VST 3 AUv3Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import int main (int argc, const char* argv[]) { return NSApplicationMain (argc, argv); } qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/PaxHeaders/CMakeLists.txt0000644000000000000000000000012715124701711025376 xustar0029 mtime=1767080905.27906427 29 atime=1767080905.27906427 29 ctime=1767080905.27906427 qtractor-1.5.11/src/vst3/public.sdk/source/vst/auv3wrapper/CMakeLists.txt0000644000175000001440000000551715124701711025372 0ustar00rncbcusersinclude(SMTG_AddVST3AuV3) # iOS target if(SMTG_MAC) if(XCODE) set(auv3wrapperlib_sources ${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3Wrapper.mm ${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3Wrapper.h ${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3AudioEngine.mm ${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3AudioEngine.h ${SDK_ROOT}/public.sdk/source/vst/auwrapper/NSDataIBStream.mm ${SDK_ROOT}/public.sdk/source/vst/auwrapper/NSDataIBStream.h ${SDK_ROOT}/public.sdk/source/vst/utility/mpeprocessor.cpp ${SDK_ROOT}/public.sdk/source/vst/utility/mpeprocessor.h ) # -------------------------------------------------------------------------------------------------------- set(target auv3_wrapper_macos) add_library(${target} STATIC ${auv3wrapperlib_sources} ) set_target_properties(${target} PROPERTIES ${SDK_IDE_LIBS_FOLDER} XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES ) target_compile_features(${target} PUBLIC cxx_std_17 ) target_link_libraries(${target} PRIVATE sdk_hosting ) if (SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY) target_compile_definitions(${target} PRIVATE SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY=1) endif() # -------------------------------------------------------------------------------------------------------- if(SMTG_ENABLE_IOS_TARGETS) set(target auv3_wrapper_ios) add_library(${target} STATIC ${auv3wrapperlib_sources} ) set_target_properties(${target} PROPERTIES ${SDK_IDE_LIBS_FOLDER} XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES ) set_target_properties(${target} PROPERTIES LINK_FLAGS "-Wl,-F/Library/Frameworks" ) smtg_target_set_platform_ios(${target}) target_compile_features(${target} PUBLIC cxx_std_17 ) target_link_libraries(${target} PRIVATE sdk_hosting_ios ) if (SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY) target_compile_definitions(${target} PRIVATE SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY=1 ) endif() endif() else() message("* To enable building the AUv3 Wrapper example for iOS you need to set the SMTG_IOS_DEVELOPMENT_TEAM and use the Xcode generator") endif() endif() qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstguieditor.cpp0000644000000000000000000000013215124701711023607 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstguieditor.cpp0000644000175000001440000002506615124701711023610 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstguieditor.cpp // Created by : Steinberg, 04/2005 // Description : VSTGUI Editor // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstguieditor.h" #include "public.sdk/source/main/moduleinit.h" #include "pluginterfaces/base/keycodes.h" #if SMTG_OS_LINUX #include "public.sdk/source/main/pluginfactory.h" #include "public.sdk/source/vst/vstgui_linux_runloop_support.h" #include "pluginterfaces/base/funknownimpl.h" #endif #if SMTG_OS_WINDOWS && SMTG_MODULE_IS_BUNDLE #include "vstgui_win32_bundle_support.h" #endif #define VSTGUI_NEEDS_INIT_LIB ((VSTGUI_VERSION_MAJOR == 4 && VSTGUI_VERSION_MINOR > 9) || (VSTGUI_VERSION_MAJOR > 4)) #if VSTGUI_NEWER_THAN_4_10 #include "vstgui/lib/events.h" #include "vstgui/lib/platform/iplatformframe.h" #endif #include "base/source/fstring.h" #if VSTGUI_NEEDS_INIT_LIB #include "vstgui/lib/vstguiinit.h" static Steinberg::ModuleInitializer InitVSTGUI ([] () { using namespace Steinberg; VSTGUI::init (getPlatformModuleHandle ()); #if SMTG_MODULE_IS_BUNDLE Vst::setupVSTGUIBundleSupport (getPlatformModuleHandle ()); #endif // SMTG_MODULE_IS_BUNDLE #if SMTG_OS_LINUX static auto pluginFactory = Steinberg::owned (GetPluginFactory ()); if (pluginFactory) { if (auto pf = U::cast (pluginFactory)) { pf->addHostContextCallback ([] (auto hostContext) { Steinberg::Linux::setupVSTGUIRunloop (hostContext); }); } } #endif }); static Steinberg::ModuleTerminator TermVSTGUI ([] () { VSTGUI::exit (); }); #else #if SMTG_OS_MACOS namespace VSTGUI { void* gBundleRef = nullptr; } #elif SMTG_OS_WINDOWS void* hInstance = nullptr; // VSTGUI hInstance extern void* moduleHandle; #elif SMTG_OS_LINUX extern void* moduleHandle; namespace VSTGUI { void* soHandle = nullptr; } // VSTGUI #endif // SMTG_OS_MACOS //------------------------------------------------------------------------ static Steinberg::ModuleInitializer InitVSTGUIEditor ([] () { using namespace Steinberg; #if SMTG_OS_MACOS || SMTG_OS_IOS VSTGUI::gBundleRef = getPlatformModuleHandle (); #elif SMTG_OS_WINDOWS hInstance = getPlatformModuleHandle (); #if SMTG_MODULE_IS_BUNDLE Vst::setupVSTGUIBundleSupport (hInstance); #endif // SMTG_MODULE_IS_BUNDLE #elif SMTG_OS_LINUX VSTGUI::soHandle = getPlatformModuleHandle (); #endif }); #endif // VSTGUI_NEEDS_INIT_LIB using namespace VSTGUI; namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ VSTGUIEditor::VSTGUIEditor (void* controller, ViewRect* size) : EditorView (static_cast (controller), size) { // create a timer used for idle update: will call notify method timer = new CVSTGUITimer (dynamic_cast (this)); } //------------------------------------------------------------------------ VSTGUIEditor::~VSTGUIEditor () { if (timer) timer->forget (); } //------------------------------------------------------------------------ void VSTGUIEditor::setIdleRate (int32 millisec) { if (timer) timer->setFireTime (millisec); } //------------------------------------------------------------------------ tresult PLUGIN_API VSTGUIEditor::isPlatformTypeSupported (FIDString type) { #if SMTG_OS_WINDOWS if (strcmp (type, kPlatformTypeHWND) == 0) return kResultTrue; #elif SMTG_OS_MACOS #if TARGET_OS_IPHONE if (strcmp (type, kPlatformTypeUIView) == 0) return kResultTrue; #else if (strcmp (type, kPlatformTypeNSView) == 0) return kResultTrue; #endif // TARGET_OS_IPHONE #elif SMTG_OS_LINUX if (strcmp (type, kPlatformTypeX11EmbedWindowID) == 0) return kResultTrue; if (strcmp (type, kPlatformTypeWaylandSurfaceID) == 0) return kResultTrue; #endif return kInvalidArgument; } //------------------------------------------------------------------------ tresult PLUGIN_API VSTGUIEditor::attached (void* parent, FIDString type) { if (isPlatformTypeSupported (type) != kResultTrue) return kResultFalse; PlatformType platformType = PlatformType::kDefaultNative; #if SMTG_OS_MACOS #if TARGET_OS_IPHONE if (strcmp (type, kPlatformTypeUIView) == 0) platformType = PlatformType::kUIView; #else if (strcmp (type, kPlatformTypeNSView) == 0) platformType = PlatformType::kNSView; #endif // TARGET_OS_IPHONE #endif // SMTG_OS_MACOS #if SMTG_OS_LINUX if (strcmp (type, kPlatformTypeWaylandSurfaceID) == 0) platformType = PlatformType::kWaylandSurfaceID; #endif if (open (parent, platformType) == true) { ViewRect vr (0, 0, (int32)frame->getWidth (), (int32)frame->getHeight ()); setRect (vr); if (plugFrame) plugFrame->resizeView (this, &vr); if (timer) timer->start (); } return EditorView::attached (parent, type); } //------------------------------------------------------------------------ tresult PLUGIN_API VSTGUIEditor::removed () { if (timer) timer->stop (); close (); return EditorView::removed (); } //------------------------------------------------------------------------ tresult PLUGIN_API VSTGUIEditor::onSize (ViewRect* newSize) { if (frame) frame->setSize (newSize->right - newSize->left, newSize->bottom - newSize->top); return EditorView::onSize (newSize); } //------------------------------------------------------------------------ void VSTGUIEditor::beginEdit (VSTGUI_INT32 index) { if (controller) controller->beginEdit (index); } //------------------------------------------------------------------------ void VSTGUIEditor::endEdit (VSTGUI_INT32 index) { if (controller) controller->endEdit (index); } //------------------------------------------------------------------------ VSTGUI_INT32 VSTGUIEditor::getKnobMode () const { switch (EditController::getHostKnobMode ()) { case kRelativCircularMode: return VSTGUI::kRelativCircularMode; case kLinearMode: return VSTGUI::kLinearMode; } return VSTGUI::kCircularMode; } //------------------------------------------------------------------------ CMessageResult VSTGUIEditor::notify (CBaseObject* /*sender*/, const char* message) { if (message == CVSTGUITimer::kMsgTimer) { if (frame) frame->idle (); return kMessageNotified; } return kMessageUnknown; } #if VSTGUI_NEWER_THAN_4_10 //------------------------------------------------------------------------ static KeyboardEvent translateKeyMessage (char16 key, int16 keyMsg, int16 modifiers) { KeyboardEvent event; if (keyMsg >= 0 && keyMsg <= static_cast (VirtualKey::Equals)) event.virt = static_cast (keyMsg); if (key == 0) key = VirtualKeyCodeToChar ((uint8)keyMsg); if (key) event.character = key; if (modifiers) { if (modifiers & kShiftKey) event.modifiers.add (ModifierKey::Shift); if (modifiers & kAlternateKey) event.modifiers.add (ModifierKey::Alt); if (modifiers & kCommandKey) event.modifiers.add (ModifierKey::Control); if (modifiers & kControlKey) event.modifiers.add (ModifierKey::Super); } return event; } #else //------------------------------------------------------------------------ static bool translateKeyMessage (VstKeyCode& keyCode, char16 key, int16 keyMsg, int16 modifiers) { keyCode.character = 0; keyCode.virt = (unsigned char)keyMsg; keyCode.modifier = 0; if (key == 0) key = VirtualKeyCodeToChar ((uint8)keyMsg); if (key) { String keyStr (STR (" ")); keyStr.setChar16 (0, key); keyStr.toMultiByte (kCP_Utf8); if (keyStr.length () == 1) keyCode.character = keyStr.getChar8 (0); } if (modifiers) { if (modifiers & kShiftKey) keyCode.modifier |= MODIFIER_SHIFT; if (modifiers & kAlternateKey) keyCode.modifier |= MODIFIER_ALTERNATE; if (modifiers & kCommandKey) keyCode.modifier |= MODIFIER_CONTROL; if (modifiers & kControlKey) keyCode.modifier |= MODIFIER_COMMAND; } return true; } #endif //------------------------------------------------------------------------ tresult PLUGIN_API VSTGUIEditor::onKeyDown (char16 key, int16 keyMsg, int16 modifiers) { if (frame) { #if VSTGUI_NEWER_THAN_4_10 auto event = translateKeyMessage (key, keyMsg, modifiers); event.type = EventType::KeyDown; frame->dispatchEvent (event); if (event.consumed) return kResultTrue; #else VstKeyCode keyCode = {}; if (translateKeyMessage (keyCode, key, keyMsg, modifiers)) { VSTGUI_INT32 result = frame->onKeyDown (keyCode); if (result == 1) return kResultTrue; } #endif } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API VSTGUIEditor::onKeyUp (char16 key, int16 keyMsg, int16 modifiers) { if (frame) { #if VSTGUI_NEWER_THAN_4_10 auto event = translateKeyMessage (key, keyMsg, modifiers); event.type = EventType::KeyUp; frame->dispatchEvent (event); if (event.consumed) return kResultTrue; #else VstKeyCode keyCode = {}; if (translateKeyMessage (keyCode, key, keyMsg, modifiers)) { VSTGUI_INT32 result = frame->onKeyUp (keyCode); if (result == 1) return kResultTrue; } #endif } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API VSTGUIEditor::onWheel (float distance) { if (frame) { #if VSTGUI_NEWER_THAN_4_10 CPoint where; frame->getCurrentMouseLocation (where); MouseWheelEvent event; event.mousePosition = where; event.deltaY = distance; frame->getPlatformFrame ()->getCurrentModifiers (event.modifiers); frame->dispatchEvent (event); if (event.consumed) return kResultTrue; #else CPoint where; frame->getCurrentMouseLocation (where); if (frame->onWheel (where, kMouseWheelAxisY, distance, frame->getCurrentMouseButtons ())) return kResultTrue; #endif } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API VSTGUIEditor::setFrame (IPlugFrame* frame) { #if 0 if (frame) { if (auto frameIdle = U::cast (frame)) frameIdle->addIdleHandler (this); } else if (plugFrame) { if (auto frameIdle = U::cast (plugFrame)) frameIdle->removeIdleHandler (this); } #endif return EditorView::setFrame (frame); } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/interappaudio0000644000000000000000000000013215124701711023142 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.281761729 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/0000755000175000001440000000000015124701711023207 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/VST3Editor.mm0000644000000000000000000000013215124701711025460 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282210861 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/VST3Editor.mm0000644000175000001440000000621715124701711025456 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/VST3Editor.mm // Created by : Steinberg, 08/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "VST3Editor.h" #import "pluginterfaces/vst/ivsteditcontroller.h" //------------------------------------------------------------------------ @interface VST3EditorViewController : UIViewController //------------------------------------------------------------------------ @end //------------------------------------------------------------------------ @implementation VST3EditorViewController //------------------------------------------------------------------------ - (BOOL)prefersStatusBarHidden { return YES; } - (BOOL)shouldAutorotate { return YES; } - (NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight; } @end namespace Steinberg { namespace Vst { namespace InterAppAudio { //------------------------------------------------------------------------ VST3Editor::VST3Editor () = default; //------------------------------------------------------------------------ VST3Editor::~VST3Editor () { if (plugView) { plugView->release (); } } //------------------------------------------------------------------------ bool VST3Editor::init (const CGRect& frame) { viewController = [VST3EditorViewController new]; viewController.view = [[UIView alloc] initWithFrame:frame]; return true; } //------------------------------------------------------------------------ bool VST3Editor::attach (IEditController* editController) { auto ec2 = U::cast (editController); if (ec2) { ec2->setKnobMode (kLinearMode); } plugView = editController->createView (ViewType::kEditor); if (plugView) { if (plugView->isPlatformTypeSupported (kPlatformTypeUIView) == kResultTrue) { plugView->setFrame (this); if (plugView->attached ((__bridge void*)viewController.view, kPlatformTypeUIView) == kResultTrue) { return true; } } plugView->release (); plugView = nullptr; } return false; } //------------------------------------------------------------------------ tresult PLUGIN_API VST3Editor::resizeView (IPlugView* view, ViewRect* newSize) { if (newSize && plugView && plugView == view) { if (view->onSize (newSize) == kResultTrue) return kResultTrue; return kResultFalse; } return kInvalidArgument; } //------------------------------------------------------------------------ } // InterAppAudio } // Vst } // Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/AudioIO.h0000644000000000000000000000013215124701711024661 xustar0030 mtime=1767080905.281761729 30 atime=1767080905.281761729 30 ctime=1767080905.281761729 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/AudioIO.h0000644000175000001440000001312215124701711024650 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/AudioIO.h // Created by : Steinberg, 08/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once /// \cond ignore #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" #include #include #include #ifndef __OBJC__ struct UIImage; struct NSString; #endif namespace Steinberg { namespace Vst { namespace InterAppAudio { class AudioIO; //------------------------------------------------------------------------ class IMidiProcessor { public: virtual void onMIDIEvent (UInt32 status, UInt32 data1, UInt32 data2, UInt32 sampleOffset, bool withinRealtimeThread) = 0; }; //------------------------------------------------------------------------ class IAudioIOProcessor : public IMidiProcessor { public: virtual void willStartAudio (AudioIO* audioIO) = 0; virtual void didStopAudio (AudioIO* audioIO) = 0; virtual void process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames, AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) = 0; }; //------------------------------------------------------------------------ class AudioIO { public: static AudioIO* instance (); tresult init (OSType type, OSType subType, OSType manufacturer, CFStringRef name); bool switchToHost (); bool sendRemoteControlEvent (AudioUnitRemoteControlEvent event); UIImage* getHostIcon (); tresult start (); tresult stop (); tresult addProcessor (IAudioIOProcessor* processor); tresult removeProcessor (IAudioIOProcessor* processor); // accessors AudioUnit getRemoteIO () const { return remoteIO; } SampleRate getSampleRate () const { return sampleRate; } bool getInterAppAudioConnected () const { return interAppAudioConnected; } // host context information bool getBeatAndTempo (Float64& beat, Float64& tempo); bool getMusicalTimeLocation (UInt32& deltaSampleOffset, Float32& timeSigNumerator, UInt32& timeSigDenominator, Float64& downBeat); bool getTransportState (Boolean& isPlaying, Boolean& isRecording, Boolean& transportStateChanged, Float64& sampleInTimeLine, Boolean& isCycling, Float64& cycleStartBeat, Float64& cycleEndBeat); void setStaticFallbackTempo (Float64 tempo) { staticTempo = tempo; } Float64 getStaticFallbackTempo () const { return staticTempo; } static NSString* kConnectionStateChange; //------------------------------------------------------------------------ protected: AudioIO (); ~AudioIO (); static OSStatus inputCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); static OSStatus renderCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); static void propertyChangeStatic (void* inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement); static void midiEventCallbackStatic (void* inRefCon, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame); OSStatus inputCallback (AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); OSStatus renderCallback (AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); void midiEventCallback (UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame); void remoteIOPropertyChanged (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement); void setAudioSessionActive (bool state); tresult setupRemoteIO (OSType type); tresult setupAUGraph (OSType type); void updateInterAppAudioConnectionState (); AudioUnit remoteIO {nullptr}; AUGraph graph {nullptr}; AudioBufferList* ioBufferList {nullptr}; HostCallbackInfo hostCallback {}; UInt32 maxFrames {4096}; Float64 staticTempo {120.}; SampleRate sampleRate; bool interAppAudioConnected {false}; std::vector audioProcessors; enum InternalState { kUninitialized, kInitialized, kStarted, }; InternalState internalState {kUninitialized}; }; //------------------------------------------------------------------------ } // namespace InterAppAudio } // namespace Vst } // namespace Steinberg /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/MidiIO.mm0000644000000000000000000000013215124701711024664 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/MidiIO.mm0000644000175000001440000001352615124701711024663 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/MidiIO.mm // Created by : Steinberg, 09/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "MidiIO.h" #import namespace Steinberg { namespace Vst { namespace InterAppAudio { //----------------------------------------------------------------------------- MidiIO& MidiIO::instance () { static MidiIO gInstance; return gInstance; } //----------------------------------------------------------------------------- MidiIO::MidiIO () = default; //----------------------------------------------------------------------------- MidiIO::~MidiIO () { setEnabled (false); } //----------------------------------------------------------------------------- void MidiIO::addProcessor (IMidiProcessor* processor) { midiProcessors.push_back (processor); } //----------------------------------------------------------------------------- void MidiIO::removeProcessor (IMidiProcessor* processor) { auto it = std::find (midiProcessors.begin (), midiProcessors.end (), processor); if (it != midiProcessors.end ()) { midiProcessors.erase (it); } } //----------------------------------------------------------------------------- bool MidiIO::isEnabled () const { return client != 0; } //----------------------------------------------------------------------------- bool MidiIO::setEnabled (bool state) { if (state) { if (client) return true; OSStatus err; NSString* name = [[NSBundle mainBundle] bundleIdentifier]; if ((err = MIDIClientCreate ((__bridge CFStringRef)name, notifyProc, this, &client) != noErr)) return false; if ((err = MIDIInputPortCreate (client, CFSTR ("Input"), readProc, this, &inputPort) != noErr)) { MIDIClientDispose (client); client = 0; return false; } name = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleDisplayName"]; if ((err = MIDIDestinationCreate (client, (__bridge CFStringRef)name, readProc, this, &destPort) != noErr)) { MIDIPortDispose (inputPort); inputPort = 0; MIDIClientDispose (client); client = 0; return false; } } else { if (client == 0) return true; disconnectSources (); MIDIEndpointDispose (destPort); destPort = 0; MIDIPortDispose (inputPort); inputPort = 0; MIDIClientDispose (client); client = 0; } return true; } //----------------------------------------------------------------------------- void MidiIO::setMidiNetworkEnabled (bool state) { if (inputPort && isMidiNetworkEnabled () != state) { if (!state) { MIDIPortDisconnectSource (inputPort, [MIDINetworkSession defaultSession].sourceEndpoint); } [MIDINetworkSession defaultSession].enabled = state; if (state) { MIDIPortConnectSource (inputPort, [MIDINetworkSession defaultSession].sourceEndpoint, 0); } } } //----------------------------------------------------------------------------- bool MidiIO::isMidiNetworkEnabled () const { return [MIDINetworkSession defaultSession].isEnabled; } //----------------------------------------------------------------------------- void MidiIO::setMidiNetworkPolicy (MIDINetworkConnectionPolicy policy) { [MIDINetworkSession defaultSession].connectionPolicy = policy; } //----------------------------------------------------------------------------- MIDINetworkConnectionPolicy MidiIO::getMidiNetworkPolicy () const { return [MIDINetworkSession defaultSession].connectionPolicy; } //----------------------------------------------------------------------------- void MidiIO::onInput (const MIDIPacketList* pktlist) { const MIDIPacket* packet = &pktlist->packet[0]; for (UInt32 i = 0; i < pktlist->numPackets; i++) { for (auto processor : midiProcessors) { processor->onMIDIEvent (packet->data[0], packet->data[1], packet->data[2], 0, false); } packet = MIDIPacketNext (packet); } } //----------------------------------------------------------------------------- void MidiIO::onSourceAdded (MIDIObjectRef source) { connectedSources.push_back ((MIDIEndpointRef)source); MIDIPortConnectSource (inputPort, (MIDIEndpointRef)source, NULL); } //----------------------------------------------------------------------------- void MidiIO::disconnectSources () { for (auto source : connectedSources) MIDIPortDisconnectSource (inputPort, source); connectedSources.clear (); } //----------------------------------------------------------------------------- void MidiIO::onSetupChanged () { disconnectSources (); ItemCount numSources = MIDIGetNumberOfSources (); for (ItemCount i = 0; i < numSources; i++) { onSourceAdded (MIDIGetSource (i)); } } //----------------------------------------------------------------------------- void MidiIO::readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon) { MidiIO* io = static_cast (readProcRefCon); io->onInput (pktlist); } //----------------------------------------------------------------------------- void MidiIO::notifyProc (const MIDINotification* message, void* refCon) { if (message->messageID == kMIDIMsgSetupChanged) { MidiIO* mio = (MidiIO*)refCon; mio->onSetupChanged (); } } } } } // namespaces qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/PresetManager.h0000644000000000000000000000013215124701711026125 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PresetManager.h0000644000175000001440000000426415124701711026123 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/PresetManager.h // Created by : Steinberg, 10/2013 // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "VST3Plugin.h" #include "base/source/fstring.h" #include "pluginterfaces/vst/ivstinterappaudio.h" #if __OBJC__ @class NSArray, PresetBrowserViewController, PresetSaveViewController; #else struct NSArray; struct PresetBrowserViewController; struct PresetSaveViewController; #endif namespace Steinberg { namespace Vst { namespace InterAppAudio { class PresetManager : public FObject, public IInterAppAudioPresetManager { public: PresetManager (VST3Plugin* plugin, const TUID& cid); tresult PLUGIN_API runLoadPresetBrowser () override; tresult PLUGIN_API runSavePresetBrowser () override; tresult PLUGIN_API loadNextPreset () override; tresult PLUGIN_API loadPreviousPreset () override; DEFINE_INTERFACES DEF_INTERFACE (IInterAppAudioPresetManager) END_DEFINE_INTERFACES (FObject) REFCOUNT_METHODS (FObject) private: enum PresetPathType { kFactory, kUser }; NSArray* getPresetPaths (PresetPathType type); tresult loadPreset (bool next); tresult loadPreset (const char* path); void savePreset (const char* path); VST3Plugin* plugin; PresetBrowserViewController* visiblePresetBrowserViewController; PresetSaveViewController* visibleSavePresetViewController; FUID cid; String lastPreset; }; //------------------------------------------------------------------------ } // namespace InterAppAudio } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/AudioIO.mm0000644000000000000000000000013215124701711025043 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.281761729 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/AudioIO.mm0000644000175000001440000004461015124701711025040 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/AudioIO.mm // Created by : Steinberg, 08/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "AudioIO.h" #import "MidiIO.h" #import "pluginterfaces/base/fstrdefs.h" #import #import #import #define FORCE_INLINE __attribute__ ((always_inline)) namespace Steinberg { namespace Vst { namespace InterAppAudio { //------------------------------------------------------------------------ static AudioBufferList* createBuffers (uint32 numChannels, uint32 maxFrames, uint32 frameSize) { AudioBufferList* result = (AudioBufferList*)malloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * numChannels); result->mNumberBuffers = numChannels; for (int32 i = 0; i < numChannels; i++) { result->mBuffers[i].mDataByteSize = maxFrames * sizeof (float); result->mBuffers[i].mData = calloc (1, result->mBuffers[i].mDataByteSize); result->mBuffers[i].mNumberChannels = 1; } return result; } //------------------------------------------------------------------------ static void freeAudioBufferList (AudioBufferList* audioBufferList) { for (uint32 i = 0; i < audioBufferList->mNumberBuffers; i++) { free (audioBufferList->mBuffers[i].mData); } free (audioBufferList); } //------------------------------------------------------------------------ NSString* AudioIO::kConnectionStateChange = @"AudioIO::kConnectionStateChange"; //------------------------------------------------------------------------ AudioIO::AudioIO () { sampleRate = [[AVAudioSession sharedInstance] sampleRate]; MidiIO::instance (); } //------------------------------------------------------------------------ AudioIO::~AudioIO () { if (ioBufferList) freeAudioBufferList (ioBufferList); } //------------------------------------------------------------------------ AudioIO* AudioIO::instance () { static AudioIO gInstance; return &gInstance; } //------------------------------------------------------------------------ tresult AudioIO::setupRemoteIO (OSType type) { if (remoteIO != nullptr) { AudioStreamBasicDescription streamFormat = {}; streamFormat.mChannelsPerFrame = 2; streamFormat.mSampleRate = sampleRate; streamFormat.mFormatID = kAudioFormatLinearPCM; streamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; streamFormat.mBytesPerFrame = streamFormat.mBytesPerPacket = sizeof (Float32); streamFormat.mBitsPerChannel = 32; streamFormat.mFramesPerPacket = 1; OSStatus status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &streamFormat, sizeof (streamFormat)); if (status != noErr) return kInternalError; status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof (streamFormat)); if (status != noErr) return kInternalError; status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, &maxFrames, sizeof (maxFrames)); if (status != noErr) return kInternalError; bool needInput = (type == kAudioUnitType_RemoteGenerator || type == kAudioUnitType_RemoteInstrument) == false; UInt32 flag = 1; if (needInput) { // enable IO Input status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flag, sizeof (flag)); if (status != noErr) return kInternalError; } // enable IO Output status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &flag, sizeof (flag)); if (status != noErr) return kInternalError; AURenderCallbackStruct renderCallback = {}; if (needInput) { renderCallback.inputProc = inputCallbackStatic; renderCallback.inputProcRefCon = this; status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 1, &renderCallback, sizeof (renderCallback)); if (status != noErr) return kInternalError; } renderCallback.inputProc = renderCallbackStatic; renderCallback.inputProcRefCon = this; status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &renderCallback, sizeof (renderCallback)); if (status != noErr) return kInternalError; if (type == kAudioUnitType_RemoteInstrument || type == kAudioUnitType_RemoteMusicEffect) { AudioOutputUnitMIDICallbacks callBackStruct = {}; callBackStruct.userData = this; callBackStruct.MIDIEventProc = midiEventCallbackStatic; status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_MIDICallbacks, kAudioUnitScope_Global, 0, &callBackStruct, sizeof (callBackStruct)); if (status != noErr) { NSLog (@"Setting MIDICallback on OutputUnit failed"); } } if (ioBufferList) freeAudioBufferList (ioBufferList); ioBufferList = createBuffers (streamFormat.mChannelsPerFrame, maxFrames, streamFormat.mBytesPerFrame); if (ioBufferList == nullptr) return kOutOfMemory; status = AudioUnitAddPropertyListener (remoteIO, kAudioUnitProperty_IsInterAppConnected, propertyChangeStatic, this); status = AudioUnitAddPropertyListener ( remoteIO, kAudioOutputUnitProperty_HostTransportState, propertyChangeStatic, this); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult AudioIO::setupAUGraph (OSType type) { if (graph == nullptr) { OSStatus status = NewAUGraph (&graph); if (status != noErr) return kInternalError; AudioComponentDescription iOUnitDescription; iOUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple; iOUnitDescription.componentFlags = 0; iOUnitDescription.componentFlagsMask = 0; iOUnitDescription.componentType = kAudioUnitType_Output; iOUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO; AUNode remoteIONode; status = AUGraphAddNode (graph, &iOUnitDescription, &remoteIONode); if (status != noErr) return kInternalError; status = AUGraphOpen (graph); if (status != noErr) return kInternalError; status = AUGraphNodeInfo (graph, remoteIONode, nullptr, &remoteIO); if (status != noErr) return kInternalError; return setupRemoteIO (type); } return kResultFalse; } //------------------------------------------------------------------------ void AudioIO::updateInterAppAudioConnectionState () { if (remoteIO) { UInt32 connected; UInt32 dataSize = sizeof (connected); OSStatus status = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_IsInterAppConnected, kAudioUnitScope_Global, 0, &connected, &dataSize); if (status == noErr) { if (interAppAudioConnected != connected) { if (connected) { UInt32 size = sizeof (HostCallbackInfo); status = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, 0, &hostCallback, &size); } else { memset (&hostCallback, 0, sizeof (HostCallbackInfo)); } interAppAudioConnected = connected > 0 ? true : false; [[NSNotificationCenter defaultCenter] postNotificationName:kConnectionStateChange object:nil]; } } } } //------------------------------------------------------------------------ tresult AudioIO::init (OSType type, OSType subType, OSType manufacturer, CFStringRef name) { tresult result = setupAUGraph (type); if (result != kResultTrue) return result; AudioComponentDescription desc = {type, subType, manufacturer, 0, 0}; OSStatus status = AudioOutputUnitPublish (&desc, name, 0, remoteIO); if (status != noErr) { NSLog (@"AudioOutputUnitPublish failed with status:%d", (int)status); } internalState = kInitialized; return result; } //------------------------------------------------------------------------ void AudioIO::setAudioSessionActive (bool state) { NSError* error; AVAudioSession* session = [AVAudioSession sharedInstance]; [session setPreferredSampleRate:sampleRate error:&error]; [session setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error]; [session setActive:(state ? YES : NO)error:&error]; } //------------------------------------------------------------------------ tresult AudioIO::start () { if (internalState == kInitialized) { bool appIsActive = [UIApplication sharedApplication].applicationState == UIApplicationStateActive; if (!(appIsActive || interAppAudioConnected)) { return kResultFalse; } setAudioSessionActive (true); Boolean graphInitialized = true; OSStatus status = AUGraphIsInitialized (graph, &graphInitialized); if (status != noErr) return kInternalError; if (graphInitialized == false) { status = AUGraphInitialize (graph); if (status != noErr) return kInternalError; updateInterAppAudioConnectionState (); } for (auto processor : audioProcessors) { processor->willStartAudio (this); } status = AUGraphStart (graph); if (status == noErr) { internalState = kStarted; updateInterAppAudioConnectionState (); return kResultTrue; } return kInternalError; } return kResultFalse; } //------------------------------------------------------------------------ tresult AudioIO::stop () { if (internalState == kStarted) { if (AUGraphStop (graph) == noErr) { for (auto processor : audioProcessors) { processor->didStopAudio (this); } internalState = kInitialized; if (interAppAudioConnected == false) setAudioSessionActive (false); return kResultTrue; } } return kResultFalse; } //------------------------------------------------------------------------ tresult AudioIO::addProcessor (IAudioIOProcessor* processor) { if (internalState == kInitialized) { audioProcessors.push_back (processor); MidiIO::instance ().addProcessor (processor); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult AudioIO::removeProcessor (IAudioIOProcessor* processor) { if (internalState == kInitialized) { auto it = std::find (audioProcessors.begin (), audioProcessors.end (), processor); if (it != audioProcessors.end ()) { audioProcessors.erase (it); MidiIO::instance ().removeProcessor (processor); return kResultTrue; } } return kResultFalse; } //------------------------------------------------------------------------ bool AudioIO::switchToHost () { if (remoteIO && interAppAudioConnected) { CFURLRef instrumentUrl; UInt32 dataSize = sizeof (instrumentUrl); OSStatus result = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_PeerURL, kAudioUnitScope_Global, 0, &instrumentUrl, &dataSize); if (result == noErr) { [[UIApplication sharedApplication] openURL:(__bridge NSURL*)instrumentUrl]; return true; } } return false; } //------------------------------------------------------------------------ bool AudioIO::sendRemoteControlEvent (AudioUnitRemoteControlEvent event) { if (remoteIO && interAppAudioConnected) { UInt32 controlEvent = event; UInt32 dataSize = sizeof (controlEvent); OSStatus status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_RemoteControlToHost, kAudioUnitScope_Global, 0, &controlEvent, dataSize); return status == noErr; } return false; } //------------------------------------------------------------------------ UIImage* AudioIO::getHostIcon () { if (remoteIO && interAppAudioConnected) { return AudioOutputUnitGetHostIcon (remoteIO, 128); } return nil; } //------------------------------------------------------------------------ bool AudioIO::getBeatAndTempo (Float64& beat, Float64& tempo) { if (hostCallback.beatAndTempoProc) { if (hostCallback.beatAndTempoProc (hostCallback.hostUserData, &beat, &tempo) == noErr) return true; } tempo = staticTempo; beat = 0; return true; } //------------------------------------------------------------------------ bool AudioIO::getMusicalTimeLocation (UInt32& deltaSampleOffset, Float32& timeSigNumerator, UInt32& timeSigDenominator, Float64& downBeat) { if (hostCallback.musicalTimeLocationProc) { if (hostCallback.musicalTimeLocationProc (hostCallback.hostUserData, &deltaSampleOffset, &timeSigNumerator, &timeSigDenominator, &downBeat) == noErr) return true; } return false; } //------------------------------------------------------------------------ bool AudioIO::getTransportState (Boolean& isPlaying, Boolean& isRecording, Boolean& transportStateChanged, Float64& sampleInTimeLine, Boolean& isCycling, Float64& cycleStartBeat, Float64& cycleEndBeat) { if (hostCallback.transportStateProc2) { if (hostCallback.transportStateProc2 (hostCallback.hostUserData, &isPlaying, &isRecording, &transportStateChanged, &sampleInTimeLine, &isCycling, &cycleStartBeat, &cycleEndBeat) == noErr) return true; } return false; } //------------------------------------------------------------------------ FORCE_INLINE void AudioIO::remoteIOPropertyChanged (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement) { if (inID == kAudioUnitProperty_IsInterAppConnected) { bool wasConnected = interAppAudioConnected; updateInterAppAudioConnectionState (); if (wasConnected != interAppAudioConnected) { stop (); start (); } } } //------------------------------------------------------------------------ FORCE_INLINE OSStatus AudioIO::renderCallback (AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { if (ioData->mNumberBuffers == ioBufferList->mNumberBuffers) { for (uint32 i = 0; i < ioData->mNumberBuffers; i++) { memcpy (ioData->mBuffers[i].mData, ioBufferList->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize); } bool outputIsSilence = ioActionFlags ? *ioActionFlags & kAudioUnitRenderAction_OutputIsSilence : false; for (auto processor : audioProcessors) { outputIsSilence = false; processor->process (inTimeStamp, inBusNumber, inNumberFrames, ioData, outputIsSilence, this); } if (ioActionFlags) { *ioActionFlags = outputIsSilence ? kAudioUnitRenderAction_OutputIsSilence : 0; } } return noErr; } //------------------------------------------------------------------------ FORCE_INLINE OSStatus AudioIO::inputCallback (AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { OSStatus status = AudioUnitRender (remoteIO, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioBufferList); return status; } //------------------------------------------------------------------------ FORCE_INLINE void AudioIO::midiEventCallback (UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame) { for (auto processor : audioProcessors) { processor->onMIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame, true); } } //------------------------------------------------------------------------ OSStatus AudioIO::inputCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { AudioIO* io = (AudioIO*)inRefCon; return io->inputCallback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); } //------------------------------------------------------------------------ OSStatus AudioIO::renderCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { AudioIO* io = (AudioIO*)inRefCon; return io->renderCallback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); } //------------------------------------------------------------------------ void AudioIO::propertyChangeStatic (void* inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement) { AudioIO* audioIO = (AudioIO*)inRefCon; audioIO->remoteIOPropertyChanged (inID, inScope, inElement); } //------------------------------------------------------------------------ void AudioIO::midiEventCallbackStatic (void* inRefCon, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame) { AudioIO* audioIO = (AudioIO*)inRefCon; audioIO->midiEventCallback (inStatus, inData1, inData2, inOffsetSampleFrame); } } } } qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/PresetBrowserViewController.0000644000000000000000000000013215124701711030725 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PresetBrowserViewController.h0000644000175000001440000000261015124701711031064 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/PresetBrowserViewController.h // Created by : Steinberg, 09/2013 // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once /// \cond ignore #ifdef __OBJC__ #import #import //----------------------------------------------------------------------------- @interface PresetBrowserViewController : UIViewController //----------------------------------------------------------------------------- - (id)initWithCallback:(std::function)callback; - (void)setFactoryPresets:(NSArray*)factoryPresets userPresets:(NSArray*)userPresets; @end #endif // __OBJC__ /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/PresetBrowserView.xib0000644000000000000000000000013215124701711027364 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PresetBrowserView.xib0000644000175000001440000001546715124701711027371 0ustar00rncbcusers qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/VST3Editor.h0000644000000000000000000000013215124701711025276 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282210861 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/VST3Editor.h0000644000175000001440000000364615124701711025277 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/VST3Editor.h // Created by : Steinberg, 08/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once /// \cond ignore #import "base/source/fobject.h" #import "pluginterfaces/gui/iplugview.h" #import namespace Steinberg { namespace Vst { class IEditController; namespace InterAppAudio { //------------------------------------------------------------------------ class VST3Editor : public FObject, public IPlugFrame { public: //------------------------------------------------------------------------ VST3Editor (); virtual ~VST3Editor (); bool init (const CGRect& frame); bool attach (IEditController* editController); UIViewController* getViewController () const { return viewController; } OBJ_METHODS (VST3Editor, FObject) REFCOUNT_METHODS (FObject) DEFINE_INTERFACES DEF_INTERFACE (IPlugFrame) END_DEFINE_INTERFACES (FObject) protected: // IPlugFrame tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) override; IPlugView* plugView {nullptr}; UIViewController* viewController {nullptr}; }; //------------------------------------------------------------------------ } // namespace InterAppAudio } // namespace Vst } // namespace Steinberg /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/PresetSaveViewController.mm0000644000000000000000000000013215124701711030532 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282031469 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PresetSaveViewController.mm0000644000175000001440000001243315124701711030525 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/PresetSaveViewController.mm // Created by : Steinberg, 09/2013 // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "PresetSaveViewController.h" #import "pluginterfaces/base/funknown.h" //------------------------------------------------------------------------ @interface PresetSaveViewController () //------------------------------------------------------------------------ { IBOutlet UIView* containerView; IBOutlet UITextField* presetName; std::function callback; Steinberg::FUID uid; } @end //------------------------------------------------------------------------ @implementation PresetSaveViewController //------------------------------------------------------------------------ //------------------------------------------------------------------------ - (id)initWithCallback:(std::function)_callback { self = [super initWithNibName:@"PresetSaveView" bundle:nil]; if (self) { callback = _callback; self.modalPresentationStyle = UIModalPresentationOverCurrentContext; self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; UIViewController* rootViewController = [[UIApplication sharedApplication].windows[0] rootViewController]; [rootViewController presentViewController:self animated:YES completion:^{ [self showKeyboard]; }]; } return self; } //------------------------------------------------------------------------ - (void)viewDidLoad { [super viewDidLoad]; containerView.layer.shadowOpacity = 0.5; containerView.layer.shadowOffset = CGSizeMake (5, 5); containerView.layer.shadowRadius = 5; } //------------------------------------------------------------------------ - (void)showKeyboard { [presetName becomeFirstResponder]; } //------------------------------------------------------------------------ - (void)removeSelf { [self dismissViewControllerAnimated:YES completion:^{}]; } //------------------------------------------------------------------------ - (NSURL*)presetURL { NSFileManager* fs = [NSFileManager defaultManager]; NSURL* documentsUrl = [fs URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:Nil create:YES error:NULL]; if (documentsUrl) { NSURL* presetPath = [[documentsUrl URLByAppendingPathComponent:presetName.text] URLByAppendingPathExtension:@"vstpreset"]; return presetPath; } return nil; } //------------------------------------------------------------------------ - (BOOL)textFieldShouldReturn:(UITextField*)textField { if ([textField.text length] > 0) { [self save:textField]; return YES; } return NO; } //------------------------------------------------------------------------ - (IBAction)save:(id)sender { if (callback) { NSURL* presetPath = [self presetURL]; NSFileManager* fs = [NSFileManager defaultManager]; if ([fs fileExistsAtPath:[presetPath path]]) { // alert for overwrite auto alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString ( @"A Preset with this name already exists", "Alert title") message:NSLocalizedString (@"Save it anyway ?", "Alert message") preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString (@"Save", "Alert Save Button") style:UIAlertActionStyleDefault handler:^(UIAlertAction* _Nonnull action) { callback ([[[self presetURL] path] UTF8String]); [self removeSelf]; }]]; [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString (@"Cancel", "Alert Cancel Button") style:UIAlertActionStyleCancel handler:^(UIAlertAction* _Nonnull action) {}]]; [self presentViewController:alertController animated:YES completion:nil]; return; } callback ([[presetPath path] UTF8String]); } [self removeSelf]; } //------------------------------------------------------------------------ - (IBAction)cancel:(id)sender { if (callback) { callback (nullptr); } [self removeSelf]; } //------------------------------------------------------------------------ - (BOOL)prefersStatusBarHidden { return YES; } @end qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/VST3Plugin.h0000644000000000000000000000013215124701711025306 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282210861 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/VST3Plugin.h0000644000175000001440000001035715124701711025304 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/VST3Plugin.h // Created by : Steinberg, 08/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once /// \cond ignore #import "AudioIO.h" #import "public.sdk/source/vst/hosting/eventlist.h" #import "public.sdk/source/vst/hosting/parameterchanges.h" #import "public.sdk/source/vst/hosting/processdata.h" #import "public.sdk/source/vst/utility/ringbuffer.h" #import "base/source/fobject.h" #import "base/source/timer.h" #import "pluginterfaces/vst/ivstaudioprocessor.h" #import "pluginterfaces/vst/ivsteditcontroller.h" #import "pluginterfaces/vst/ivstprocesscontext.h" #import #import #ifndef __OBJC__ struct NSData; #endif namespace Steinberg { namespace Vst { namespace InterAppAudio { static const int32 kMaxUIEvents = 100; //------------------------------------------------------------------------ class VST3Plugin : public FObject, public IComponentHandler, public IAudioIOProcessor, public ITimerCallback { public: //------------------------------------------------------------------------ VST3Plugin (); virtual ~VST3Plugin (); bool init (); IEditController* getEditController () const { return editController; } IAudioProcessor* getAudioProcessor () const { return processor; } tresult scheduleEventFromUI (Event& event); NSData* getProcessorState (); bool setProcessorState (NSData* data); NSData* getControllerState (); bool setControllerState (NSData* data); OBJ_METHODS (VST3Plugin, FObject) REFCOUNT_METHODS (FObject) DEFINE_INTERFACES DEF_INTERFACE (IComponentHandler) END_DEFINE_INTERFACES (FObject) protected: typedef std::map NoteIDPitchMap; typedef uint32 ChannelAndCtrlNumber; typedef std::map MIDIControllerToParamIDMap; void createProcessorAndController (); void updateProcessContext (AudioIO* audioIO); MIDIControllerToParamIDMap createMIDIControllerToParamIDMap (); // IComponentHandler tresult PLUGIN_API beginEdit (ParamID id) override; tresult PLUGIN_API performEdit (ParamID id, ParamValue valueNormalized) override; tresult PLUGIN_API endEdit (ParamID id) override; tresult PLUGIN_API restartComponent (int32 flags) override; // IAudioIOProcessor void willStartAudio (AudioIO* audioIO) override; void didStopAudio (AudioIO* audioIO) override; void onMIDIEvent (UInt32 status, UInt32 data1, UInt32 data2, UInt32 sampleOffset, bool withinRealtimeThread) override; void process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames, AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) override; // ITimerCallback void onTimer (Timer* timer) override; IAudioProcessor* processor {nullptr}; IEditController* editController {nullptr}; Timer* timer {nullptr}; HostProcessData processData; ProcessContext processContext; ParameterChangeTransfer inputParamChangeTransfer; ParameterChangeTransfer outputParamChangeTransfer; ParameterChanges inputParamChanges; ParameterChanges outputParamChanges; EventList inputEvents; NoteIDPitchMap noteIDPitchMap; std::atomic lastNodeID {0}; bool processing {false}; MIDIControllerToParamIDMap midiControllerToParamIDMap; OneReaderOneWriter::RingBuffer uiScheduledEvents; static ChannelAndCtrlNumber channelAndCtrlNumber (uint16 channel, CtrlNumber ctrler) { return (channel << 16) + ctrler; } }; //------------------------------------------------------------------------ } // namespace InterAppAudio } // namespace Vst } // namespace Steinberg /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/VSTInterAppAudioAppDelegateB0000644000000000000000000000013215124701711030441 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282210861 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.h0000644000175000001440000000400015124701711031342 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.h // Created by : Steinberg, 08/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import //------------------------------------------------------------------------ /** Base UIApplicationDelegate class. * This class provides the base handling of the audio engine, plug-in and plug-in editor\n * You should subclass it for customization\n * Make sure to call the methods of this class if you override one in your subclass ! */ //------------------------------------------------------------------------ @interface VSTInterAppAudioAppDelegateBase : UIResponder //------------------------------------------------------------------------ @property (strong, nonatomic) UIWindow* window; - (BOOL)application:(UIApplication*)application willFinishLaunchingWithOptions:(NSDictionary*)launchOptions; - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions; - (BOOL)application:(UIApplication*)application shouldSaveApplicationState:(NSCoder*)coder; - (BOOL)application:(UIApplication*)application shouldRestoreApplicationState:(NSCoder*)coder; - (void)applicationDidBecomeActive:(UIApplication*)application; - (void)applicationWillResignActive:(UIApplication*)application; @end qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/PresetSaveViewController.h0000644000000000000000000000013215124701711030350 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PresetSaveViewController.h0000644000175000001440000000221015124701711030333 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/PresetSaveViewController.h // Created by : Steinberg, 09/2013 // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once /// \cond ignore #ifdef __OBJC__ #import #import @interface PresetSaveViewController : UIViewController - (id)initWithCallback:(std::function)callback; @end #endif //__OBJC__ /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/MidiIO.h0000644000000000000000000000013215124701711024502 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/MidiIO.h0000644000175000001440000000450415124701711024475 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/MidiIO.h // Created by : Steinberg, 09/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "AudioIO.h" #include #include namespace Steinberg { namespace Vst { namespace InterAppAudio { //----------------------------------------------------------------------------- class MidiIO { public: static MidiIO& instance (); bool setEnabled (bool state); bool isEnabled () const; // MIDI Network is experimental, do not use yet void setMidiNetworkEnabled (bool state); bool isMidiNetworkEnabled () const; void setMidiNetworkPolicy (MIDINetworkConnectionPolicy policy); MIDINetworkConnectionPolicy getMidiNetworkPolicy () const; void addProcessor (IMidiProcessor* processor); void removeProcessor (IMidiProcessor* processor); //----------------------------------------------------------------------------- private: MidiIO (); ~MidiIO (); void onInput (const MIDIPacketList* pktlist); void onSourceAdded (MIDIObjectRef source); void onSetupChanged (); void disconnectSources (); MIDIClientRef client {0}; MIDIPortRef inputPort {0}; MIDIEndpointRef destPort {0}; using MidiProcessors = std::vector; MidiProcessors midiProcessors; using ConnectionList = std::vector; ConnectionList connectedSources; static void readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon); static void notifyProc (const MIDINotification* message, void* refCon); }; //------------------------------------------------------------------------ } // namespace InterAppAudio } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/SettingsView.xib0000644000000000000000000000013215124701711026356 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282210861 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/SettingsView.xib0000644000175000001440000001707015124701711026353 0ustar00rncbcusers qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/LaunchScreen.storyboard0000644000000000000000000000013215124701711027703 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/LaunchScreen.storyboard0000644000175000001440000000316315124701711027676 0ustar00rncbcusers qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/VSTInterAppAudioAppDelegateB0000644000000000000000000000013215124701711030441 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282210861 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.mm0000644000175000001440000001421315124701711031533 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.mm // Created by : Steinberg, 08/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "VSTInterAppAudioAppDelegateBase.h" #import "public.sdk/source/vst/interappaudio/AudioIO.h" #import "public.sdk/source/vst/interappaudio/HostApp.h" #import "public.sdk/source/vst/interappaudio/MidiIO.h" #import "public.sdk/source/vst/interappaudio/VST3Editor.h" #import "public.sdk/source/vst/interappaudio/VST3Plugin.h" using namespace Steinberg::Vst::InterAppAudio; //------------------------------------------------------------------------ static OSType fourCharCodeToOSType (NSString* inCode) { OSType rval = 0; NSData* data = [inCode dataUsingEncoding:NSMacOSRomanStringEncoding]; [data getBytes:&rval length:sizeof (rval)]; HTONL (rval); return rval; } //------------------------------------------------------------------------ @interface VSTInterAppAudioAppDelegateBase () //------------------------------------------------------------------------ { VST3Plugin plugin; VST3Editor editor; BOOL audioIOInitialized; } @end //------------------------------------------------------------------------ @implementation VSTInterAppAudioAppDelegateBase //------------------------------------------------------------------------ //------------------------------------------------------------------------ - (BOOL)initAudioIO { id auArray = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AudioComponents"]; if (auArray) { id desc = [auArray objectAtIndex:0]; if (desc) { NSString* typeStr = [desc objectForKey:@"type"]; NSString* subtypeStr = [desc objectForKey:@"subtype"]; NSString* manufacturerStr = [desc objectForKey:@"manufacturer"]; NSString* nameStr = [desc objectForKey:@"name"]; if (typeStr && subtypeStr && manufacturerStr && nameStr) { OSType type = fourCharCodeToOSType (typeStr); OSType subtype = fourCharCodeToOSType (subtypeStr); OSType manufacturer = fourCharCodeToOSType (manufacturerStr); AudioIO* audioIO = AudioIO::instance (); if (audioIO->init (type, subtype, manufacturer, (__bridge CFStringRef)nameStr) == Steinberg::kResultTrue) { if (plugin.init ()) { InterAppAudioHostApp::instance ()->setPlugin (&plugin); audioIO->addProcessor (&plugin); audioIOInitialized = YES; return YES; } } } } } return NO; } //------------------------------------------------------------------------ - (BOOL)createUI { if (audioIOInitialized) { [UIApplication sharedApplication].statusBarHidden = YES; self.window = [UIWindow new]; self.window.backgroundColor = [UIColor redColor]; CGRect screenSize = self.window.bounds; if (editor.init (screenSize)) { self.window.rootViewController = editor.getViewController (); [self.window makeKeyAndVisible]; if (editor.attach (plugin.getEditController ()) == false) { return NO; } } return YES; } return NO; } //------------------------------------------------------------------------ - (void)savePluginState:(NSCoder*)coder { NSData* processorState = plugin.getProcessorState (); NSData* controllerState = plugin.getControllerState (); if (processorState) [coder encodeObject:processorState forKey:@"VST3ProcessorState"]; if (controllerState) [coder encodeObject:controllerState forKey:@"VST3ControllerState"]; } //------------------------------------------------------------------------ - (void)restorePluginState:(NSCoder*)coder { NSData* processorState = [coder decodeObjectForKey:@"VST3ProcessorState"]; if (processorState) { plugin.setProcessorState (processorState); } NSData* controllerState = [coder decodeObjectForKey:@"VST3ControllerState"]; if (controllerState) { plugin.setControllerState (controllerState); } } //------------------------------------------------------------------------ // UIApplicationDelegate methods //------------------------------------------------------------------------ - (BOOL)application:(UIApplication*)application willFinishLaunchingWithOptions:(NSDictionary*)launchOptions { return [self initAudioIO]; } //------------------------------------------------------------------------ - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { BOOL result = [self createUI]; if (result) { AudioIO::instance ()->start (); } return result; } //------------------------------------------------------------------------ - (BOOL)application:(UIApplication*)application shouldSaveApplicationState:(NSCoder*)coder { [self savePluginState:coder]; [coder encodeBool:MidiIO::instance ().isEnabled () forKey:@"MIDI Enabled"]; return YES; } //------------------------------------------------------------------------ - (BOOL)application:(UIApplication*)application shouldRestoreApplicationState:(NSCoder*)coder { [self restorePluginState:coder]; BOOL midiEnabled = [coder decodeBoolForKey:@"MIDI Enabled"]; MidiIO::instance ().setEnabled (midiEnabled); return YES; } //------------------------------------------------------------------------ - (void)applicationDidBecomeActive:(UIApplication*)application { AudioIO* audioIO = AudioIO::instance (); audioIO->start (); } //------------------------------------------------------------------------ - (void)applicationWillResignActive:(UIApplication*)application { AudioIO* audioIO = AudioIO::instance (); if (audioIO->getInterAppAudioConnected () == false && MidiIO::instance ().isEnabled () == false) { audioIO->stop (); } } @end qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/PresetBrowserViewController.0000644000000000000000000000013215124701711030725 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PresetBrowserViewController.mm0000644000175000001440000001730115124701711031251 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/PresetBrowserViewController.mm // Created by : Steinberg, 09/2013 // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "PresetBrowserViewController.h" #import "pluginterfaces/base/funknown.h" //------------------------------------------------------------------------ @interface PresetBrowserViewController () //------------------------------------------------------------------------ { IBOutlet UITableView* presetTableView; IBOutlet UIView* containerView; std::function callback; Steinberg::FUID uid; } @property (strong) NSArray* factoryPresets; @property (strong) NSArray* userPresets; @property (strong) NSArray* displayPresets; @property (assign) BOOL editMode; @end //------------------------------------------------------------------------ @implementation PresetBrowserViewController //------------------------------------------------------------------------ //------------------------------------------------------------------------ - (id)initWithCallback:(std::function)_callback { self = [super initWithNibName:@"PresetBrowserView" bundle:nil]; if (self) { callback = _callback; self.modalPresentationStyle = UIModalPresentationOverCurrentContext; self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; UIViewController* rootViewController = [[UIApplication sharedApplication].windows[0] rootViewController]; [rootViewController presentViewController:self animated:YES completion:^{}]; } return self; } //------------------------------------------------------------------------ - (void)setFactoryPresets:(NSArray*)factoryPresets userPresets:(NSArray*)userPresets { self.factoryPresets = factoryPresets; self.userPresets = userPresets; [self updatePresetArray]; dispatch_async (dispatch_get_main_queue (), ^{ [presetTableView reloadData]; }); } //------------------------------------------------------------------------ - (void)viewDidLoad { [super viewDidLoad]; containerView.layer.shadowOpacity = 0.5; containerView.layer.shadowOffset = CGSizeMake (5, 5); containerView.layer.shadowRadius = 5; } //------------------------------------------------------------------------ - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } //------------------------------------------------------------------------ - (void)updatePresetArray { if (self.userPresets) { self.displayPresets = [[self.factoryPresets arrayByAddingObjectsFromArray:self.userPresets] sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) { return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]]; }]; } else { self.displayPresets = self.factoryPresets; } } //------------------------------------------------------------------------ - (void)removeSelf { [self dismissViewControllerAnimated:YES completion:^{}]; } //------------------------------------------------------------------------ - (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath { NSURL* url = [self.displayPresets objectAtIndex:indexPath.row]; if (url) { callback ([[url path] UTF8String]); } [self removeSelf]; } //------------------------------------------------------------------------ - (IBAction)toggleEditMode:(id)sender { self.editMode = !self.editMode; if (self.editMode) { NSMutableArray* indexPaths = [NSMutableArray new]; for (NSURL* url in self.factoryPresets) { NSUInteger index = [self.displayPresets indexOfObjectIdenticalTo:url]; [indexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]]; } [presetTableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; } else { [self updatePresetArray]; NSMutableArray* indexPaths = [NSMutableArray new]; for (NSURL* url in self.factoryPresets) { NSUInteger index = [self.displayPresets indexOfObjectIdenticalTo:url]; [indexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]]; } [presetTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; } [presetTableView setEditing:self.editMode animated:YES]; } //------------------------------------------------------------------------ - (IBAction)cancel:(id)sender { if (callback) { callback (nullptr); } [self removeSelf]; } //------------------------------------------------------------------------ - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section { if (self.editMode) { return [self.userPresets count]; } return [self.displayPresets count]; } //------------------------------------------------------------------------ - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"PresetBrowserCell"]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"PresetBrowserCell"]; } cell.backgroundColor = [UIColor clearColor]; NSURL* presetUrl = nil; if (self.editMode) { presetUrl = [self.userPresets objectAtIndex:indexPath.row]; cell.detailTextLabel.text = @"User"; } else { presetUrl = [self.displayPresets objectAtIndex:indexPath.row]; if ([self.factoryPresets indexOfObject:presetUrl] == NSNotFound) { cell.detailTextLabel.text = @"User"; } else { cell.detailTextLabel.text = @"Factory"; } } cell.textLabel.text = [[presetUrl lastPathComponent] stringByDeletingPathExtension]; return cell; } //------------------------------------------------------------------------ - (BOOL)tableView:(UITableView*)tableView canEditRowAtIndexPath:(NSIndexPath*)indexPath { if (self.editMode) { return YES; } return NO; } //------------------------------------------------------------------------ - (void)tableView:(UITableView*)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath*)indexPath { NSURL* presetUrl = [self.userPresets objectAtIndex:indexPath.row]; if (presetUrl) { NSFileManager* fs = [NSFileManager defaultManager]; NSError* error = nil; if ([fs removeItemAtURL:presetUrl error:&error] == NO) { auto alertController = [UIAlertController alertControllerWithTitle:[error localizedDescription] message:[error localizedRecoverySuggestion] preferredStyle:UIAlertControllerStyleAlert]; [self presentViewController:alertController animated:YES completion:nil]; } else { NSMutableArray* newArray = [NSMutableArray arrayWithArray:self.userPresets]; [newArray removeObject:presetUrl]; self.userPresets = newArray; [presetTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } } } //------------------------------------------------------------------------ - (BOOL)prefersStatusBarHidden { return YES; } @end qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/SettingsViewController.h0000644000000000000000000000013215124701711030067 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282210861 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/SettingsViewController.h0000644000175000001440000000204415124701711030057 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/SettingsViewController.h // Created by : Steinberg, 09/2013 // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #ifdef __OBJC__ #import @interface SettingsViewController : UIViewController @end #endif // __OBJC__ extern void showIOSettings (); qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/PresetManager.mm0000644000000000000000000000013215124701711026307 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PresetManager.mm0000644000175000001440000002064415124701711026305 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/PresetManager.mm // Created by : Steinberg, 10/2013 // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "PresetManager.h" #import "PresetBrowserViewController.h" #import "PresetSaveViewController.h" #import "public.sdk/source/vst/vstpresetfile.h" #import "pluginterfaces/vst/ivstattributes.h" namespace Steinberg { namespace Vst { namespace InterAppAudio { //----------------------------------------------------------------------------- class PresetStream : public ReadOnlyBStream, public IStreamAttributes { public: PresetStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize, const char* utf8Path) : ReadOnlyBStream (sourceStream, sourceOffset, sectionSize), fileName (utf8Path) { fileName.toWideString (kCP_Utf8); } virtual tresult PLUGIN_API getFileName (String128 name) override { if (fileName.length () > 0) { fileName.copyTo (name, 0, 128); return kResultTrue; } return kResultFalse; } virtual IAttributeList* PLUGIN_API getAttributes () override { return nullptr; } DEF_INTERFACES_1 (IStreamAttributes, ReadOnlyBStream) REFCOUNT_METHODS (ReadOnlyBStream) protected: String fileName; }; //----------------------------------------------------------------------------- PresetManager::PresetManager (VST3Plugin* plugin, const TUID& cid) : plugin (plugin) , visiblePresetBrowserViewController (nil) , visibleSavePresetViewController (nil) , cid (cid) { id obj = [[NSUserDefaults standardUserDefaults] objectForKey:@"PresetManager|lastPreset"]; if (obj && [obj isKindOfClass:[NSString class]]) { lastPreset = [obj UTF8String]; } } //----------------------------------------------------------------------------- NSArray* PresetManager::getPresetPaths (PresetPathType type) { if (type == kFactory) { return [[NSBundle mainBundle] URLsForResourcesWithExtension:@"vstpreset" subdirectory:@"Presets"]; } NSFileManager* fs = [NSFileManager defaultManager]; NSURL* documentsUrl = [fs URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:Nil create:YES error:NULL]; if (documentsUrl) { NSMutableArray* userUrls = [NSMutableArray new]; NSDirectoryEnumerator* enumerator = [fs enumeratorAtURL:documentsUrl includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsSubdirectoryDescendants errorHandler:nil]; for (NSURL* url in enumerator.allObjects) { if ([[url pathExtension] isEqualToString:@"vstpreset"]) { [userUrls addObject:url]; } } return [userUrls sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) { return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]]; }]; } return nil; } //----------------------------------------------------------------------------- tresult PLUGIN_API PresetManager::runLoadPresetBrowser () { if (visiblePresetBrowserViewController != nil) return kResultFalse; addRef (); visiblePresetBrowserViewController = [[PresetBrowserViewController alloc] initWithCallback:[this] (const char* path) { loadPreset (path); visiblePresetBrowserViewController = nil; release (); }]; addRef (); dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ if (visiblePresetBrowserViewController) { [visiblePresetBrowserViewController setFactoryPresets:getPresetPaths (kFactory) userPresets:getPresetPaths (kUser)]; } release (); }); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API PresetManager::runSavePresetBrowser () { if (visibleSavePresetViewController != nil) return kResultFalse; addRef (); visibleSavePresetViewController = [[PresetSaveViewController alloc] initWithCallback:[this] (const char* path) { savePreset (path); visibleSavePresetViewController = nil; release (); }]; return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API PresetManager::loadNextPreset () { return loadPreset (true); } //----------------------------------------------------------------------------- tresult PLUGIN_API PresetManager::loadPreviousPreset () { return loadPreset (false); } //----------------------------------------------------------------------------- tresult PresetManager::loadPreset (bool next) { NSArray* presets = [[getPresetPaths (kFactory) arrayByAddingObjectsFromArray:getPresetPaths (kUser)] sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) { return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]]; }]; __block NSUInteger index = NSNotFound; if (lastPreset.isEmpty () == false) { NSURL* lastUrl = [[NSURL fileURLWithPath:[NSString stringWithUTF8String:lastPreset]] fileReferenceURL]; if (lastUrl) { [presets enumerateObjectsUsingBlock:^(NSURL* obj, NSUInteger idx, BOOL* stop) { if ([[obj fileReferenceURL] isEqual:lastUrl]) { index = idx; *stop = YES; } }]; } } if (index == NSNotFound) { if (next) index = [presets count] - 1; else index = 1; } if (index != NSNotFound) { if (next) { if (index >= [presets count] - 1) index = 0; else index++; } else { if (index == 0) index = [presets count] - 1; else index--; } return loadPreset ([[[presets objectAtIndex:index] path] UTF8String]); } return kResultFalse; } //----------------------------------------------------------------------------- tresult PresetManager::loadPreset (const char* path) { if (path) { IPtr stream = owned (FileStream::open (path, "r")); if (stream) { [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithUTF8String:path] forKey:@"PresetManager|lastPreset"]; lastPreset = path; auto component = U::cast (plugin->getAudioProcessor ()); IEditController* controller = plugin->getEditController (); if (component) { PresetFile pf (stream); if (!pf.readChunkList ()) return kResultFalse; if (pf.getClassID () != cid) return kResultFalse; const PresetFile::Entry* e = pf.getEntry (kComponentState); if (e == nullptr) return kResultFalse; auto filename = strrchr (path, '/'); if (filename) filename++; IPtr readOnlyBStream = owned (new PresetStream (stream, e->offset, e->size, filename)); tresult result = component->setState (readOnlyBStream); if ((result == kResultTrue || result == kNotImplemented) && controller) { readOnlyBStream->seek (0, IBStream::kIBSeekSet); controller->setComponentState (readOnlyBStream); if (pf.contains (kControllerState)) { e = pf.getEntry (kControllerState); if (e) { readOnlyBStream = owned (new PresetStream (stream, e->offset, e->size, filename)); controller->setState (readOnlyBStream); } } } return result; } } } return kResultFalse; } //----------------------------------------------------------------------------- void PresetManager::savePreset (const char* path) { IBStream* stream = FileStream::open (path, "w"); if (stream) { auto component = U::cast (plugin->getAudioProcessor ()); IEditController* controller = plugin->getEditController (); if (component) { PresetFile::savePreset (stream, cid, component, controller); } stream->release (); loadPreset (path); } } } } } // namespaces qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/HostApp.h0000644000000000000000000000013215124701711024746 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/HostApp.h0000644000175000001440000000516115124701711024741 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/HostApp.h // Created by : Steinberg, 08/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once /// \cond ignore #import "public.sdk/source/vst/hosting/hostclasses.h" #import "base/source/fobject.h" #import "pluginterfaces/vst/ivstinterappaudio.h" namespace Steinberg { namespace Vst { namespace InterAppAudio { class VST3Plugin; //----------------------------------------------------------------------------- class InterAppAudioHostApp : public FObject, public HostApplication, public IInterAppAudioHost { public: //----------------------------------------------------------------------------- static InterAppAudioHostApp* instance (); void setPlugin (VST3Plugin* plugin); VST3Plugin* getPlugin () const { return plugin; } //----------------------------------------------------------------------------- // IInterAppAudioHost tresult PLUGIN_API getScreenSize (ViewRect* size, float* scale) override; tresult PLUGIN_API connectedToHost () override; tresult PLUGIN_API switchToHost () override; tresult PLUGIN_API sendRemoteControlEvent (uint32 event) override; tresult PLUGIN_API getHostIcon (void** icon) override; tresult PLUGIN_API scheduleEventFromUI (Event& event) override; IInterAppAudioPresetManager* PLUGIN_API createPresetManager (const TUID& cid) override; tresult PLUGIN_API showSettingsView () override; //----------------------------------------------------------------------------- // HostApplication tresult PLUGIN_API getName (String128 name) override; OBJ_METHODS (InterAppAudioHostApp, FObject) REFCOUNT_METHODS (FObject) DEFINE_INTERFACES DEF_INTERFACE (IHostApplication) DEF_INTERFACE (IInterAppAudioHost) END_DEFINE_INTERFACES (FObject) protected: InterAppAudioHostApp (); VST3Plugin* plugin {nullptr}; }; //------------------------------------------------------------------------ } // namespace InterAppAudio } // namespace Vst } // namespace Steinberg /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/PresetSaveView.xib0000644000000000000000000000013215124701711026637 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PresetSaveView.xib0000644000175000001440000001647415124701711026643 0ustar00rncbcusers qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/SettingsViewController.mm0000644000000000000000000000013215124701711030251 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282210861 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/SettingsViewController.mm0000644000175000001440000000772215124701711030251 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/SettingsViewController.mm // Created by : Steinberg, 09/2013 // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "SettingsViewController.h" #import "AudioIO.h" #import "MidiIO.h" #import using namespace Steinberg::Vst::InterAppAudio; static const NSUInteger kMinTempo = 30; //------------------------------------------------------------------------ @interface SettingsViewController () //------------------------------------------------------------------------ { IBOutlet UIView* containerView; IBOutlet UISwitch* midiOnSwitch; IBOutlet UIPickerView* tempoView; } @end //------------------------------------------------------------------------ @implementation SettingsViewController //------------------------------------------------------------------------ //------------------------------------------------------------------------ - (id)init { self = [super initWithNibName:@"SettingsView" bundle:nil]; if (self) { self.modalPresentationStyle = UIModalPresentationOverCurrentContext; self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; } return self; } //------------------------------------------------------------------------ - (void)viewDidLoad { [super viewDidLoad]; containerView.layer.shadowOpacity = 0.5; containerView.layer.shadowOffset = CGSizeMake (5, 5); containerView.layer.shadowRadius = 5; midiOnSwitch.on = MidiIO::instance ().isEnabled (); Float64 tempo = AudioIO::instance ()->getStaticFallbackTempo (); [tempoView selectRow:tempo - kMinTempo inComponent:0 animated:YES]; } //------------------------------------------------------------------------ - (IBAction)enableMidi:(id)sender { BOOL state = midiOnSwitch.on; MidiIO::instance ().setEnabled (state); } //------------------------------------------------------------------------ - (IBAction)close:(id)sender { [self dismissViewControllerAnimated:YES completion:^{}]; } //------------------------------------------------------------------------ - (void)pickerView:(UIPickerView*)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { AudioIO::instance ()->setStaticFallbackTempo (row + kMinTempo); } //------------------------------------------------------------------------ - (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView { return 1; } //------------------------------------------------------------------------ - (NSInteger)pickerView:(UIPickerView*)pickerView numberOfRowsInComponent:(NSInteger)component { return 301 - kMinTempo; } //------------------------------------------------------------------------ - (NSString*)pickerView:(UIPickerView*)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { return [@(row + kMinTempo) stringValue]; } //------------------------------------------------------------------------ - (BOOL)prefersStatusBarHidden { return YES; } @end //------------------------------------------------------------------------ void showIOSettings () { SettingsViewController* controller = [[SettingsViewController alloc] init]; UIViewController* rootViewController = [[UIApplication sharedApplication].windows[0] rootViewController]; [rootViewController presentViewController:controller animated:YES completion:^{}]; } qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/VST3Plugin.mm0000644000000000000000000000013215124701711025470 xustar0030 mtime=1767080905.282210861 30 atime=1767080905.282210861 30 ctime=1767080905.282210861 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/VST3Plugin.mm0000644000175000001440000004177715124701711025500 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/VST3Plugin.mm // Created by : Steinberg, 08/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "VST3Plugin.h" #import "HostApp.h" #import "public.sdk/source/vst/auwrapper/NSDataIBStream.h" #import "public.sdk/source/vst/hosting/hostclasses.h" #import "base/source/updatehandler.h" #import "pluginterfaces/base/ipluginbase.h" #import "pluginterfaces/vst/ivstinterappaudio.h" #import "pluginterfaces/vst/ivstmessage.h" #import "pluginterfaces/vst/ivstmidicontrollers.h" #import //------------------------------------------------------------------------ extern "C" { bool bundleEntry (CFBundleRef); bool bundleExit (void); } namespace Steinberg { namespace Vst { namespace InterAppAudio { //------------------------------------------------------------------------ __attribute__ ((constructor)) static void InitUpdateHandler () { UpdateHandler::instance (); } //------------------------------------------------------------------------ VST3Plugin::VST3Plugin () { processData.processContext = &processContext; processData.inputParameterChanges = &inputParamChanges; processData.outputParameterChanges = &outputParamChanges; processData.inputEvents = &inputEvents; } //------------------------------------------------------------------------ VST3Plugin::~VST3Plugin () { } //------------------------------------------------------------------------ bool VST3Plugin::init () { if (processor == nullptr && editController == nullptr) { ::bundleEntry (CFBundleGetMainBundle ()); createProcessorAndController (); } return processor && editController; } //------------------------------------------------------------------------ void VST3Plugin::createProcessorAndController () { Steinberg::IPluginFactory* factory = GetPluginFactory (); if (factory == nullptr) return; IComponent* component = nullptr; PClassInfo classInfo; int32 classCount = factory->countClasses (); for (int32 i = 0; i < classCount; i++) { if (factory->getClassInfo (i, &classInfo) != kResultTrue) return; if (strcmp (classInfo.category, kVstAudioEffectClass) == 0) { if (factory->createInstance (classInfo.cid, IComponent::iid, (void**)&component) != kResultTrue) { return; } break; } } if (component) { if (component->initialize (InterAppAudioHostApp::instance ()->unknownCast ()) != kResultTrue) { component->release (); return; } if (component->queryInterface (IEditController::iid, (void**)&editController) != kResultTrue) { TUID controllerCID {}; if (component->getControllerClassId (controllerCID) == kResultTrue) { if (factory->createInstance (controllerCID, IEditController::iid, (void**)&editController) != kResultTrue) return; editController->setComponentHandler (this); if (editController->initialize ( InterAppAudioHostApp::instance ()->unknownCast ()) != kResultTrue) { component->release (); editController->release (); editController = nullptr; return; } auto compConnection = U::cast (component); auto ctrlerConnection = U::cast (editController); if (compConnection && ctrlerConnection) { compConnection->connect (ctrlerConnection); ctrlerConnection->connect (compConnection); } } else { component->release (); return; } } component->queryInterface (IAudioProcessor::iid, (void**)&processor); if (processor == nullptr) { if (editController) { editController->release (); editController = nullptr; } } else { NSMutableData* data = [NSMutableData new]; NSMutableDataIBStream state (data); if (component->getState (&state) == kResultTrue) { state.seek (0, IBStream::kIBSeekSet); editController->setComponentState (&state); } int32 paramCount = editController->getParameterCount (); inputParamChanges.setMaxParameters (paramCount); inputParamChangeTransfer.setMaxParameters (paramCount); outputParamChanges.setMaxParameters (paramCount); outputParamChangeTransfer.setMaxParameters (paramCount); midiControllerToParamIDMap = createMIDIControllerToParamIDMap (); uiScheduledEvents.resize (kMaxUIEvents); } component->release (); } } //------------------------------------------------------------------------ VST3Plugin::MIDIControllerToParamIDMap VST3Plugin::createMIDIControllerToParamIDMap () { MIDIControllerToParamIDMap newMap; auto midiMapping = U::cast (editController); if (midiMapping) { uint16 channelCount = 0; auto component = U::cast (processor); if (component) { int32 busCount = component->getBusCount (kEvent, kInput); if (busCount > 0) { BusInfo busInfo; if (component->getBusInfo (kEvent, kInput, 0, busInfo) == kResultTrue) { channelCount = busInfo.channelCount; } } } ParamID paramID; for (int32 channel = 0; channel < channelCount; channel++) { for (CtrlNumber ctrler = 0; ctrler < kCountCtrlNumber; ctrler++) { if (midiMapping->getMidiControllerAssignment (0, channel, ctrler, paramID) == kResultTrue) { newMap.insert ( std::make_pair (channelAndCtrlNumber (channel, ctrler), paramID)); } } } } return newMap; } //------------------------------------------------------------------------ tresult VST3Plugin::scheduleEventFromUI (Event& event) { if (event.type == Event::kNoteOnEvent) event.noteOn.noteId = lastNodeID++; return uiScheduledEvents.push (event) ? kResultTrue : kResultFalse; } //------------------------------------------------------------------------ NSData* VST3Plugin::getProcessorState () { if (processor) { NSMutableData* data = [NSMutableData new]; NSMutableDataIBStream state (data); auto comp = U::cast (processor); if (comp->getState (&state) == kResultTrue) { return data; } } return nil; } //------------------------------------------------------------------------ bool VST3Plugin::setProcessorState (NSData* data) { if (editController && processor) { NSDataIBStream stream (data); auto comp = U::cast (processor); if (comp->setState (&stream) == kResultTrue) { stream.seek (0, IBStream::kIBSeekSet); editController->setComponentState (&stream); return true; } } return false; } //------------------------------------------------------------------------ NSData* VST3Plugin::getControllerState () { if (editController) { NSMutableData* data = [NSMutableData new]; NSMutableDataIBStream state (data); if (editController->getState (&state) == kResultTrue) { return data; } } return nil; } //------------------------------------------------------------------------ bool VST3Plugin::setControllerState (NSData* data) { if (editController) { NSDataIBStream stream (data); if (editController->setState (&stream) == kResultTrue) { return true; } } return false; } //------------------------------------------------------------------------ void VST3Plugin::willStartAudio (AudioIO* audioIO) { noteIDPitchMap.clear (); lastNodeID.store (0); ProcessSetup setup; setup.processMode = kRealtime; setup.symbolicSampleSize = kSample32; setup.maxSamplesPerBlock = 4096; // TODO: setup.sampleRate = audioIO->getSampleRate (); processor->setupProcessing (setup); SpeakerArrangement inputs[1]; SpeakerArrangement outputs[1]; inputs[0] = SpeakerArr::kStereo; outputs[0] = SpeakerArr::kStereo; processor->setBusArrangements (inputs, 1, outputs, 1); auto comp = U::cast (processor); comp->setActive (true); processData.prepare (*comp, setup.maxSamplesPerBlock, setup.symbolicSampleSize); auto iaaConnectionNotification = U::cast (editController); if (iaaConnectionNotification) { iaaConnectionNotification->onInterAppAudioConnectionStateChange ( audioIO->getInterAppAudioConnected () ? true : false); } timer = Timer::create (this, 16); } //------------------------------------------------------------------------ void VST3Plugin::didStopAudio (AudioIO* audioIO) { processor->setProcessing (false); processing = false; auto comp = U::cast (processor); comp->setActive (false); timer->release (); timer = nullptr; } //------------------------------------------------------------------------ void VST3Plugin::onMIDIEvent (UInt32 inStatus, UInt32 data1, UInt32 data2, UInt32 sampleOffset, bool withinRealtimeThread) { Event e = {}; e.flags = Event::kIsLive; uint16 status = inStatus & 0xF0; uint16 channel = inStatus & 0x0F; if (status == 0x90 && data2 != 0) // note on { auto noteID = noteIDPitchMap.find ((channel << 8) + data1); if (noteID != noteIDPitchMap.end ()) { // for now, we just turn off the old note on Event e2 = {}; e2.type = Event::kNoteOffEvent; e2.noteOff.noteId = noteID->second; e2.noteOff.channel = channel; e2.noteOff.pitch = data1; e2.noteOff.velocity = (float)data2 / 128.f; e2.sampleOffset = 0; inputEvents.addEvent (e2); noteIDPitchMap.erase (noteID); } e.type = Event::kNoteOnEvent; e.noteOn.channel = channel; e.noteOn.pitch = data1; e.noteOn.velocity = (float)data2 / 128.f; e.noteOn.length = -1; e.sampleOffset = sampleOffset; if (withinRealtimeThread) { e.noteOn.noteId = lastNodeID++; inputEvents.addEvent (e); noteIDPitchMap.insert (std::make_pair ((channel << 8) + data1, e.noteOn.noteId)); } else { scheduleEventFromUI (e); } } else if (status == 0x80 || (status == 0x90 && data2 == 0)) // note off { auto noteID = noteIDPitchMap.find ((channel << 8) + data1); if (noteID != noteIDPitchMap.end ()) { e.type = Event::kNoteOffEvent; e.noteOff.noteId = noteID->second; e.noteOff.channel = channel; e.noteOff.pitch = data1; e.noteOff.velocity = (float)data2 / 128.f; e.sampleOffset = sampleOffset; if (withinRealtimeThread) { inputEvents.addEvent (e); noteIDPitchMap.erase (noteID); } else { scheduleEventFromUI (e); } } else { NSLog (@"NoteID not found:%d", (unsigned int)data1); } } else if (status == 0xb0 && data1 < kAfterTouch) // controller { auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, data1)); if (it != midiControllerToParamIDMap.end ()) { ParamValue value = (ParamValue)data2 / 128.; if (withinRealtimeThread) { int32 index; IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index); if (queue) { queue->addPoint (sampleOffset, value, index); } } else { inputParamChangeTransfer.addChange (it->second, value, sampleOffset); } } } else if (status == 0xe0) // pitch bend { auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, kPitchBend)); if (it != midiControllerToParamIDMap.end ()) { uint16 _14bit; _14bit = (uint16)data2; _14bit <<= 7; _14bit |= (uint16)data1; ParamValue value = (double)_14bit / (double)0x3fff; if (withinRealtimeThread) { int32 index; IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index); if (queue) { queue->addPoint (sampleOffset, value, index); } } else { inputParamChangeTransfer.addChange (it->second, value, sampleOffset); } } } else if (status == 0xd0) // aftertouch { auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, kAfterTouch)); if (it != midiControllerToParamIDMap.end ()) { ParamValue value = (ParamValue)data1 / 128.; if (withinRealtimeThread) { int32 index; IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index); if (queue) { queue->addPoint (sampleOffset, value, index); } } else { inputParamChangeTransfer.addChange (it->second, value, sampleOffset); } } } } //------------------------------------------------------------------------ void VST3Plugin::process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames, AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) { if (processing == false) { processor->setProcessing (true); processing = true; } updateProcessContext (audioIO); if (timeStamp) processContext.systemTime = timeStamp->mHostTime; // TODO: silence state update for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) { processData.setChannelBuffer (kInput, 0, i, (float*)ioData->mBuffers[i].mData); processData.setChannelBuffer (kOutput, 0, i, (float*)ioData->mBuffers[i].mData); } Event e; while (uiScheduledEvents.pop (e)) { inputEvents.addEvent (e); if (e.type == Event::kNoteOnEvent) { auto noteID = noteIDPitchMap.find ((e.noteOn.channel << 8) + e.noteOn.pitch); if (noteID == noteIDPitchMap.end ()) noteIDPitchMap.insert ( std::make_pair ((e.noteOn.channel << 8) + e.noteOn.pitch, e.noteOn.noteId)); } } processData.numSamples = numFrames; inputParamChangeTransfer.transferChangesTo (inputParamChanges); if (processor->process (processData) == kResultTrue) { inputParamChanges.clearQueue (); outputParamChangeTransfer.transferChangesFrom (outputParamChanges); outputParamChanges.clearQueue (); inputEvents.clear (); } } //------------------------------------------------------------------------ void VST3Plugin::updateProcessContext (AudioIO* audioIO) { memset (&processContext, 0, sizeof (ProcessContext)); processContext.sampleRate = audioIO->getSampleRate (); Float64 beat = 0., tempo = 0.; if (audioIO->getBeatAndTempo (beat, tempo)) { processContext.state |= ProcessContext::kTempoValid | ProcessContext::kProjectTimeMusicValid; processContext.tempo = tempo; processContext.projectTimeMusic = beat; } else { processContext.state |= ProcessContext::kTempoValid; processContext.tempo = 120.; } UInt32 deltaSampleOffsetToNextBeat = 0; Float32 timeSigNumerator = 0; UInt32 timeSigDenominator = 0; Float64 currentMeasureDownBeat = 0; if (audioIO->getMusicalTimeLocation (deltaSampleOffsetToNextBeat, timeSigNumerator, timeSigDenominator, currentMeasureDownBeat)) { processContext.state |= ProcessContext::kTimeSigValid | ProcessContext::kBarPositionValid | ProcessContext::kClockValid; processContext.timeSigNumerator = timeSigNumerator; processContext.timeSigDenominator = timeSigDenominator; processContext.samplesToNextClock = deltaSampleOffsetToNextBeat; processContext.barPositionMusic = currentMeasureDownBeat; } Boolean isPlaying; Boolean isRecording; Boolean transportStateChanged; Float64 currentSampleInTimeLine; Boolean isCycling; Float64 cycleStartBeat; Float64 cycleEndBeat; if (audioIO->getTransportState (isPlaying, isRecording, transportStateChanged, currentSampleInTimeLine, isCycling, cycleStartBeat, cycleEndBeat)) { processContext.state |= ProcessContext::kCycleValid; processContext.cycleStartMusic = cycleStartBeat; processContext.cycleEndMusic = cycleEndBeat; processContext.projectTimeSamples = currentSampleInTimeLine; if (isPlaying) processContext.state |= ProcessContext::kPlaying; if (isCycling) processContext.state |= ProcessContext::kCycleActive; if (isRecording) processContext.state |= ProcessContext::kRecording; } } //------------------------------------------------------------------------ tresult PLUGIN_API VST3Plugin::beginEdit (ParamID id) { return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API VST3Plugin::performEdit (ParamID id, ParamValue valueNormalized) { inputParamChangeTransfer.addChange (id, valueNormalized, 0); return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API VST3Plugin::endEdit (ParamID id) { return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API VST3Plugin::restartComponent (int32 flags) { tresult result = kNotImplemented; return result; } //------------------------------------------------------------------------ void VST3Plugin::onTimer (Timer* timer) { ParamID paramID; ParamValue paramValue; int32 sampleOffset; while (outputParamChangeTransfer.getNextChange (paramID, paramValue, sampleOffset)) { editController->setParamNormalized (paramID, paramValue); } UpdateHandler::instance ()->triggerDeferedUpdates (); } } } } qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215124701711025757 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/CMakeLists.txt0000644000175000001440000000364515124701711025757 0ustar00rncbcusers if(SMTG_MAC) option(SMTG_BUILD_INTERAPPAUDIO "Enable building the iOS InterAppAudio examples (deprecated)" OFF) if(XCODE AND SMTG_ENABLE_IOS_TARGETS AND SMTG_BUILD_INTERAPPAUDIO) message("[SMTG] ********************************************************************************************************************************") message("[SMTG] * The iOS InterAppAudio wrapper is deprecated and may be removed in the next SDK update. Please switch to AudioUnit V3 on iOS. *") message("[SMTG] ********************************************************************************************************************************") set(target interappaudio) set(${target}_sources AudioIO.mm AudioIO.h HostApp.mm HostApp.h MidiIO.mm MidiIO.h PresetBrowserViewController.mm PresetBrowserViewController.h PresetManager.mm PresetManager.h PresetSaveViewController.mm PresetSaveViewController.h SettingsViewController.mm SettingsViewController.h VST3Editor.mm VST3Editor.h VST3Plugin.mm VST3Plugin.h VSTInterAppAudioAppDelegateBase.mm VSTInterAppAudioAppDelegateBase.h ) add_library(${target} STATIC ${${target}_sources}) smtg_set_platform_ios(${target}) set_target_properties(${target} PROPERTIES ${SDK_IDE_LIBS_FOLDER} ) target_link_libraries(${target} PRIVATE sdk_ios "-framework CoreGraphics" "-framework UIKit" "-framework CoreMIDI" "-framework AudioToolbox" "-framework AVFoundation" ) endif(XCODE AND SMTG_ENABLE_IOS_TARGETS AND SMTG_BUILD_INTERAPPAUDIO) endif(SMTG_MAC)qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/PaxHeaders/HostApp.mm0000644000000000000000000000013215124701711025130 xustar0030 mtime=1767080905.282031469 30 atime=1767080905.282031469 30 ctime=1767080905.282031469 qtractor-1.5.11/src/vst3/public.sdk/source/vst/interappaudio/HostApp.mm0000644000175000001440000001051215124701711025117 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/HostApp.mm // Created by : Steinberg, 08/2013. // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "HostApp.h" #import "AudioIO.h" #import "PresetManager.h" #import "SettingsViewController.h" #import "VST3Plugin.h" #import "base/source/updatehandler.h" #import "pluginterfaces/gui/iplugview.h" namespace Steinberg { namespace Vst { namespace InterAppAudio { //------------------------------------------------------------------------ InterAppAudioHostApp* InterAppAudioHostApp::instance () { static InterAppAudioHostApp gInstance; return &gInstance; } //----------------------------------------------------------------------------- InterAppAudioHostApp::InterAppAudioHostApp () = default; //----------------------------------------------------------------------------- void InterAppAudioHostApp::setPlugin (VST3Plugin* plugin) { this->plugin = plugin; } //----------------------------------------------------------------------------- tresult PLUGIN_API InterAppAudioHostApp::getName (String128 name) { String str ("InterAppAudioHost"); str.copyTo (name, 0, 127); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API InterAppAudioHostApp::getScreenSize (ViewRect* size, float* scale) { if (size) { UIScreen* screen = [UIScreen mainScreen]; CGSize s = [screen currentMode].size; UIWindow* window = [[[UIApplication sharedApplication] windows] objectAtIndex:0]; if (window) { NSArray* subViews = [window subviews]; if ([subViews count] == 1) { s = [[subViews objectAtIndex:0] bounds].size; } } size->left = 0; size->top = 0; size->right = s.width; size->bottom = s.height; if (scale) { *scale = screen.scale; } return kResultTrue; } return kInvalidArgument; } //----------------------------------------------------------------------------- tresult PLUGIN_API InterAppAudioHostApp::connectedToHost () { return AudioIO::instance ()->getInterAppAudioConnected () ? kResultTrue : kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API InterAppAudioHostApp::switchToHost () { return AudioIO::instance ()->switchToHost () ? kResultTrue : kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API InterAppAudioHostApp::sendRemoteControlEvent (uint32 event) { return AudioIO::instance ()->sendRemoteControlEvent ( static_cast (event)) ? kResultTrue : kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API InterAppAudioHostApp::getHostIcon (void** icon) { if (icon) { UIImage* hostIcon = AudioIO::instance ()->getHostIcon (); if (hostIcon) { CGImageRef cgImage = [hostIcon CGImage]; if (cgImage) { *icon = cgImage; return kResultTrue; } } return kNotImplemented; } return kInvalidArgument; } //----------------------------------------------------------------------------- tresult PLUGIN_API InterAppAudioHostApp::scheduleEventFromUI (Event& event) { if (plugin) { return plugin->scheduleEventFromUI (event); } return kNotInitialized; } //----------------------------------------------------------------------------- IInterAppAudioPresetManager* PLUGIN_API InterAppAudioHostApp::createPresetManager (const TUID& cid) { return plugin ? new PresetManager (plugin, cid) : nullptr; } //----------------------------------------------------------------------------- tresult PLUGIN_API InterAppAudioHostApp::showSettingsView () { showIOSettings (); return kResultTrue; } } } } qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/basewrapper0000644000000000000000000000013015124701711022607 xustar0029 mtime=1767080905.28072064 30 atime=1767080905.280506322 29 ctime=1767080905.28072064 qtractor-1.5.11/src/vst3/public.sdk/source/vst/basewrapper/0000755000175000001440000000000015124701711022656 5ustar00rncbcusersqtractor-1.5.11/src/vst3/public.sdk/source/vst/basewrapper/PaxHeaders/basewrapper.cpp0000644000000000000000000000013215124701711025705 xustar0030 mtime=1767080905.280506322 30 atime=1767080905.280506322 30 ctime=1767080905.280506322 qtractor-1.5.11/src/vst3/public.sdk/source/vst/basewrapper/basewrapper.cpp0000644000175000001440000011727415124701711025711 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/basewrapper/basewrapper.cpp // Created by : Steinberg, 01/2009 // Description : VST 3 -> XXX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /// \cond ignore #include "public.sdk/source/vst/basewrapper/basewrapper.h" #include "public.sdk/source/vst/hosting/connectionproxy.h" #include "public.sdk/source/vst/hosting/hostclasses.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/base/futils.h" #include "pluginterfaces/base/keycodes.h" #include "pluginterfaces/gui/iplugview.h" #include "pluginterfaces/vst/ivstmessage.h" #include "pluginterfaces/vst/ivstmidicontrollers.h" #include "pluginterfaces/vst/vstpresetkeys.h" #include "base/source/fstreamer.h" #include #include #include #if SMTG_OS_MACOS #include #include #endif extern "C" { #if SMTG_OS_MACOS // implemented in macmain.cpp SMTG_EXPORT_SYMBOL bool bundleEntry (CFBundleRef); SMTG_EXPORT_SYMBOL bool bundleExit (void); #elif SMTG_OS_WINDOWS // implemented in dllmain.cpp SMTG_EXPORT_SYMBOL bool InitDll (); SMTG_EXPORT_SYMBOL bool ExitDll (); #else #error platform not supported! #endif } #if SMTG_OS_MACOS //------------------------------------------------------------------------ static CFBundleRef GetBundleFromExecutable (const char* filepath) { // AutoreleasePool ap; char* fname = strdup (filepath); size_t pos = strlen (fname); int level = 3; while (level > 0 && --pos >= 0) { if (fname[pos] == '/') level--; } if (level > 0) return 0; fname[pos] = 0; CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*)fname, pos, true); CFBundleRef bundle = CFBundleCreate (0, url); return bundle; } //------------------------------------------------------------------------ static CFBundleRef GetCurrentBundle () { Dl_info info; if (dladdr ((const void*)GetCurrentBundle, &info)) { if (info.dli_fname) { return GetBundleFromExecutable (info.dli_fname); } } return 0; } #endif // SMTG_OS_MACOS //------------------------------------------------------------------------ bool _InitModule () { #if SMTG_OS_MACOS return bundleEntry (GetCurrentBundle ()); #else return InitDll (); #endif } //------------------------------------------------------------------------ bool _DeinitModule () { #if SMTG_OS_MACOS return bundleExit (); #else return ExitDll (); #endif } //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // some Globals //------------------------------------------------------------------------ // In order to speed up hasEditor function gPluginHasEditor can be set in EditController::initialize enum { kDontKnow = -1, kNoEditor = 0, kEditor, }; // default: kDontKnow which uses createView to find out using EditorAvailability = int32; EditorAvailability gPluginHasEditor = kDontKnow; // Set to 'true' in EditController::initialize // default: VST 3 kIsProgramChange parameter will not be exported bool gExportProgramChangeParameters = false; //------------------------------------------------------------------------ BaseEditorWrapper::BaseEditorWrapper (IEditController* controller) : mController (controller) { } //------------------------------------------------------------------------ BaseEditorWrapper::~BaseEditorWrapper () { if (mView) _close (); mController = nullptr; } //------------------------------------------------------------------------ tresult PLUGIN_API BaseEditorWrapper::queryInterface (const char* _iid, void** obj) { QUERY_INTERFACE (_iid, obj, FUnknown::iid, IPlugFrame) QUERY_INTERFACE (_iid, obj, IPlugFrame::iid, IPlugFrame) *obj = nullptr; return kNoInterface; } //------------------------------------------------------------------------ tresult PLUGIN_API BaseEditorWrapper::resizeView (IPlugView* view, ViewRect* newSize) { tresult result = kResultFalse; if (view && newSize) { result = view->onSize (newSize); } return result; } //------------------------------------------------------------------------ bool BaseEditorWrapper::hasEditor (IEditController* controller) { /* Some plug-ins might have large GUIs. In order to speed up hasEditor function while * initializing the plug-in gPluginHasEditor can be set in EditController::initialize * beforehand. */ bool result = false; if (gPluginHasEditor == kEditor) { result = true; } else if (gPluginHasEditor == kNoEditor) { result = false; } else { IPlugView* view = controller ? controller->createView (ViewType::kEditor) : nullptr; FReleaser viewRel (view); result = (view != nullptr); } return result; } //------------------------------------------------------------------------ void BaseEditorWrapper::createView () { if (mView == nullptr && mController != nullptr) { mView = owned (mController->createView (ViewType::kEditor)); mView->setFrame (this); #if SMTG_OS_MACOS #if SMTG_PLATFORM_64 if (mView && mView->isPlatformTypeSupported (kPlatformTypeNSView) != kResultTrue) #else if (mView && mView->isPlatformTypeSupported (kPlatformTypeHIView) != kResultTrue) #endif { mView = nullptr; mController = nullptr; } #endif } } //------------------------------------------------------------------------ bool BaseEditorWrapper::getRect (ViewRect& rect) { createView (); if (!mView) return false; if (mView->getSize (&rect) == kResultTrue) { if ((rect.bottom - rect.top) > 0 && (rect.right - rect.left) > 0) { mViewRect = rect; return true; } } return false; } //------------------------------------------------------------------------ bool BaseEditorWrapper::_open (void* ptr) { createView (); if (mView) { #if SMTG_OS_WINDOWS FIDString type = kPlatformTypeHWND; #elif SMTG_OS_MACOS && SMTG_PLATFORM_64 FIDString type = kPlatformTypeNSView; #elif SMTG_OS_MACOS FIDString type = kPlatformTypeHIView; #endif return mView->attached (ptr, type) == kResultTrue; } return false; } //------------------------------------------------------------------------ void BaseEditorWrapper::_close () { if (mView) { mView->setFrame (nullptr); mView->removed (); mView = nullptr; } } //------------------------------------------------------------------------ bool BaseEditorWrapper::_setKnobMode (Vst::KnobMode val) { bool result = false; auto editController2 = U::cast (mController); if (editController2) result = editController2->setKnobMode (val) == kResultTrue; return result; } //------------------------------------------------------------------------ // MemoryStream with attributes to add information "preset or project" //------------------------------------------------------------------------ class VstPresetStream : public MemoryStream, Vst::IStreamAttributes { public: VstPresetStream () {} VstPresetStream (void* memory, TSize memorySize) : MemoryStream (memory, memorySize) {} //---from Vst::IStreamAttributes----- tresult PLUGIN_API getFileName (String128 /*name*/) SMTG_OVERRIDE { return kNotImplemented; } IAttributeList* PLUGIN_API getAttributes () SMTG_OVERRIDE { return attrList; } //------------------------------------------------------------------------ DELEGATE_REFCOUNT (MemoryStream) tresult PLUGIN_API queryInterface (const TUID iid, void** obj) SMTG_OVERRIDE { QUERY_INTERFACE (iid, obj, IStreamAttributes::iid, IStreamAttributes) return MemoryStream::queryInterface (iid, obj); } protected: IPtr attrList {HostAttributeList::make ()}; }; //------------------------------------------------------------------------ // BaseWrapper //------------------------------------------------------------------------ BaseWrapper::BaseWrapper (SVST3Config& config) { mPlugInterfaceSupport = owned (NEW PlugInterfaceSupport); mProcessor = owned (config.processor); mController = owned (config.controller); mFactory = config.factory; // share it mVst3EffectClassID = config.vst3ComponentID; memset (mName, 0, sizeof (mName)); memset (mVendor, 0, sizeof (mVendor)); memset (mSubCategories, 0, sizeof (mSubCategories)); memset (&mProcessContext, 0, sizeof (ProcessContext)); mProcessContext.sampleRate = 44100; mProcessContext.tempo = 120; for (int32 b = 0; b < kMaxMidiMappingBusses; b++) { for (int32 i = 0; i < 16; i++) { mMidiCCMapping[b][i] = nullptr; } } for (int32 i = 0; i < kMaxProgramChangeParameters; i++) { mProgramChangeParameterIDs[i] = kNoParamId; mProgramChangeParameterIdxs[i] = -1; } } //------------------------------------------------------------------------ BaseWrapper::~BaseWrapper () { term (); } //------------------------------------------------------------------------ void BaseWrapper::term () { mTimer = nullptr; mProcessData.unprepare (); //---Disconnect components if (mComponentsConnected) { auto cp1 = U::cast (mProcessor); auto cp2 = U::cast (mController); if (cp1 && cp2) { if (mProcessorConnection) mProcessorConnection->disconnect (cp2); if (mControllerConnection) mControllerConnection->disconnect (cp1); } mComponentsConnected = false; } //---Terminate Controller Component if (mController) { mController->setComponentHandler (nullptr); if (mControllerInitialized) mController->terminate (); mControllerInitialized = false; } //---Terminate Processor Component if (mComponent && mComponentInitialized) { mComponent->terminate (); mComponentInitialized = false; } mInputEvents = nullptr; mOutputEvents = nullptr; mUnitInfo = nullptr; mMidiMapping = nullptr; if (mMidiCCMapping[0][0]) for (int32 b = 0; b < kMaxMidiMappingBusses; b++) for (int32 i = 0; i < 16; i++) { delete mMidiCCMapping[b][i]; mMidiCCMapping[b][i] = nullptr; } mEditor = nullptr; mController = nullptr; mProcessor = nullptr; mComponent = nullptr; mFactory = nullptr; mPlugInterfaceSupport = nullptr; mProcessorConnection = nullptr; mControllerConnection = nullptr; } //------------------------------------------------------------------------ bool BaseWrapper::init () { // VST 3 stuff ----------------------------------------------- if (mProcessor) mProcessor->queryInterface (IComponent::iid, (void**)&mComponent); if (mController) { mController->queryInterface (IUnitInfo::iid, (void**)&mUnitInfo); mController->queryInterface (IMidiMapping::iid, (void**)&mMidiMapping); } //---init the processor component mComponentInitialized = true; if (mComponent->initialize ((IHostApplication*)this) != kResultTrue) return false; //---init the controller component if (mController) { // do not initialize 2 times the component if it is singleComponent if (FUnknownPtr (mComponent).getInterface () != mController) { mControllerInitialized = true; if (mController->initialize ((IHostApplication*)this) != kResultTrue) return false; } //---set this class as Component Handler mController->setComponentHandler (this); //---connect the 2 components auto cp1 = U::cast (mProcessor); auto cp2 = U::cast (mController); if (cp1 && cp2) { mProcessorConnection = owned (NEW ConnectionProxy (cp1)); mProcessorConnection->connect (cp2); mControllerConnection = owned (NEW ConnectionProxy (cp2)); mControllerConnection->connect (cp1); mComponentsConnected = true; } //---inform the component "controller" with the component "processor" state MemoryStream stream; if (mComponent->getState (&stream) == kResultTrue) { stream.seek (0, IBStream::kIBSeekSet, nullptr); mController->setComponentState (&stream); } } // Wrapper ----------------------------------------------- if (mProcessor) { if (mProcessor->canProcessSampleSize (kSample64) == kResultTrue) { _canDoubleReplacing (true); // supports double precision processing // we use the 64 as default only if 32 bit not supported if (mProcessor->canProcessSampleSize (kSample32) != kResultTrue) mVst3SampleSize = kSample64; else mVst3SampleSize = kSample32; } // latency ------------------------------- _setInitialDelay (mProcessor->getLatencySamples ()); if (mProcessor->getTailSamples () == kNoTail) _noTail (true); setupProcessing (); // initialize vst3 component processing parameters } // parameters setupParameters (); // setup inputs and outputs setupBuses (); // find out programs of root unit -------------------------- mNumPrograms = 0; if (mUnitInfo) { int32 programListCount = mUnitInfo->getProgramListCount (); if (programListCount > 0) { ProgramListID rootUnitProgramListId = kNoProgramListId; for (int32 i = 0; i < mUnitInfo->getUnitCount (); i++) { UnitInfo unit = {}; if (mUnitInfo->getUnitInfo (i, unit) == kResultTrue) { if (unit.id == kRootUnitId) { rootUnitProgramListId = unit.programListId; break; } } } if (rootUnitProgramListId != kNoProgramListId) { for (int32 i = 0; i < programListCount; i++) { ProgramListInfo progList = {}; if (mUnitInfo->getProgramListInfo (i, progList) == kResultTrue) { if (progList.id == rootUnitProgramListId) { mNumPrograms = progList.programCount; break; } } } } } } if (mTimer == nullptr) mTimer = owned (Timer::create (this, 50)); initMidiCtrlerAssignment (); return true; } //------------------------------------------------------------------------ void BaseWrapper::_suspend () { _stopProcess (); if (mComponent && mActive == true) mComponent->setActive (false); mActive = false; } //------------------------------------------------------------------------ void BaseWrapper::_resume () { if (mComponent && mActive == false) mComponent->setActive (true); mActive = true; } //------------------------------------------------------------------------ void BaseWrapper::_startProcess () { if (mProcessor && mProcessing == false) { mProcessing = true; mProcessor->setProcessing (true); } } //------------------------------------------------------------------------ void BaseWrapper::_stopProcess () { if (mProcessor && mProcessing == true) { mProcessor->setProcessing (false); mProcessing = false; } } //------------------------------------------------------------------------ void BaseWrapper::_setEditor (BaseEditorWrapper* editor) { mEditor = owned (editor); } //------------------------------------------------------------------------ bool BaseWrapper::_setBlockSize (int32 newBlockSize) { if (mProcessing) return false; if (mBlockSize != newBlockSize) { mBlockSize = newBlockSize; setupProcessing (); return true; } return false; } //------------------------------------------------------------------------ bool BaseWrapper::setupProcessing (int32 processModeOverwrite) { if (!mProcessor) return false; ProcessSetup setup; if (processModeOverwrite >= 0) setup.processMode = processModeOverwrite; else setup.processMode = mVst3processMode; setup.maxSamplesPerBlock = mBlockSize; setup.sampleRate = mSampleRate; setup.symbolicSampleSize = mVst3SampleSize; return mProcessor->setupProcessing (setup) == kResultTrue; } //------------------------------------------------------------------------ bool BaseWrapper::getEditorSize (int32& width, int32& height) const { if (auto* editor = getEditor ()) { ViewRect rect; if (editor->getRect (rect)) { width = rect.right - rect.left; height = rect.bottom - rect.top; return true; } } return false; } //------------------------------------------------------------------------ float BaseWrapper::_getParameter (int32 index) const { if (!mController) return 0.f; if (index < static_cast (mParameterMap.size ())) { ParamID id = mParameterMap.at (index).vst3ID; return (float)mController->getParamNormalized (id); } return 0.f; } //------------------------------------------------------------------------ void BaseWrapper::addParameterChange (ParamID id, ParamValue value, int32 sampleOffset) { mGuiTransfer.addChange (id, value, sampleOffset); mInputTransfer.addChange (id, value, sampleOffset); } //------------------------------------------------------------------------ /*! Usually VST 2 hosts call setParameter (...) and getParameterDisplay (...) synchronously. In setParameter (...) param changes get queued (guiTransfer) and transfered in idle (::onTimer). The ::onTimer call almost always comes AFTER getParameterDisplay (...) and therefore returns an old value. To avoid sending back old values, getLastParamChange (...) returns the latest value from the guiTransfer queue. */ //------------------------------------------------------------------------ bool BaseWrapper::getLastParamChange (ParamID id, ParamValue& value) { ParameterChanges changes; mGuiTransfer.transferChangesTo (changes); for (int32 i = 0; i < changes.getParameterCount (); ++i) { IParamValueQueue* queue = changes.getParameterData (i); if (queue) { if (queue->getParameterId () == id) { int32 points = queue->getPointCount (); if (points > 0) { mGuiTransfer.transferChangesFrom (changes); int32 sampleOffset = 0; return queue->getPoint (points - 1, sampleOffset, value) == kResultTrue; } } } } mGuiTransfer.transferChangesFrom (changes); return false; } //------------------------------------------------------------------------ void BaseWrapper::getUnitPath (UnitID unitID, String& path) const { if (!mUnitInfo) return; //! Build the unit path up to the root unit (e.g. "Modulators.LFO 1.". Separator is a ".") for (int32 unitIndex = 0; unitIndex < mUnitInfo->getUnitCount (); ++unitIndex) { UnitInfo info = {}; mUnitInfo->getUnitInfo (unitIndex, info); if (info.id == unitID) { String unitName (info.name); unitName.append ("."); path.insertAt (0, unitName); if (info.parentUnitId != kRootUnitId) getUnitPath (info.parentUnitId, path); break; } } } //------------------------------------------------------------------------ int32 BaseWrapper::_getChunk (void** data, bool /*isPreset*/) { // Host stores plug-in state. Returns the size in bytes of the chunk (Plug-in allocates the data // array) MemoryStream componentStream; if (mComponent && mComponent->getState (&componentStream) != kResultTrue) componentStream.setSize (0); MemoryStream controllerStream; if (mController && mController->getState (&controllerStream) != kResultTrue) controllerStream.setSize (0); if (componentStream.getSize () + controllerStream.getSize () == 0) return 0; mChunk.setSize (0); IBStreamer acc (&mChunk, kLittleEndian); acc.writeInt64 (componentStream.getSize ()); acc.writeInt64 (controllerStream.getSize ()); acc.writeRaw (componentStream.getData (), static_cast (componentStream.getSize ())); acc.writeRaw (controllerStream.getData (), static_cast (controllerStream.getSize ())); auto chunkSize = static_cast (mChunk.getSize ()); *data = mChunk.getData (); return chunkSize; } //------------------------------------------------------------------------ int32 BaseWrapper::_setChunk (void* data, int32 byteSize, bool isPreset) { if (!mComponent) return 0; // throw away all previously queued parameter changes, they are obsolete mGuiTransfer.removeChanges (); mInputTransfer.removeChanges (); MemoryStream chunk (data, byteSize); IBStreamer acc (&chunk, kLittleEndian); int64 componentDataSize = 0; int64 controllerDataSize = 0; acc.readInt64 (componentDataSize); acc.readInt64 (controllerDataSize); VstPresetStream componentStream (((char*)data) + acc.tell (), componentDataSize); VstPresetStream controllerStream (((char*)data) + acc.tell () + componentDataSize, controllerDataSize); if (!isPreset) { if (Vst::IAttributeList* attr = componentStream.getAttributes ()) attr->setString (Vst::PresetAttributes::kStateType, String (Vst::StateType::kProject)); if (Vst::IAttributeList* attr = controllerStream.getAttributes ()) attr->setString (Vst::PresetAttributes::kStateType, String (Vst::StateType::kProject)); } mComponent->setState (&componentStream); componentStream.seek (0, IBStream::kIBSeekSet, nullptr); if (mController) { mController->setComponentState (&componentStream); mController->setState (&controllerStream); } return 0; } //------------------------------------------------------------------------ bool BaseWrapper::_setBypass (bool onOff) { if (mBypassParameterID != kNoParamId) { addParameterChange (mBypassParameterID, onOff ? 1.0 : 0.0, 0); return true; } return false; } //----------------------------------------------------------------------------- bool BaseWrapper::getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId) { programListId = kNoProgramListId; unitId = -1; if (!mUnitInfo) return false; // use the first input event bus (VST 2 has only 1 bus for event) if (mUnitInfo->getUnitByBus (kEvent, kInput, 0, midiChannel, unitId) == kResultTrue) { for (int32 i = 0, unitCount = mUnitInfo->getUnitCount (); i < unitCount; i++) { UnitInfo unitInfoStruct = {}; if (mUnitInfo->getUnitInfo (i, unitInfoStruct) == kResultTrue) { if (unitId == unitInfoStruct.id) { programListId = unitInfoStruct.programListId; return programListId != kNoProgramListId; } } } } return false; } //----------------------------------------------------------------------------- bool BaseWrapper::getProgramListInfoByProgramListID (ProgramListID programListId, ProgramListInfo& info) { if (mUnitInfo) { int32 programListCount = mUnitInfo->getProgramListCount (); for (int32 i = 0; i < programListCount; i++) { memset (&info, 0, sizeof (ProgramListInfo)); if (mUnitInfo->getProgramListInfo (i, info) == kResultTrue) { if (info.id == programListId) { return true; } } } } return false; } //----------------------------------------------------------------------------- void BaseWrapper::setVendorName (char* name) { memcpy (mVendor, name, sizeof (mVendor)); } //----------------------------------------------------------------------------- void BaseWrapper::setEffectName (char* effectName) { memcpy (mName, effectName, sizeof (mName)); } //----------------------------------------------------------------------------- void BaseWrapper::setEffectVersion (char* version) { if (!version) mVersion = 0; else { long major = 1; long minor = 0; long subminor = 0; long subsubminor = 0; int32 ret = sscanf (version, "%ld.%ld.%ld.%ld", &major, &minor, &subminor, &subsubminor); mVersion = static_cast ((major & 0xff) << 24); if (ret > 3) mVersion += static_cast (subsubminor & 0xff); if (ret > 2) mVersion += static_cast ((subminor & 0xff) << 8); if (ret > 1) mVersion += static_cast ((minor & 0xff) << 16); } } //----------------------------------------------------------------------------- void BaseWrapper::setSubCategories (char* string) { memcpy (mSubCategories, string, sizeof (mSubCategories)); } //----------------------------------------------------------------------------- void BaseWrapper::setupBuses () { if (!mComponent) return; mProcessData.prepare (*mComponent, 0, mVst3SampleSize); _setNumInputs (countMainBusChannels (kInput, mMainAudioInputBuses)); _setNumOutputs (countMainBusChannels (kOutput, mMainAudioOutputBuses)); mHasEventInputBuses = mComponent->getBusCount (kEvent, kInput) > 0; mHasEventOutputBuses = mComponent->getBusCount (kEvent, kOutput) > 0; if (mHasEventInputBuses) { if (mInputEvents == nullptr) mInputEvents = owned (new EventList (kMaxEvents)); } else mInputEvents = nullptr; if (mHasEventOutputBuses) { if (mOutputEvents == nullptr) mOutputEvents = owned (new EventList (kMaxEvents)); } else mOutputEvents = nullptr; } //----------------------------------------------------------------------------- void BaseWrapper::setupParameters () { mParameterMap.clear (); mParamIndexMap.clear (); mBypassParameterID = mProgramParameterID = kNoParamId; mProgramParameterIdx = -1; std::vector programParameterInfos; std::vector programParameterIdxs; int32 paramCount = mController ? mController->getParameterCount () : 0; int32 numParamID = 0; for (int32 i = 0; i < paramCount; i++) { ParameterInfo paramInfo = {}; if (mController->getParameterInfo (i, paramInfo) == kResultTrue) { //--- ------------------------------------------ if ((paramInfo.flags & ParameterInfo::kIsBypass) != 0) { if (mBypassParameterID == kNoParamId) mBypassParameterID = paramInfo.id; if (mUseExportedBypass) { ParamMapEntry entry = {paramInfo.id, i}; mParameterMap.push_back (entry); mParamIndexMap[paramInfo.id] = mUseIncIndex ? numParamID : i; numParamID++; } } //--- ------------------------------------------ else if ((paramInfo.flags & ParameterInfo::kIsProgramChange) != 0) { programParameterInfos.push_back (paramInfo); programParameterIdxs.push_back (i); if (paramInfo.unitId == kRootUnitId) { if (mProgramParameterID == kNoParamId) { mProgramParameterID = paramInfo.id; mProgramParameterIdx = i; } } if (gExportProgramChangeParameters == true) { ParamMapEntry entry = {paramInfo.id, i}; mParameterMap.push_back (entry); mParamIndexMap[paramInfo.id] = mUseIncIndex ? numParamID : i; numParamID++; } } //--- ------------------------------------------ // do not export read only parameters else if ((paramInfo.flags & ParameterInfo::kIsReadOnly) == 0) { ParamMapEntry entry = {paramInfo.id, i}; mParameterMap.push_back (entry); mParamIndexMap[paramInfo.id] = mUseIncIndex ? numParamID : i; numParamID++; } } } mNumParams = static_cast (mParameterMap.size ()); mInputTransfer.setMaxParameters (paramCount); mOutputTransfer.setMaxParameters (paramCount); mGuiTransfer.setMaxParameters (paramCount); mInputChanges.setMaxParameters (paramCount); mOutputChanges.setMaxParameters (paramCount); for (int32 midiChannel = 0; midiChannel < kMaxProgramChangeParameters; midiChannel++) { mProgramChangeParameterIDs[midiChannel] = kNoParamId; mProgramChangeParameterIdxs[midiChannel] = -1; UnitID unitId; ProgramListID programListId; if (getProgramListAndUnit (midiChannel, unitId, programListId)) { for (uint32 i = 0; i < programParameterInfos.size (); i++) { const ParameterInfo& paramInfo = programParameterInfos.at (i); if (paramInfo.unitId == unitId) { mProgramChangeParameterIDs[midiChannel] = paramInfo.id; mProgramChangeParameterIdxs[midiChannel] = programParameterIdxs.at (i); break; } } } } } //----------------------------------------------------------------------------- void BaseWrapper::initMidiCtrlerAssignment () { if (!mMidiMapping || !mComponent) return; int32 busses = Min (mComponent->getBusCount (kEvent, kInput), kMaxMidiMappingBusses); if (!mMidiCCMapping[0][0]) { for (int32 b = 0; b < busses; b++) for (int32 i = 0; i < 16; i++) mMidiCCMapping[b][i] = NEW ParamID[Vst::kCountCtrlNumber]; } ParamID paramID; for (int32 b = 0; b < busses; b++) { for (int16 ch = 0; ch < 16; ch++) { for (int32 i = 0; i < Vst::kCountCtrlNumber; i++) { paramID = kNoParamId; if (mMidiMapping->getMidiControllerAssignment (b, ch, (CtrlNumber)i, paramID) == kResultTrue) { // TODO check if tag is associated to a parameter mMidiCCMapping[b][ch][i] = paramID; } else mMidiCCMapping[b][ch][i] = kNoParamId; } } } } //----------------------------------------------------------------------------- void BaseWrapper::_setSampleRate (float newSamplerate) { if (mProcessing) return; if (newSamplerate != mSampleRate) { mSampleRate = newSamplerate; setupProcessing (); } } //----------------------------------------------------------------------------- uint32 BaseWrapper::countMainBusChannels (BusDirection dir, uint64& mainBusBitset) { uint32 result = 0; mainBusBitset = 0; int32 busCount = mComponent->getBusCount (kAudio, dir); for (int32 i = 0; i < busCount; i++) { BusInfo busInfo = {}; if (mComponent->getBusInfo (kAudio, dir, i, busInfo) == kResultTrue) { if (busInfo.busType == kMain) { result += busInfo.channelCount; mainBusBitset |= (uint64 (1) << i); mComponent->activateBus (kAudio, dir, i, true); } else if (busInfo.flags & BusInfo::kDefaultActive) { mComponent->activateBus (kAudio, dir, i, false); } } } return result; } //----------------------------------------------------------------------------- void BaseWrapper::processMidiEvent (Event& toAdd, char* midiData, bool isLive, int32 noteLength, float noteOffVelocity, float detune) { uint8 status = static_cast (midiData[0] & kStatusMask); uint8 channel = static_cast (midiData[0] & kChannelMask); // not allowed if (channel >= 16) return; if (isLive) toAdd.flags |= Event::kIsLive; //--- ----------------------------- switch (status) { case kNoteOn: case kNoteOff: { if (status == kNoteOff || midiData[2] == 0) // note off { toAdd.type = Event::kNoteOffEvent; toAdd.noteOff.channel = channel; toAdd.noteOff.pitch = midiData[1]; toAdd.noteOff.velocity = noteOffVelocity; toAdd.noteOff.noteId = -1; // TODO ? } else if (status == kNoteOn) // note on { toAdd.type = Event::kNoteOnEvent; toAdd.noteOn.channel = channel; toAdd.noteOn.pitch = midiData[1]; toAdd.noteOn.tuning = detune; toAdd.noteOn.velocity = (float)midiData[2] * kMidiScaler; toAdd.noteOn.length = noteLength; toAdd.noteOn.noteId = -1; // TODO ? } mInputEvents->addEvent (toAdd); } break; //--- ----------------------------- case kPolyPressure: { toAdd.type = Vst::Event::kPolyPressureEvent; toAdd.polyPressure.channel = channel; toAdd.polyPressure.pitch = static_cast(midiData[1] & kDataMask); toAdd.polyPressure.pressure = (float)(midiData[2] & kDataMask) * kMidiScaler; toAdd.polyPressure.noteId = -1; // TODO ? mInputEvents->addEvent (toAdd); } break; //--- ----------------------------- case kController: { if (toAdd.busIndex < kMaxMidiMappingBusses && mMidiCCMapping[toAdd.busIndex][channel]) { ParamID paramID = mMidiCCMapping[toAdd.busIndex][channel][static_cast (midiData[1])]; if (paramID != kNoParamId) { ParamValue value = (double)midiData[2] * kMidiScaler; int32 index = 0; if (IParamValueQueue* queue = mInputChanges.addParameterData (paramID, index)) queue->addPoint (toAdd.sampleOffset, value, index); mGuiTransfer.addChange (paramID, value, toAdd.sampleOffset); } } } break; //--- ----------------------------- case kPitchBendStatus: { if (toAdd.busIndex < kMaxMidiMappingBusses && mMidiCCMapping[toAdd.busIndex][channel]) { ParamID paramID = mMidiCCMapping[toAdd.busIndex][channel][Vst::kPitchBend]; if (paramID != kNoParamId) { const double kPitchWheelScaler = 1. / (double)0x3FFF; const int32 ctrl = static_cast ((midiData[1] & kDataMask) | ((midiData[2] & kDataMask) << 7)); ParamValue value = kPitchWheelScaler * (double)ctrl; int32 index = 0; if (IParamValueQueue* queue = mInputChanges.addParameterData (paramID, index)) queue->addPoint (toAdd.sampleOffset, value, index); mGuiTransfer.addChange (paramID, value, toAdd.sampleOffset); } } } break; //--- ----------------------------- case kAfterTouchStatus: { if (toAdd.busIndex < kMaxMidiMappingBusses && mMidiCCMapping[toAdd.busIndex][channel]) { ParamID paramID = mMidiCCMapping[toAdd.busIndex][channel][Vst::kAfterTouch]; if (paramID != kNoParamId) { ParamValue value = (ParamValue) (midiData[1] & kDataMask) * kMidiScaler; int32 index = 0; if (IParamValueQueue* queue = mInputChanges.addParameterData (paramID, index)) queue->addPoint (toAdd.sampleOffset, value, index); mGuiTransfer.addChange (paramID, value, toAdd.sampleOffset); } } } break; //--- ----------------------------- case kProgramChangeStatus: { if (mProgramChangeParameterIDs[channel] != kNoParamId && mProgramChangeParameterIdxs[channel] != -1) { ParameterInfo paramInfo = {}; if (mController->getParameterInfo (mProgramChangeParameterIdxs[channel], paramInfo) == kResultTrue) { int32 program = midiData[1]; if (paramInfo.stepCount > 0 && program <= paramInfo.stepCount) { ParamValue normalized = (ParamValue)program / (ParamValue)paramInfo.stepCount; addParameterChange (mProgramChangeParameterIDs[channel], normalized, toAdd.sampleOffset); } } } } break; } } //----------------------------------------------------------------------------- template inline void BaseWrapper::setProcessingBuffers (T** inputs, T** outputs) { // set processing buffers int32 sourceIndex = 0; for (int32 i = 0; i < mProcessData.numInputs; i++) { AudioBusBuffers& buffers = mProcessData.inputs[i]; if (mMainAudioInputBuses & (uint64 (1) << i)) { for (int32 j = 0; j < buffers.numChannels; j++) { buffers.channelBuffers32[j] = (Sample32*)inputs[sourceIndex++]; } } else buffers.silenceFlags = HostProcessData::kAllChannelsSilent; } sourceIndex = 0; for (int32 i = 0; i < mProcessData.numOutputs; i++) { AudioBusBuffers& buffers = mProcessData.outputs[i]; buffers.silenceFlags = 0; if (mMainAudioOutputBuses & (uint64 (1) << i)) { for (int32 j = 0; j < buffers.numChannels; j++) { buffers.channelBuffers32[j] = (Sample32*)outputs[sourceIndex++]; } } } } //----------------------------------------------------------------------------- inline void BaseWrapper::setEventPPQPositions () { if (!mInputEvents) return; int32 eventCount = mInputEvents->getEventCount (); if (eventCount > 0 && (mProcessContext.state & ProcessContext::kTempoValid) && (mProcessContext.state & ProcessContext::kProjectTimeMusicValid)) { TQuarterNotes projectTimeMusic = mProcessContext.projectTimeMusic; double secondsToQuarterNoteScaler = mProcessContext.tempo / 60.0; double multiplicator = secondsToQuarterNoteScaler / mSampleRate; for (int32 i = 0; i < eventCount; i++) { Event* e = mInputEvents->getEventByIndex (i); if (e) { TQuarterNotes localTimeMusic = e->sampleOffset * multiplicator; e->ppqPosition = projectTimeMusic + localTimeMusic; } } } } //----------------------------------------------------------------------------- inline void BaseWrapper::doProcess (int32 sampleFrames) { if (!mProcessor) return; mProcessData.numSamples = sampleFrames; if (mProcessing == false) _startProcess (); mProcessData.inputEvents = mInputEvents; mProcessData.outputEvents = mOutputEvents; setupProcessTimeInfo (); setEventPPQPositions (); mInputTransfer.transferChangesTo (mInputChanges); mProcessData.inputParameterChanges = &mInputChanges; mProcessData.outputParameterChanges = &mOutputChanges; mOutputChanges.clearQueue (); // VST 3 process call mProcessor->process (mProcessData); processOutputParametersChanges (); mOutputTransfer.transferChangesFrom (mOutputChanges); processOutputEvents (); // clear input parameters and events mInputChanges.clearQueue (); if (mInputEvents) mInputEvents->clear (); } //----------------------------------------------------------------------------- void BaseWrapper::_processReplacing (float** inputs, float** outputs, int32 sampleFrames) { if (mProcessData.symbolicSampleSize != kSample32) return; setProcessingBuffers (inputs, outputs); doProcess (sampleFrames); } //----------------------------------------------------------------------------- void BaseWrapper::_processDoubleReplacing (double** inputs, double** outputs, int32 sampleFrames) { if (mProcessData.symbolicSampleSize != kSample64) return; setProcessingBuffers (inputs, outputs); doProcess (sampleFrames); } //----------------------------------------------------------------------------- void BaseWrapper::onTimer (Timer*) { if (!mController) return; ParamID id; ParamValue value; int32 sampleOffset; while (mOutputTransfer.getNextChange (id, value, sampleOffset)) { mController->setParamNormalized (id, value); } while (mGuiTransfer.getNextChange (id, value, sampleOffset)) { mController->setParamNormalized (id, value); } } // FUnknown //----------------------------------------------------------------------------- tresult PLUGIN_API BaseWrapper::queryInterface (const char* iid, void** obj) { QUERY_INTERFACE (iid, obj, FUnknown::iid, Vst::IHostApplication) QUERY_INTERFACE (iid, obj, Vst::IHostApplication::iid, Vst::IHostApplication) QUERY_INTERFACE (iid, obj, Vst::IComponentHandler::iid, Vst::IComponentHandler) QUERY_INTERFACE (iid, obj, Vst::IUnitHandler::iid, Vst::IUnitHandler) if (mPlugInterfaceSupport && mPlugInterfaceSupport->queryInterface (iid, obj) == kResultTrue) return ::Steinberg::kResultOk; *obj = nullptr; return kNoInterface; } //----------------------------------------------------------------------------- tresult PLUGIN_API BaseWrapper::restartComponent (int32 flags) { tresult result = kResultFalse; //--- ---------------------- if (flags & kIoChanged) { setupBuses (); _ioChanged (); result = kResultTrue; } //--- ---------------------- if ((flags & kParamValuesChanged) || (flags & kParamTitlesChanged)) { _updateDisplay (); result = kResultTrue; } //--- ---------------------- if (flags & kLatencyChanged) { if (mProcessor) _setInitialDelay (mProcessor->getLatencySamples ()); _ioChanged (); result = kResultTrue; } //--- ---------------------- if (flags & kMidiCCAssignmentChanged) { initMidiCtrlerAssignment (); result = kResultTrue; } // kReloadComponent is Not supported return result; } //----------------------------------------------------------------------------- // IHostApplication //----------------------------------------------------------------------------- tresult PLUGIN_API BaseWrapper::createInstance (TUID cid, TUID iid, void** obj) { FUID classID (FUID::fromTUID (cid)); FUID interfaceID (FUID::fromTUID (iid)); if (classID == IMessage::iid && interfaceID == IMessage::iid) { *obj = new HostMessage; return kResultTrue; } else if (classID == IAttributeList::iid && interfaceID == IAttributeList::iid) { if (auto al = HostAttributeList::make ()) { *obj = al.take (); return kResultTrue; } return kOutOfMemory; } *obj = nullptr; return kResultFalse; } //----------------------------------------------------------------------------- // IUnitHandler //----------------------------------------------------------------------------- tresult PLUGIN_API BaseWrapper::notifyUnitSelection (UnitID /*unitId*/) { return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API BaseWrapper::notifyProgramListChange (ProgramListID /*listId*/, int32 /*programIndex*/) { // TODO -> redirect to hasMidiProgramsChanged somehow... return kResultTrue; } //----------------------------------------------------------------------------- } // namespace Vst } // namespace Steinberg //----------------------------------------------------------------------------- /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/basewrapper/PaxHeaders/basewrapper.h0000644000000000000000000000013015124701711025350 xustar0029 mtime=1767080905.28072064 30 atime=1767080905.280506322 29 ctime=1767080905.28072064 qtractor-1.5.11/src/vst3/public.sdk/source/vst/basewrapper/basewrapper.h0000644000175000001440000002254015124701711025345 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/basewrapper/basewrapper.h // Created by : Steinberg, 01/2018 // Description : VST 3 -> XXX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once /// \cond ignore #include "pluginterfaces/base/ftypes.h" #include "pluginterfaces/gui/iplugview.h" #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivsteditcontroller.h" #include "pluginterfaces/vst/ivsthostapplication.h" #include "pluginterfaces/vst/ivstprocesscontext.h" #include "pluginterfaces/vst/ivstunits.h" #include "public.sdk/source/common/memorystream.h" #include "public.sdk/source/vst/hosting/eventlist.h" #include "public.sdk/source/vst/hosting/parameterchanges.h" #include "public.sdk/source/vst/hosting/pluginterfacesupport.h" #include "public.sdk/source/vst/hosting/processdata.h" #include "base/source/fstring.h" #include "base/source/timer.h" #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ class BaseEditorWrapper : public IPlugFrame, public FObject { public: //------------------------------------------------------------------------ BaseEditorWrapper (IEditController* controller); ~BaseEditorWrapper () override; static bool hasEditor (IEditController* controller); bool getRect (ViewRect& rect); virtual bool _open (void* ptr); virtual void _close (); bool _setKnobMode (Vst::KnobMode val); // IPlugFrame tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) SMTG_OVERRIDE; // FUnknown tresult PLUGIN_API queryInterface (const char* _iid, void** obj) SMTG_OVERRIDE; REFCOUNT_METHODS (FObject); //------------------------------------------------------------------------ protected: void createView (); IPtr mController; IPtr mView; ViewRect mViewRect; }; //------------------------------------------------------------------------ const int32 kMaxEvents = 2048; class ConnectionProxy; //------------------------------------------------------------------------------------------------------- class BaseWrapper : public IHostApplication, public IComponentHandler, public IUnitHandler, public ITimerCallback, public FObject { public: struct SVST3Config { IPluginFactory* factory = nullptr; IAudioProcessor* processor = nullptr; IEditController* controller = nullptr; FUID vst3ComponentID; }; BaseWrapper (SVST3Config& config); ~BaseWrapper () override; virtual bool init (); virtual void _canDoubleReplacing (bool /*val*/) {} virtual void _setInitialDelay (uint32 /*delay*/) {} virtual void _noTail (bool /*val*/) {} virtual void _ioChanged () {} virtual void _updateDisplay () {} virtual void _setNumInputs (uint32 inputs) { mNumInputs = inputs; } virtual void _setNumOutputs (uint32 outputs) { mNumOutputs = outputs; } virtual bool _sizeWindow (int32 width, int32 height) = 0; virtual int32 _getChunk (void** data, bool isPreset); virtual int32 _setChunk (void* data, int32 byteSize, bool isPreset); virtual bool getEditorSize (int32& width, int32& height) const; bool isActive () const { return mActive; } uint32 getNumInputs () const { return mNumInputs; } uint32 getNumOutputs () const { return mNumOutputs; } BaseEditorWrapper* getEditor () const { return mEditor; } //--- --------------------------------------------------------------------- // VST 3 Interfaces ------------------------------------------------------ // FUnknown tresult PLUGIN_API queryInterface (const char* iid, void** obj) SMTG_OVERRIDE; REFCOUNT_METHODS (FObject); // IHostApplication tresult PLUGIN_API createInstance (TUID cid, TUID iid, void** obj) SMTG_OVERRIDE; // IComponentHandler tresult PLUGIN_API restartComponent (int32 flags) SMTG_OVERRIDE; // IUnitHandler tresult PLUGIN_API notifyUnitSelection (UnitID unitId) SMTG_OVERRIDE; tresult PLUGIN_API notifyProgramListChange (ProgramListID listId, int32 programIndex) SMTG_OVERRIDE; // ITimer void onTimer (Timer* timer) SMTG_OVERRIDE; //------------------------------------------------------------------------------------------------------- protected: void term (); virtual void setupParameters (); virtual void setupProcessTimeInfo () = 0; virtual void processOutputEvents () {} virtual void processOutputParametersChanges () {} void _setSampleRate (float newSamplerate); bool setupProcessing (int32 processModeOverwrite = -1); void _processReplacing (float** inputs, float** outputs, int32 sampleFrames); void _processDoubleReplacing (double** inputs, double** outputs, int32 sampleFrames); template void setProcessingBuffers (T** inputs, T** outputs); void doProcess (int32 sampleFrames); void processMidiEvent (Event& toAdd, char* midiData, bool isLive = false, int32 noteLength = 0, float noteOffVelocity = 1.f, float detune = 0.f); void setEventPPQPositions (); void _setEditor (BaseEditorWrapper* editor); bool _setBlockSize (int32 newBlockSize); float _getParameter (int32 index) const; void _suspend (); void _resume (); void _startProcess (); void _stopProcess (); bool _setBypass (bool onOff); virtual void setupBuses (); void initMidiCtrlerAssignment (); void getUnitPath (UnitID unitID, String& path) const; uint32 countMainBusChannels (BusDirection dir, uint64& mainBusBitset); /** Returns the last param change from guiTransfer queue. */ bool getLastParamChange (ParamID id, ParamValue& value); void addParameterChange (ParamID id, ParamValue value, int32 sampleOffset); void setVendorName (char* name); void setEffectName (char* name); void setEffectVersion (char* version); void setSubCategories (char* string); bool getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId); bool getProgramListInfoByProgramListID (ProgramListID programListId, ProgramListInfo& info); static const int32 kMaxProgramChangeParameters = 16; ParamID mProgramChangeParameterIDs[kMaxProgramChangeParameters]; // for each MIDI channel int32 mProgramChangeParameterIdxs[kMaxProgramChangeParameters]; // for each MIDI channel FUID mVst3EffectClassID; // vst3 data IPtr mProcessor; IPtr mComponent; IPtr mController; IPtr mUnitInfo; IPtr mMidiMapping; IPtr mEditor; IPtr mPlugInterfaceSupport; IPtr mProcessorConnection; IPtr mControllerConnection; int32 mVst3SampleSize = kSample32; int32 mVst3processMode = kRealtime; char mName[PClassInfo::kNameSize]; char mVendor[PFactoryInfo::kNameSize]; char mSubCategories[PClassInfo2::kSubCategoriesSize]; int32 mVersion = 0; struct ParamMapEntry { ParamID vst3ID; int32 vst3Index; }; std::vector mParameterMap; std::map mParamIndexMap; ParamID mBypassParameterID = kNoParamId; ParamID mProgramParameterID = kNoParamId; int32 mProgramParameterIdx = -1; HostProcessData mProcessData; ProcessContext mProcessContext; ParameterChanges mInputChanges; ParameterChanges mOutputChanges; IPtr mInputEvents; IPtr mOutputEvents; uint64 mMainAudioInputBuses = 0; uint64 mMainAudioOutputBuses = 0; ParameterChangeTransfer mInputTransfer; ParameterChangeTransfer mOutputTransfer; ParameterChangeTransfer mGuiTransfer; MemoryStream mChunk; IPtr mTimer; IPtr mFactory; int32 mNumPrograms {0}; float mSampleRate {44100}; int32 mBlockSize {256}; int32 mNumParams {0}; int32 mCurProgram {-1}; uint32 mNumInputs {0}; uint32 mNumOutputs {0}; enum { kMaxMidiMappingBusses = 4 }; ParamID* mMidiCCMapping[kMaxMidiMappingBusses][16]; bool mComponentInitialized = false; bool mControllerInitialized = false; bool mComponentsConnected = false; bool mUseExportedBypass = true; bool mActive = false; bool mProcessing = false; bool mHasEventInputBuses = false; bool mHasEventOutputBuses = false; bool mUseIncIndex = true; }; const uint8 kNoteOff = 0x80; ///< note, off velocity const uint8 kNoteOn = 0x90; ///< note, on velocity const uint8 kPolyPressure = 0xA0; ///< note, pressure const uint8 kController = 0xB0; ///< controller, value const uint8 kProgramChangeStatus = 0xC0; ///< program change const uint8 kAfterTouchStatus = 0xD0; ///< channel pressure const uint8 kPitchBendStatus = 0xE0; ///< lsb, msb const float kMidiScaler = 1.f / 127.f; static const uint8 kChannelMask = 0x0F; static const uint8 kStatusMask = 0xF0; static const uint32 kDataMask = 0x7F; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg /// \endcond qtractor-1.5.11/src/vst3/public.sdk/source/vst/basewrapper/PaxHeaders/basewrapper.sdk.cpp0000644000000000000000000000012715124701711026471 xustar0029 mtime=1767080905.28072064 29 atime=1767080905.28072064 29 ctime=1767080905.28072064 qtractor-1.5.11/src/vst3/public.sdk/source/vst/basewrapper/basewrapper.sdk.cpp0000644000175000001440000000236215124701711026460 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/basewrapper/basewrapper.sdk.cpp // Created by : Steinberg, 05/2018 // Description : VST 3 -> XXX Wrapper // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/common/memorystream.cpp" #include "public.sdk/source/vst/basewrapper/basewrapper.cpp" #include "public.sdk/source/vst/hosting/connectionproxy.cpp" #include "public.sdk/source/vst/hosting/eventlist.cpp" #include "public.sdk/source/vst/hosting/hostclasses.cpp" #include "public.sdk/source/vst/hosting/parameterchanges.cpp" #include "public.sdk/source/vst/hosting/pluginterfacesupport.cpp" #include "public.sdk/source/vst/hosting/processdata.cpp" qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstguieditor.h0000644000000000000000000000013015124701711023252 xustar0029 mtime=1767080905.28721084 30 atime=1767080905.286210845 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstguieditor.h0000644000175000001440000000710415124701711023246 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstguieditor.h // Created by : Steinberg, 04/2005 // Description : VSTGUI Editor // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "vsteditcontroller.h" #include "vstgui/vstgui.h" #if VSTGUI_VERSION_MAJOR < 4 #include "vstgui/cvstguitimer.h" #define VSTGUI_INT32 long #else #define VSTGUI_INT32 int32_t #endif namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Base class for an edit view using VSTGUI. \ingroup vstClasses */ class VSTGUIEditor : public EditorView, public VSTGUI::VSTGUIEditorInterface, public VSTGUI::CBaseObject { public: /** Constructor. */ VSTGUIEditor (void* controller, ViewRect* size = nullptr); /** Destructor. */ ~VSTGUIEditor () override; //---Internal function----- /** Called when the editor will be opened. */ #if VSTGUI_VERSION_MAJOR >= 4 && VSTGUI_VERSION_MINOR >= 1 virtual bool PLUGIN_API open (void* parent, const VSTGUI::PlatformType& platformType) = 0; #else virtual bool PLUGIN_API open (void* parent) = 0; #endif /** Called when the editor will be closed. */ virtual void PLUGIN_API close () = 0; /** Sets the idle rate controlling the parameter update rate. */ void setIdleRate (int32 millisec); //---from CBaseObject--------------- VSTGUI::CMessageResult notify (VSTGUI::CBaseObject* sender, const char* message) SMTG_OVERRIDE; void forget () SMTG_OVERRIDE { EditorView::release (); } void remember () SMTG_OVERRIDE { EditorView::addRef (); } VSTGUI_INT32 getNbReference () const SMTG_OVERRIDE { return refCount; } //---from IPlugView------- tresult PLUGIN_API isPlatformTypeSupported (FIDString type) SMTG_OVERRIDE; tresult PLUGIN_API onSize (ViewRect* newSize) SMTG_OVERRIDE; //---from VSTGUIEditorInterface------- /** Called from VSTGUI when a user begins editing. The default implementation calls performEdit of the EditController. */ void beginEdit (VSTGUI_INT32 index) SMTG_OVERRIDE; /** Called from VSTGUI when a user ends editing. The default implementation calls endEdit of the EditController. */ void endEdit (VSTGUI_INT32 index) SMTG_OVERRIDE; VSTGUI_INT32 getKnobMode () const SMTG_OVERRIDE; OBJ_METHODS (VSTGUIEditor, EditorView) DEFINE_INTERFACES END_DEFINE_INTERFACES (EditorView) REFCOUNT_METHODS (EditorView) #if TARGET_OS_IPHONE static void setBundleRef (/*CFBundleRef*/ void* bundle); #endif protected: //---from IPlugView------- tresult PLUGIN_API attached (void* parent, FIDString type) SMTG_OVERRIDE; tresult PLUGIN_API removed () SMTG_OVERRIDE; tresult PLUGIN_API onKeyDown (char16 key, int16 keyMsg, int16 modifiers) SMTG_OVERRIDE; tresult PLUGIN_API onKeyUp (char16 key, int16 keyMsg, int16 modifiers) SMTG_OVERRIDE; tresult PLUGIN_API onWheel (float distance) SMTG_OVERRIDE; tresult PLUGIN_API setFrame (IPlugFrame* frame) SMTG_OVERRIDE; private: VSTGUI::CVSTGUITimer* timer; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vsthelpers.h0000644000000000000000000000012715124701711022727 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vsthelpers.h0000644000175000001440000000467615124701711022730 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vsthelpers.h // Created by : Steinberg, 11/2018 // Description : common defines // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/base/ibstream.h" #include "pluginterfaces/base/ustring.h" #include "pluginterfaces/vst/ivstattributes.h" #include "pluginterfaces/vst/vstpresetkeys.h" #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace Helpers { //------------------------------------------------------------------------ /** Helpers */ //------------------------------------------------------------------------ //------------------------------------------------------------------------ /** Retrieve from a IBStream the state type, here the StateType::kProject return kResultTrue if the state is coming from a project, return kResultFalse if the state is coming from a preset, return kNotImplemented if the host does not implement such feature */ inline tresult isProjectState (IBStream* state) { if (!state) return kInvalidArgument; auto stream = U::cast (state); if (!stream) return kNotImplemented; if (IAttributeList* list = stream->getAttributes ()) { // get the current type (project/Default..) of this state String128 string = {0}; if (list->getString (PresetAttributes::kStateType, string, 128 * sizeof (TChar)) == kResultTrue) { UString128 tmp (string); char ascii[128]; tmp.toAscii (ascii, 128); if (strncmp (ascii, StateType::kProject, strlen (StateType::kProject)) == 0) { return kResultTrue; } return kResultFalse; } } return kNotImplemented; } /**@}*/ //------------------------------------------------------------------------ } // namespace Helpers } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstaudioprocessoralgo.h0000644000000000000000000000013215124701711025165 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstaudioprocessoralgo.h0000644000175000001440000002605515124701711025165 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstaudioprocessoralgo.h // Created by : Steinberg, 04/2015 // Description : Helper algo for AudioBusBuffers // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "pluginterfaces/vst/ivstevents.h" #include "pluginterfaces/vst/ivstparameterchanges.h" #include #include namespace Steinberg { namespace Vst { //---helpers--------- /** Returns the current channelBuffers used (depending of symbolicSampleSize). */ inline void** getChannelBuffersPointer (const ProcessSetup& processSetup, const AudioBusBuffers& bufs) { if (processSetup.symbolicSampleSize == kSample32) return (void**)bufs.channelBuffers32; return (void**)bufs.channelBuffers64; } /** Returns the size in bytes of numSamples for one channel depending of symbolicSampleSize.*/ inline uint32 getSampleFramesSizeInBytes (const ProcessSetup& processSetup, int32 numSamples) { if (processSetup.symbolicSampleSize == kSample32) return numSamples * sizeof (Sample32); return numSamples * sizeof (Sample64); } /** return the bit-mask of channels for the given number of channel * for example: * numChannels = 1 => 0b0001 (binar) = 0x01 = 1 (decimal) * numChannels = 2 => 0b0011 (binar) = 0x03 = 3 (decimal) * numChannels = 6 => 0b0011 1111 (binar) = 0x3F = 63 (decimal) */ inline uint64 getChannelMask (int32 numChannels) { if (numChannels >= 64) return kMaxInt64u; return ((uint64)1 << numChannels) - 1; } namespace Algo { //------------------------------------------------------------------------ template inline void foreach (AudioBusBuffers* audioBusBuffers, int32 busCount, const T& func) { if (!audioBusBuffers) return; for (int32 busIndex = 0; busIndex < busCount; ++busIndex) { func (audioBusBuffers[busIndex]); } } //------------------------------------------------------------------------ template inline void foreach32 (AudioBusBuffers& audioBuffer, const T& func) { for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex) { if (!audioBuffer.channelBuffers32[channelIndex]) return; func (audioBuffer.channelBuffers32[channelIndex]); } } //------------------------------------------------------------------------ template inline void foreach64 (AudioBusBuffers& audioBuffer, const T& func) { for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex) { if (!audioBuffer.channelBuffers64[channelIndex]) return; func (audioBuffer.channelBuffers64[channelIndex]); } } //------------------------------------------------------------------------ template inline void foreach32 (AudioBusBuffers& buffer1, AudioBusBuffers& buffer2, const T& func) { int32 numChannels = std::min (buffer1.numChannels, buffer2.numChannels); for (int32 channelIndex = 0; channelIndex < numChannels; ++channelIndex) { func (buffer1.channelBuffers32[channelIndex], buffer2.channelBuffers32[channelIndex], channelIndex); } } //------------------------------------------------------------------------ template inline void foreach64 (AudioBusBuffers& buffer1, AudioBusBuffers& buffer2, const T& func) { int32 numChannels = std::min (buffer1.numChannels, buffer2.numChannels); for (int32 channelIndex = 0; channelIndex < numChannels; ++channelIndex) { func (buffer1.channelBuffers64[channelIndex], buffer2.channelBuffers64[channelIndex], channelIndex); } } //------------------------------------------------------------------------ inline void copy32 (AudioBusBuffers* src, AudioBusBuffers* dest, int32 sliceSize, int32 startIndex) { if (!src || !dest) return; int32 numChannels = std::min (src->numChannels, dest->numChannels); size_t numBytes = sliceSize * sizeof (Sample32); for (int32 chIdx = 0; chIdx < numChannels; ++chIdx) { memcpy (&dest->channelBuffers32[chIdx][startIndex], src->channelBuffers32[chIdx], numBytes); } } //------------------------------------------------------------------------ inline void copy64 (AudioBusBuffers* src, AudioBusBuffers* dest, int32 sliceSize, int32 startIndex) { if (!src || !dest) return; int32 numChannels = std::min (src->numChannels, dest->numChannels); size_t numBytes = sliceSize * sizeof (Sample64); for (int32 chIdx = 0; chIdx < numChannels; ++chIdx) { memcpy (&dest->channelBuffers64[chIdx][startIndex], src->channelBuffers64[chIdx], numBytes); } } //------------------------------------------------------------------------ inline void clear32 (AudioBusBuffers* audioBusBuffers, int32 sampleCount, int32 busCount = 1) { if (!audioBusBuffers) return; const int32 numBytes = sampleCount * sizeof (Sample32); foreach (audioBusBuffers, busCount, [&] (AudioBusBuffers& audioBuffer) { foreach32 (audioBuffer, [&] (Sample32* channelBuffer) { memset (channelBuffer, 0, numBytes); }); }); } //------------------------------------------------------------------------ inline void clear64 (AudioBusBuffers* audioBusBuffers, int32 sampleCount, int32 busCount = 1) { if (!audioBusBuffers) return; const int32 numBytes = sampleCount * sizeof (Sample64); foreach (audioBusBuffers, busCount, [&] (AudioBusBuffers& audioBuffer) { foreach64 (audioBuffer, [&] (Sample64* channelBuffer) { memset (channelBuffer, 0, numBytes); }); }); } //------------------------------------------------------------------------ inline void mix32 (AudioBusBuffers& src, AudioBusBuffers& dest, int32 sampleCount) { foreach32 (src, dest, [&] (Sample32* srcBuffer, Sample32* destBuffer, int32 /*channelIndex*/) { for (int32 sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) destBuffer[sampleIndex] += srcBuffer[sampleIndex]; }); } //------------------------------------------------------------------------ inline void mix64 (AudioBusBuffers& src, AudioBusBuffers& dest, int32 sampleCount) { foreach64 (src, dest, [&] (Sample64* srcBuffer, Sample64* destBuffer, int32 /*channelIndex*/) { for (int32 sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) destBuffer[sampleIndex] += srcBuffer[sampleIndex]; }); } //------------------------------------------------------------------------ /* Multiply buffer with a constant */ template inline void multiply(T* srcBuffer, T* destBuffer, int32 sampleCount, T factor) { for (int32 sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) destBuffer[sampleIndex] = srcBuffer[sampleIndex] * factor; } //------------------------------------------------------------------------ /* Multiply all channels of AudioBusBuffer with a constant */ inline void multiply32 (AudioBusBuffers& src, AudioBusBuffers& dest, int32 sampleCount, float factor) { foreach32 (src, dest, [&](Sample32* srcBuffer, Sample32* destBuffer, int32 /*channelIndex*/) { multiply (srcBuffer, destBuffer, sampleCount, factor); }); } //------------------------------------------------------------------------ /* Multiply all channels of AudioBusBuffer with a constant */ inline void multiply64 (AudioBusBuffers& src, AudioBusBuffers& dest, int32 sampleCount, double factor) { foreach64 (src, dest, [&] (Sample64* srcBuffer, Sample64* destBuffer, int32 /*channelIndex*/) { multiply (srcBuffer, destBuffer, sampleCount, factor); }); } //------------------------------------------------------------------------ inline bool isSilent32 (AudioBusBuffers& audioBuffer, int32 sampleCount, int32 startIndex = 0) { const float epsilon = 1e-10f; // under -200dB... sampleCount += startIndex; for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex) { if (!audioBuffer.channelBuffers32[channelIndex]) return true; for (int32 sampleIndex = startIndex; sampleIndex < sampleCount; ++sampleIndex) { float val = audioBuffer.channelBuffers32[channelIndex][sampleIndex]; if (std::abs (val) > epsilon) return false; } } return true; } //------------------------------------------------------------------------ inline bool isSilent64 (AudioBusBuffers& audioBuffer, int32 sampleCount, int32 startIndex = 0) { const double epsilon = 1e-10f; // under -200dB... sampleCount += startIndex; for (int32 channelIndex = 0; channelIndex < audioBuffer.numChannels; ++channelIndex) { if (!audioBuffer.channelBuffers64[channelIndex]) return true; for (int32 sampleIndex = startIndex; sampleIndex < sampleCount; ++sampleIndex) { double val = audioBuffer.channelBuffers64[channelIndex][sampleIndex]; if (std::abs (val) > epsilon) return false; } } return true; } //------------------------------------------------------------------------ //------------------------------------------------------------------------ template inline void foreach (IEventList* eventList, const T& func) { if (!eventList) return; auto eventCount = eventList->getEventCount (); for (int32 eventIndex = 0; eventIndex < eventCount; ++eventIndex) { Vst::Event event = {}; if (eventList->getEvent (eventIndex, event) != kResultOk) continue; func (event); } } //------------------------------------------------------------------------ template inline void foreach (IParamValueQueue& paramQueue, const T& func) { auto paramId = paramQueue.getParameterId (); auto numPoints = paramQueue.getPointCount (); for (int32 pointIndex = 0; pointIndex < numPoints; ++pointIndex) { int32 sampleOffset = 0; ParamValue value = 0; if (paramQueue.getPoint (pointIndex, sampleOffset, value) != kResultOk) continue; func (paramId, sampleOffset, value); } } //------------------------------------------------------------------------ template inline void foreachLast (IParamValueQueue& paramQueue, const T& func) { auto paramId = paramQueue.getParameterId (); auto numPoints = paramQueue.getPointCount (); int32 sampleOffset = 0; ParamValue value = 0; if (paramQueue.getPoint (numPoints - 1, sampleOffset, value) == kResultOk) func (paramId, sampleOffset, value); } //------------------------------------------------------------------------ template inline void foreach (IParameterChanges* changes, const T& func) { if (!changes) return; auto paramCount = changes->getParameterCount (); for (int32 paramIndex = 0; paramIndex < paramCount; ++paramIndex) { auto paramValueQueue = changes->getParameterData (paramIndex); if (!paramValueQueue) continue; func (*paramValueQueue); } } //------------------------------------------------------------------------ } // namespace Algo } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vsteditcontroller.cpp0000644000000000000000000000013215124701711024645 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vsteditcontroller.cpp0000644000175000001440000005113415124701711024641 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vsteditcontroller.cpp // Created by : Steinberg, 04/2005 // Description : VST Edit Controller Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "public.sdk/source/vst/vsteditcontroller.h" #include "base/source/updatehandler.h" #include "pluginterfaces/base/funknownimpl.h" #include "pluginterfaces/base/ustring.h" #include namespace Steinberg { namespace Vst { KnobMode EditController::hostKnobMode = kCircularMode; //------------------------------------------------------------------------ // EditController Implementation //------------------------------------------------------------------------ EditController::EditController () { } //------------------------------------------------------------------------ tresult PLUGIN_API EditController::initialize (FUnknown* context) { return ComponentBase::initialize (context); } //------------------------------------------------------------------------ tresult PLUGIN_API EditController::terminate () { parameters.removeAll (); componentHandler.reset (); componentHandler2.reset (); return ComponentBase::terminate (); } //------------------------------------------------------------------------ tresult PLUGIN_API EditController::setComponentState (IBStream* /*state*/) { return kNotImplemented; } //------------------------------------------------------------------------ tresult PLUGIN_API EditController::setState (IBStream* /*state*/) { return kNotImplemented; } //------------------------------------------------------------------------ tresult PLUGIN_API EditController::getState (IBStream* /*state*/) { return kNotImplemented; } //------------------------------------------------------------------------ int32 PLUGIN_API EditController::getParameterCount () { return parameters.getParameterCount (); } //------------------------------------------------------------------------ tresult PLUGIN_API EditController::getParameterInfo (int32 paramIndex, ParameterInfo& info) { if (Parameter* parameter = parameters.getParameterByIndex (paramIndex)) { info = parameter->getInfo (); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API EditController::getParamStringByValue (ParamID tag, ParamValue valueNormalized, String128 string) { if (Parameter* parameter = getParameterObject (tag)) { parameter->toString (valueNormalized, string); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API EditController::getParamValueByString (ParamID tag, TChar* string, ParamValue& valueNormalized) { if (Parameter* parameter = getParameterObject (tag)) { if (parameter->fromString (string, valueNormalized)) { return kResultTrue; } } return kResultFalse; } //------------------------------------------------------------------------ ParamValue PLUGIN_API EditController::normalizedParamToPlain (ParamID tag, ParamValue valueNormalized) { if (Parameter* parameter = getParameterObject (tag)) { return parameter->toPlain (valueNormalized); } return valueNormalized; } //------------------------------------------------------------------------ ParamValue PLUGIN_API EditController::plainParamToNormalized (ParamID tag, ParamValue plainValue) { if (Parameter* parameter = getParameterObject (tag)) { return parameter->toNormalized (plainValue); } return plainValue; } //------------------------------------------------------------------------ ParamValue PLUGIN_API EditController::getParamNormalized (ParamID tag) { if (Parameter* parameter = getParameterObject (tag)) { return parameter->getNormalized (); } return 0.; } //------------------------------------------------------------------------ tresult PLUGIN_API EditController::setParamNormalized (ParamID tag, ParamValue value) { if (Parameter* parameter = getParameterObject (tag)) { parameter->setNormalized (value); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API EditController::setComponentHandler (IComponentHandler* newHandler) { if (componentHandler == newHandler) { return kResultTrue; } componentHandler = newHandler; componentHandler2.reset (); // try to get the extended version if (newHandler) { newHandler->queryInterface (IComponentHandler2::iid, (void**)&componentHandler2); } return kResultTrue; } //------------------------------------------------------------------------ tresult EditController::beginEdit (ParamID tag) { if (componentHandler) { return componentHandler->beginEdit (tag); } return kResultFalse; } //------------------------------------------------------------------------ tresult EditController::performEdit (ParamID tag, ParamValue valueNormalized) { if (componentHandler) { return componentHandler->performEdit (tag, valueNormalized); } return kResultFalse; } //------------------------------------------------------------------------ tresult EditController::endEdit (ParamID tag) { if (componentHandler) { return componentHandler->endEdit (tag); } return kResultFalse; } //------------------------------------------------------------------------ tresult EditController::startGroupEdit () { if (componentHandler2) { return componentHandler2->startGroupEdit (); } return kNotImplemented; } //------------------------------------------------------------------------ tresult EditController::finishGroupEdit () { if (componentHandler2) { return componentHandler2->finishGroupEdit (); } return kNotImplemented; } //------------------------------------------------------------------------ tresult EditController::getParameterInfoByTag (ParamID tag, ParameterInfo& info) { if (Parameter* parameter = getParameterObject (tag)) { info = parameter->getInfo (); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult EditController::setDirty (TBool state) { if (componentHandler2) { return componentHandler2->setDirty (state); } return kNotImplemented; } //------------------------------------------------------------------------ tresult EditController::requestOpenEditor (FIDString name) { if (componentHandler2) { return componentHandler2->requestOpenEditor (name); } return kNotImplemented; } #ifndef NO_PLUGUI //------------------------------------------------------------------------ // EditorView Implementation //------------------------------------------------------------------------ EditorView::EditorView (EditController* _controller, ViewRect* size) : CPluginView (size), controller (_controller) { } //------------------------------------------------------------------------ EditorView::~EditorView () { if (controller) { controller->editorDestroyed (this); controller = nullptr; } } //------------------------------------------------------------------------ void EditorView::attachedToParent () { if (controller) { controller->editorAttached (this); } } //------------------------------------------------------------------------ void EditorView::removedFromParent () { if (controller) { controller->editorRemoved (this); } } #endif // NO_PLUGUI //------------------------------------------------------------------------ // EditControllerEx1 implementation //------------------------------------------------------------------------ EditControllerEx1::EditControllerEx1 () { UpdateHandler::instance (); } //------------------------------------------------------------------------ EditControllerEx1::~EditControllerEx1 () { } //------------------------------------------------------------------------ tresult PLUGIN_API EditControllerEx1::terminate () { units.clear (); for (const auto& programList : programLists) { if (programList) programList->removeDependent (this); } programLists.clear (); programIndexMap.clear (); return EditController::terminate (); } //------------------------------------------------------------------------ bool EditControllerEx1::addUnit (Unit* unit) { units.emplace_back (unit, false); return true; } //------------------------------------------------------------------------ tresult PLUGIN_API EditControllerEx1::getUnitInfo (int32 unitIndex, UnitInfo& info /*out*/) { if (unitIndex < 0 || unitIndex >= static_cast (units.size ())) return kResultFalse; if (Unit* unit = units.at (unitIndex)) { info = unit->getInfo (); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult EditControllerEx1::notifyUnitSelection () { tresult result = kResultFalse; if (auto unitHandler = U::cast (componentHandler)) result = unitHandler->notifyUnitSelection (selectedUnit); return result; } //------------------------------------------------------------------------ bool EditControllerEx1::addProgramList (ProgramList* list) { programIndexMap[list->getID ()] = programLists.size (); programLists.emplace_back (list, false); list->addDependent (this); return true; } //------------------------------------------------------------------------ ProgramList* EditControllerEx1::getProgramList (ProgramListID listId) const { auto it = programIndexMap.find (listId); return it == programIndexMap.end () ? nullptr : programLists[it->second]; } //------------------------------------------------------------------------ tresult EditControllerEx1::notifyProgramListChange (ProgramListID listId, int32 programIndex) { tresult result = kResultFalse; if (auto unitHandler = U::cast (componentHandler)) result = unitHandler->notifyProgramListChange (listId, programIndex); return result; } //------------------------------------------------------------------------ int32 PLUGIN_API EditControllerEx1::getProgramListCount () { return static_cast (programLists.size ()); } //------------------------------------------------------------------------ tresult PLUGIN_API EditControllerEx1::getProgramListInfo (int32 listIndex, ProgramListInfo& info /*out*/) { if (listIndex < 0 || listIndex >= static_cast (programLists.size ())) return kResultFalse; info = programLists[listIndex]->getInfo (); return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API EditControllerEx1::getProgramName (ProgramListID listId, int32 programIndex, String128 name /*out*/) { ProgramIndexMap::const_iterator it = programIndexMap.find (listId); if (it != programIndexMap.end ()) { return programLists[it->second]->getProgramName (programIndex, name); } return kResultFalse; } //------------------------------------------------------------------------ tresult EditControllerEx1::setProgramName (ProgramListID listId, int32 programIndex, const String128 name /*in*/) { ProgramIndexMap::const_iterator it = programIndexMap.find (listId); if (it != programIndexMap.end ()) { return programLists[it->second]->setProgramName (programIndex, name); } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API EditControllerEx1::getProgramInfo (ProgramListID listId, int32 programIndex, CString attributeId /*in*/, String128 attributeValue /*out*/) { ProgramIndexMap::const_iterator it = programIndexMap.find (listId); if (it != programIndexMap.end ()) { return programLists[it->second]->getProgramInfo (programIndex, attributeId, attributeValue); } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API EditControllerEx1::hasProgramPitchNames (ProgramListID listId, int32 programIndex) { ProgramIndexMap::const_iterator it = programIndexMap.find (listId); if (it != programIndexMap.end ()) { return programLists[it->second]->hasPitchNames (programIndex); } return kResultFalse; } //------------------------------------------------------------------------ tresult PLUGIN_API EditControllerEx1::getProgramPitchName (ProgramListID listId, int32 programIndex, int16 midiPitch, String128 name /*out*/) { ProgramIndexMap::const_iterator it = programIndexMap.find (listId); if (it != programIndexMap.end ()) { return programLists[it->second]->getPitchName (programIndex, midiPitch, name); } return kResultFalse; } //------------------------------------------------------------------------ void PLUGIN_API EditControllerEx1::update (FUnknown* changedUnknown, int32 /*message*/) { auto* programList = FCast (changedUnknown); if (programList) { if (auto unitHandler = U::cast (componentHandler)) unitHandler->notifyProgramListChange (programList->getID (), kAllProgramInvalid); } } //------------------------------------------------------------------------ // Unit implementation //------------------------------------------------------------------------ Unit::Unit () { memset (&info, 0, sizeof (UnitInfo)); } //------------------------------------------------------------------------ Unit::Unit (const String128 name, UnitID unitId, UnitID parentUnitId, ProgramListID programListId) { setName (name); info.id = unitId; info.parentUnitId = parentUnitId; info.programListId = programListId; } //------------------------------------------------------------------------ Unit::Unit (const UnitInfo& info) : info (info) { } //------------------------------------------------------------------------ void Unit::setName (const String128 newName) { UString128 (newName).copyTo (info.name, 128); } //------------------------------------------------------------------------ // ProgramList implementation //------------------------------------------------------------------------ ProgramList::ProgramList (const String128 name, ProgramListID listId, UnitID unitId) : unitId (unitId) { UString128 (name).copyTo (info.name, 128); info.id = listId; info.programCount = 0; } //------------------------------------------------------------------------ ProgramList::ProgramList (const ProgramList& programList) : info (programList.info) , unitId (programList.unitId) , programNames (programList.programNames) { } //------------------------------------------------------------------------ int32 ProgramList::addProgram (const String128 name) { ++info.programCount; programNames.emplace_back (name); programInfos.emplace_back (); if (parameter) { static_cast (parameter)->appendString (name); } return static_cast (programNames.size ()) - 1; } //------------------------------------------------------------------------ void ProgramList::clearPrograms () { info.programCount = 0; programNames.clear (); programInfos.clear (); if (parameter) { static_cast (parameter)->clear (); } } //------------------------------------------------------------------------ bool ProgramList::setProgramInfo (int32 programIndex, CString attributeId, const String128 value) { if (programIndex >= 0 && programIndex < static_cast (programNames.size ())) { programInfos.at (programIndex).insert (std::make_pair (attributeId, value)); return true; } return false; } //------------------------------------------------------------------------ tresult ProgramList::getProgramInfo (int32 programIndex, CString attributeId, String128 value /*out*/) { if (programIndex >= 0 && programIndex < static_cast (programNames.size ())) { StringMap::const_iterator it = programInfos[programIndex].find (attributeId); if (it != programInfos[programIndex].end ()) { if (!it->second.empty ()) { memset (value, 0, sizeof (String128)); it->second.copy (value, 128); return kResultTrue; } } } return kResultFalse; } //------------------------------------------------------------------------ tresult ProgramList::getProgramName (int32 programIndex, String128 name /*out*/) { if (programIndex >= 0 && programIndex < static_cast (programNames.size ())) { memset (name, 0, sizeof (String128)); programNames.at (programIndex).copy (name, 128); return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ tresult ProgramList::setProgramName (int32 programIndex, const String128 name /*in*/) { if (programIndex >= 0 && programIndex < static_cast (programNames.size ())) { programNames.at (programIndex) = name; if (parameter) { static_cast (parameter)->replaceString (programIndex, name); } return kResultTrue; } return kResultFalse; } //------------------------------------------------------------------------ Parameter* ProgramList::getParameter () { if (parameter == nullptr) { auto* listParameter = new StringListParameter ( info.name, info.id, nullptr, ParameterInfo::kCanAutomate | ParameterInfo::kIsList | ParameterInfo::kIsProgramChange, unitId); for (const auto& programName : programNames) { listParameter->appendString (programName.data ()); } parameter = listParameter; } return parameter; } //------------------------------------------------------------------------ // ProgramListWithPitchNames implementation //----------------------------------------------------------------------------- ProgramListWithPitchNames::ProgramListWithPitchNames (const String128 name, ProgramListID listId, UnitID unitId) : ProgramList (name, listId, unitId) { } //----------------------------------------------------------------------------- int32 ProgramListWithPitchNames::addProgram (const String128 name) { int32 index = ProgramList::addProgram (name); if (index >= 0) pitchNames.emplace_back (); return index; } //----------------------------------------------------------------------------- bool ProgramListWithPitchNames::setPitchName (int32 programIndex, int16 pitch, const String128 pitchName) { if (programIndex < 0 || programIndex >= getCount ()) return false; bool nameChanged = true; std::pair res = pitchNames[programIndex].insert (std::make_pair (pitch, pitchName)); if (!res.second) { if (res.first->second == pitchName) nameChanged = false; else res.first->second = pitchName; } if (nameChanged) changed (); return true; } //----------------------------------------------------------------------------- bool ProgramListWithPitchNames::removePitchName (int32 programIndex, int16 pitch) { bool result = false; if (programIndex >= 0 && programIndex < getCount ()) { result = pitchNames.at (programIndex).erase (pitch) != 0; } if (result) changed (); return result; } //----------------------------------------------------------------------------- tresult ProgramListWithPitchNames::hasPitchNames (int32 programIndex) { if (programIndex >= 0 && programIndex < getCount ()) return (pitchNames.at (programIndex).empty () == true) ? kResultFalse : kResultTrue; return kResultFalse; } //----------------------------------------------------------------------------- tresult ProgramListWithPitchNames::getPitchName (int32 programIndex, int16 midiPitch, String128 name /*out*/) { if (programIndex >= 0 && programIndex < getCount ()) { PitchNameMap::const_iterator it = pitchNames[programIndex].find (midiPitch); if (it != pitchNames[programIndex].end ()) { memset (name, 0, sizeof (String128)); it->second.copy (name, 128); return kResultTrue; } } return kResultFalse; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vsteventshelper.h0000644000000000000000000000013215124701711023765 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vsteventshelper.h0000644000175000001440000001052115124701711023754 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vsteventshelper.h // Created by : Steinberg, 11/2018 // Description : common defines // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/futils.h" #include "pluginterfaces/vst/ivstevents.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace Helpers { //------------------------------------------------------------------------ /** bound a value between a min and max */ template inline T boundTo (T minval, T maxval, T x) { if (x < minval) return minval; if (x > maxval) return maxval; return x; } //------------------------------------------------------------------------ /** Initialized a Event */ inline Event& init (Event& event, uint16 type, int32 busIndex = 0, int32 sampleOffset = 0, TQuarterNotes ppqPosition = 0, uint16 flags = 0) { event.busIndex = busIndex; event.sampleOffset = sampleOffset; event.ppqPosition = ppqPosition; event.flags = flags; event.type = type; return event; } //------------------------------------------------------------------------ /** Returns normalized value of a LegacyMIDICCOutEvent value [0, 127] */ inline ParamValue getMIDINormValue (uint8 value) { return boundTo (0., 1., ToNormalized (value, 127)); } //------------------------------------------------------------------------ /** Returns LegacyMIDICCOut value [0, 127] from a normalized value [0., 1.] */ inline int8 getMIDICCOutValue (ParamValue value) { return boundTo (0, 127, FromNormalized (value, 127)); } //------------------------------------------------------------------------ /** Returns MIDI 14bit value from a normalized value [0., 1.] */ inline int16 getMIDI14BitValue (ParamValue value) { return boundTo (0, 0x3FFF, FromNormalized (value, 0x3FFF)); } //------------------------------------------------------------------------ /** Returns normalized value of a MIDI 14bit [0., 0x3FFF] */ inline ParamValue getMIDI14BitNormValue (int16 value) { return boundTo (0.f, 1., ToNormalized (value, 0x3FFF)); } //------------------------------------------------------------------------ /** Returns pitchbend value from a PitchBend LegacyMIDICCOut Event */ inline int16 getPitchBendValue (const LegacyMIDICCOutEvent& e) { return ((e.value & 0x7F) | ((e.value2 & 0x7F) << 7)); } //------------------------------------------------------------------------ /** set a normalized pitchbend value to a LegacyMIDICCOut Event */ inline void setPitchBendValue (LegacyMIDICCOutEvent& e, ParamValue value) { auto newValue = getMIDI14BitValue (value); e.value = (newValue & 0x7F); e.value2 = ((newValue >> 7) & 0x7F); } //------------------------------------------------------------------------ /** Returns normalized pitchbend value from a PitchBend LegacyMIDICCOut Event */ inline ParamValue getNormPitchBendValue (const LegacyMIDICCOutEvent& e) { return getMIDI14BitNormValue (getPitchBendValue (e)); } //------------------------------------------------------------------------ /** Initialized a LegacyMIDICCOutEvent */ inline LegacyMIDICCOutEvent& initLegacyMIDICCOutEvent (Event& event, uint8 controlNumber, uint8 channel = 0, int8 value = 0, int8 value2 = 0) { init (event, Event::kLegacyMIDICCOutEvent); event.midiCCOut.channel = channel; event.midiCCOut.controlNumber = controlNumber; event.midiCCOut.value = value; event.midiCCOut.value2 = value2; return event.midiCCOut; } /**@}*/ //------------------------------------------------------------------------ } // namespace Helpers } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstnoteexpressiontypes.h0000644000000000000000000000012715124701711025437 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstnoteexpressiontypes.h0000644000175000001440000001515315124701711025430 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstnoteexpressiontypes.h // Created by : Steinberg, 12/2010 // Description : VST Note Expression Type Info Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "base/source/fobject.h" #include "pluginterfaces/vst/ivstnoteexpression.h" #include "pluginterfaces/vst/ivstphysicalui.h" #include #include namespace Steinberg { namespace Vst { class Parameter; //------------------------------------------------------------------------ /** Note expression type object. \ingroup vstClasses */ class NoteExpressionType : public FObject { public: NoteExpressionType (); NoteExpressionType (const NoteExpressionTypeInfo& info); NoteExpressionType (NoteExpressionTypeID typeId, const TChar* title, const TChar* shortTitle, const TChar* units, int32 unitId, NoteExpressionValue defaultValue, NoteExpressionValue minimum, NoteExpressionValue maximum, int32 stepCount, int32 flags = 0, int32 precision = 4); NoteExpressionType (NoteExpressionTypeID typeId, const TChar* title, const TChar* shortTitle, const TChar* units, int32 unitId, Parameter* associatedParameter, int32 flags = 0); /** get the underlying NoteExpressionTypeInfo struct */ NoteExpressionTypeInfo& getInfo () { return info; } /** convert a note expression value to a readable string */ virtual tresult getStringByValue (NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/); /** convert a readable string to a note expression value */ virtual tresult getValueByString (const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/); /** gets the current precision (used for string representation of float) */ int32 getPrecision () const { return precision; } /** Sets the precision for string representation of float value (for example 4.34 with 2 as * precision) */ void setPrecision (int32 val) { precision = val; } tresult getPhysicalUIType (PhysicalUITypeID& physicalUITypeID /*out*/) const; tresult setPhysicalUITypeID (PhysicalUITypeID physicalUITypeID /*in*/); //----------------------------------------------------------------------------- OBJ_METHODS (NoteExpressionType, FObject) protected: NoteExpressionTypeInfo info; IPtr associatedParameter; int32 precision; PhysicalUITypeID physicalUITypeID {static_cast (kInvalidPUITypeID)}; }; //------------------------------------------------------------------------ /** Note expression type object representing a custom range. \ingroup vstClasses */ class RangeNoteExpressionType : public NoteExpressionType { public: RangeNoteExpressionType (NoteExpressionTypeID typeId, const TChar* title, const TChar* shortTitle, const TChar* units, int32 unitId, NoteExpressionValue defaultPlainValue, NoteExpressionValue plainMin, NoteExpressionValue plainMax, int32 flags = 0, int32 precision = 4); /** Gets the minimum plain value */ virtual ParamValue getMin () const { return plainMin; } /** Sets the minimum plain value */ virtual void setMin (ParamValue value) { plainMin = value; } /** Gets the maximum plain value */ virtual ParamValue getMax () const { return plainMax; } /** Sets the maximum plain value */ virtual void setMax (ParamValue value) { plainMax = value; } tresult getStringByValue (NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/) SMTG_OVERRIDE; tresult getValueByString (const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/) SMTG_OVERRIDE; //----------------------------------------------------------------------------- OBJ_METHODS (RangeNoteExpressionType, NoteExpressionType) protected: NoteExpressionValue plainMin; NoteExpressionValue plainMax; }; //------------------------------------------------------------------------ /** Collection of note expression types. \ingroup vstClasses */ class NoteExpressionTypeContainer : public FObject { public: /** default constructor */ NoteExpressionTypeContainer (); /** add a note expression type. The container owns the type. No need to release it afterwards. */ bool addNoteExpressionType (NoteExpressionType* noteExpType); /** remove a note expression type */ bool removeNoteExpressionType (NoteExpressionTypeID typeId); /** remove all note expression types */ void removeAll (); /** get a note expression type object by ID */ NoteExpressionType* getNoteExpressionType (NoteExpressionTypeID typeId); /** get the number of note expression types */ int32 getNoteExpressionCount (); /** get note expression info */ tresult getNoteExpressionInfo (int32 noteExpressionIndex /*in*/, NoteExpressionTypeInfo& info /*out*/); /** convert a note expression value to a readable string */ tresult getNoteExpressionStringByValue (NoteExpressionTypeID id /*in*/, NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/); /** convert a string to a note expression value */ tresult getNoteExpressionValueByString (NoteExpressionTypeID id /*in*/, const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/); /** get the Physical UI Type associated to a given Note Expression Id */ tresult getMappedNoteExpression (const PhysicalUITypeID physicalUITypeID, NoteExpressionTypeID& id); //----------------------------------------------------------------------------- OBJ_METHODS (NoteExpressionTypeContainer, FObject) protected: using NoteExprTypeVector = std::vector>; NoteExprTypeVector::const_iterator find (NoteExpressionTypeID typeId) const; NoteExprTypeVector noteExps; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstbypassprocessor.h0000644000000000000000000000013215124701711024522 xustar0030 mtime=1767080905.286210845 30 atime=1767080905.286210845 30 ctime=1767080905.286210845 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstbypassprocessor.h0000644000175000001440000002245215124701711024517 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstbypassprocessor.h // Created by : Steinberg, 04/2015 // Description : Example of bypass support Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstaudioprocessor.h" #include "vstspeakerarray.h" namespace Steinberg { namespace Vst { #define kMaxChannelsSupported 64 //------------------------------------------------------------------------ // AudioBuffer //------------------------------------------------------------------------ template class AudioBuffer { public: //------------------------------------------------------------------------ AudioBuffer () : mBuffer (nullptr), mMaxSamples (0) {} ~AudioBuffer () { resize (0); } //--- --------------------------------------------------------------------- void resize (int32 _maxSamples) { if (mMaxSamples != _maxSamples) { mMaxSamples = _maxSamples; if (mMaxSamples <= 0) { if (mBuffer) { free (mBuffer); mBuffer = nullptr; } } else { if (mBuffer) mBuffer = (T*)realloc (mBuffer, mMaxSamples * sizeof (T)); else mBuffer = (T*)malloc (mMaxSamples * sizeof (T)); } } } //--- --------------------------------------------------------------------- void clear (int32 numSamples) { if (mBuffer) { int32 count = numSamples < mMaxSamples ? numSamples : mMaxSamples; memset (mBuffer, 0, count * sizeof (T)); } } int32 getMaxSamples () const { return mMaxSamples; } void release () { resize (0); } void clearAll () { if (mMaxSamples > 0) clear (mMaxSamples); } operator T* () { return mBuffer; } //------------------------------------------------------------------------ protected: T* mBuffer; int32 mMaxSamples; }; //------------------------------------------------------------------------ template static bool delay (int32 sampleFrames, T* inStream, T* outStream, T* delayBuffer, int32 bufferSize, int32 bufferInPos, int32 bufferOutPos) { // delay inStream int32 remain, inFrames, outFrames; T* bufIn; T* bufOut; remain = sampleFrames; while (remain > 0) { bufIn = delayBuffer + bufferInPos; bufOut = delayBuffer + bufferOutPos; if (bufferInPos > bufferOutPos) inFrames = bufferSize - bufferInPos; else inFrames = bufferOutPos - bufferInPos; outFrames = bufferSize - bufferOutPos; if (inFrames > remain) inFrames = remain; if (outFrames > inFrames) outFrames = inFrames; // order important for in-place processing! memcpy (bufIn, inStream, inFrames * sizeof (T)); // copy to buffer memcpy (outStream, bufOut, outFrames * sizeof (T)); // copy from buffer inStream += inFrames; outStream += outFrames; bufferInPos += inFrames; if (bufferInPos >= bufferSize) bufferInPos -= bufferSize; bufferOutPos += outFrames; if (bufferOutPos >= bufferSize) bufferOutPos -= bufferSize; if (inFrames > outFrames) { // still some output to copy bufOut = delayBuffer + bufferOutPos; outFrames = inFrames - outFrames; memcpy (outStream, bufOut, outFrames * sizeof (T)); // copy from buffer outStream += outFrames; bufferOutPos += outFrames; if (bufferOutPos >= bufferSize) bufferOutPos -= bufferSize; } remain -= inFrames; } return true; } //------------------------------------------------------------------------ // BypassProcessor //------------------------------------------------------------------------ template class BypassProcessor { public: //------------------------------------------------------------------------ BypassProcessor () { for (int32 i = 0; i < kMaxChannelsSupported; i++) { mInputPinLookup[i] = -1; mDelays[i] = nullptr; } } ~BypassProcessor () { reset (); } void setup (IAudioProcessor& audioProcessor, ProcessSetup& processSetup, int32 delaySamples) { reset (); SpeakerArrangement inputArr = 0; bool hasInput = audioProcessor.getBusArrangement (kInput, 0, inputArr) == kResultOk; SpeakerArrangement outputArr = 0; bool hasOutput = audioProcessor.getBusArrangement (kOutput, 0, outputArr) == kResultOk; mMainIOBypass = hasInput && hasOutput; if (!mMainIOBypass) return; // create lookup table (in <- out) and delays... SpeakerArray inArray (inputArr); SpeakerArray outArray (outputArr); // security check (todo) if (outArray.total () >= kMaxChannelsSupported) return; for (int32 i = 0; i < outArray.total (); i++) { if (outArray.at (i) == Vst::kSpeakerL) { if (inArray.total () == 1 && inArray.at (0) == Vst::kSpeakerM) { mInputPinLookup[i] = 0; } else mInputPinLookup[i] = inArray.getSpeakerIndex (outArray.at (i)); } else mInputPinLookup[i] = inArray.getSpeakerIndex (outArray.at (i)); mDelays[i] = new Delay (processSetup.maxSamplesPerBlock, delaySamples); mDelays[i]->flush (); } } void reset () { mMainIOBypass = false; for (int32 i = 0; i < kMaxChannelsSupported; i++) { mInputPinLookup[i] = -1; if (mDelays[i]) { delete mDelays[i]; mDelays[i] = nullptr; } } } bool isActive () const { return mActive; } void setActive (bool state) { if (mActive == state) return; mActive = state; // flush delays when turning on if (state && mMainIOBypass) for (auto &delay : mDelays) { if (!delay) break; delay->flush (); } } void process (ProcessData& data) { // flush if (data.numInputs == 0 || data.numOutputs == 0) return; AudioBusBuffers& inBus = data.inputs[0]; AudioBusBuffers& outBus = data.outputs[0]; if (data.symbolicSampleSize == kSample32) { if (!outBus.channelBuffers32) return; } else if (!outBus.channelBuffers64) return; if (mMainIOBypass) { for (int32 channel = 0; channel < outBus.numChannels; channel++) { T* src = nullptr; bool silent = true; T* dst; if (data.symbolicSampleSize == kSample32) dst = (T*)outBus.channelBuffers32[channel]; else dst = (T*)outBus.channelBuffers64[channel]; if (!dst) continue; int inputChannel = mInputPinLookup[channel]; if (inputChannel != -1) { silent = (inBus.silenceFlags & (1ll << inputChannel)) != 0; if (data.symbolicSampleSize == kSample32) src = (T*)inBus.channelBuffers32[inputChannel]; else src = (T*)inBus.channelBuffers64[inputChannel]; } if (mDelays[channel]->process (src, dst, data.numSamples, silent)) { outBus.silenceFlags |= (1ll << channel); } else { outBus.silenceFlags = 0; } } } // clear all other outputs for (int32 outBusIndex = mMainIOBypass ? 1 : 0; outBusIndex < data.numOutputs; outBusIndex++) { outBus = data.outputs[outBusIndex]; for (int32 channel = 0; channel < outBus.numChannels; channel++) { T* dst; if (data.symbolicSampleSize == kSample32) dst = (T*)outBus.channelBuffers32[channel]; else dst = (T*)outBus.channelBuffers64[channel]; if (dst) { memset (dst, 0, data.numSamples * sizeof (T)); outBus.silenceFlags |= 1ll << channel; } } } } //------------------------------------------------------------------------ protected: int32 mInputPinLookup[kMaxChannelsSupported]; struct Delay { AudioBuffer mDelayBuffer; int32 mDelaySamples; int32 mInPos; int32 mOutPos; Delay (int32 maxSamplesPerBlock, int32 delaySamples) : mDelaySamples (delaySamples), mInPos (-1), mOutPos (-1) { if (mDelaySamples > 0) mDelayBuffer.resize (maxSamplesPerBlock + mDelaySamples); } bool hasDelay () const { return mDelaySamples > 0; } int32 getBufferSamples () const { return mDelayBuffer.getMaxSamples (); } bool process (T* src, T* dst, int32 numSamples, bool silentIn) { bool silentOut = false; if (hasDelay () && src) { int32 bufferSize = getBufferSamples (); delay (numSamples, src, dst, mDelayBuffer, bufferSize, mInPos, mOutPos); // update inPos, outPos mInPos += numSamples; if (mInPos >= bufferSize) mInPos -= bufferSize; mOutPos += numSamples; if (mOutPos >= bufferSize) mOutPos -= bufferSize; } else { if (src != dst) { if (src && !silentIn) { memcpy (dst, src, numSamples * sizeof (T)); } else { memset (dst, 0, numSamples * sizeof (T)); silentOut = true; } } else { silentOut = silentIn; } } return silentOut; } void flush () { mDelayBuffer.clearAll (); mInPos = mOutPos = 0; if (hasDelay ()) mOutPos = getBufferSamples () - mDelaySamples; // must be != inPos } }; Delay* mDelays[kMaxChannelsSupported]; bool mActive {false}; bool mMainIOBypass {false}; }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstsinglecomponenteffect.cpp0000644000000000000000000000012715124701711026201 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstsinglecomponenteffect.cpp0000644000175000001440000002317215124701711026172 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstsinglecomponenteffect.cpp // Created by : Steinberg, 03/2008 // Description : Basic Audio Effect Implementation in one component // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstsinglecomponenteffect.h" //----------------------------------------------------------------------------- namespace Steinberg { namespace Vst { //----------------------------------------------------------------------------- // SingleComponentEffect Implementation //----------------------------------------------------------------------------- SingleComponentEffect::SingleComponentEffect () : audioInputs (kAudio, kInput) , audioOutputs (kAudio, kOutput) , eventInputs (kEvent, kInput) , eventOutputs (kEvent, kOutput) { processSetup.maxSamplesPerBlock = 1024; processSetup.processMode = Vst::kRealtime; processSetup.sampleRate = 44100.0; processSetup.symbolicSampleSize = Vst::kSample32; } //----------------------------------------------------------------------------- SingleComponentEffect::~SingleComponentEffect () { } //----------------------------------------------------------------------------- tresult PLUGIN_API SingleComponentEffect::initialize (FUnknown* context) { return EditControllerEx1::initialize (context); } //----------------------------------------------------------------------------- tresult PLUGIN_API SingleComponentEffect::terminate () { parameters.removeAll (); removeAllBusses (); return EditControllerEx1::terminate (); } //----------------------------------------------------------------------------- int32 PLUGIN_API SingleComponentEffect::getBusCount (MediaType type, BusDirection dir) { BusList* busList = getBusList (type, dir); return busList ? static_cast (busList->size ()) : 0; } //----------------------------------------------------------------------------- tresult PLUGIN_API SingleComponentEffect::getBusInfo (MediaType type, BusDirection dir, int32 index, BusInfo& info) { BusList* busList = getBusList (type, dir); if (busList == nullptr || index >= static_cast (busList->size ())) return kInvalidArgument; Bus* bus = busList->at (index); info.mediaType = type; info.direction = dir; if (bus->getInfo (info)) return kResultTrue; return kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API SingleComponentEffect::activateBus (MediaType type, BusDirection dir, int32 index, TBool state) { BusList* busList = getBusList (type, dir); if (busList == nullptr || index >= static_cast (busList->size ())) return kInvalidArgument; Bus* bus = busList->at (index); if (!bus) return kResultFalse; bus->setActive (state); return kResultTrue; } //----------------------------------------------------------------------------- AudioBus* SingleComponentEffect::addAudioInput (const TChar* name, SpeakerArrangement arr, BusType busType, int32 flags) { auto* newBus = new AudioBus (name, busType, flags, arr); audioInputs.push_back (IPtr (newBus, false)); return newBus; } //----------------------------------------------------------------------------- AudioBus* SingleComponentEffect::addAudioOutput (const TChar* name, SpeakerArrangement arr, BusType busType, int32 flags) { auto* newBus = new AudioBus (name, busType, flags, arr); audioOutputs.push_back (IPtr (newBus, false)); return newBus; } //----------------------------------------------------------------------------- EventBus* SingleComponentEffect::addEventInput (const TChar* name, int32 channels, BusType busType, int32 flags) { auto* newBus = new EventBus (name, busType, flags, channels); eventInputs.push_back (IPtr (newBus, false)); return newBus; } //----------------------------------------------------------------------------- EventBus* SingleComponentEffect::addEventOutput (const TChar* name, int32 channels, BusType busType, int32 flags) { auto* newBus = new EventBus (name, busType, flags, channels); eventOutputs.push_back (IPtr (newBus, false)); return newBus; } //----------------------------------------------------------------------------- tresult SingleComponentEffect::removeAudioBusses () { audioInputs.clear (); audioOutputs.clear (); return kResultOk; } //----------------------------------------------------------------------------- tresult SingleComponentEffect::removeEventBusses () { eventInputs.clear (); eventOutputs.clear (); return kResultOk; } //----------------------------------------------------------------------------- tresult SingleComponentEffect::removeAllBusses () { removeAudioBusses (); removeEventBusses (); return kResultOk; } //----------------------------------------------------------------------------- // IAudioProcessor //----------------------------------------------------------------------------- tresult PLUGIN_API SingleComponentEffect::setBusArrangements (SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts) { if (numIns < 0 || numOuts < 0) return kInvalidArgument; if (numIns > static_cast (audioInputs.size ()) || numOuts > static_cast (audioOutputs.size ())) return kResultFalse; for (int32 index = 0; index < static_cast (audioInputs.size ()); ++index) { if (index >= numIns) break; FCast (audioInputs[index].get ())->setArrangement (inputs[index]); } for (int32 index = 0; index < static_cast (audioOutputs.size ()); ++index) { if (index >= numOuts) break; FCast (audioOutputs[index].get ())->setArrangement (outputs[index]); } return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API SingleComponentEffect::getBusArrangement (BusDirection dir, int32 busIndex, SpeakerArrangement& arr) { BusList* busList = getBusList (kAudio, dir); if (busList == nullptr || busIndex >= static_cast (busList->size ())) return kInvalidArgument; if (auto* audioBus = FCast (busList->at (busIndex))) { arr = audioBus->getArrangement (); return kResultTrue; } return kResultFalse; } //----------------------------------------------------------------------------- tresult PLUGIN_API SingleComponentEffect::setupProcessing (ProcessSetup& newSetup) { if (canProcessSampleSize (newSetup.symbolicSampleSize) != kResultTrue) return kResultFalse; processSetup = newSetup; return kResultOk; } //----------------------------------------------------------------------------- tresult PLUGIN_API SingleComponentEffect::canProcessSampleSize (int32 symbolicSampleSize) { return symbolicSampleSize == kSample32 ? kResultTrue : kResultFalse; } //----------------------------------------------------------------------------- BusList* SingleComponentEffect::getBusList (MediaType type, BusDirection dir) { if (type == kAudio) return dir == kInput ? &audioInputs : &audioOutputs; if (type == kEvent) return dir == kInput ? &eventInputs : &eventOutputs; return nullptr; } //----------------------------------------------------------------------------- tresult PLUGIN_API SingleComponentEffect::queryInterface (const TUID iid, void** obj) { if (memcmp (iid, IConnectionPoint::iid, sizeof (::Steinberg::TUID)) == 0) { // no need to expose IConnectionPoint to the host return kNoInterface; } DEF_INTERFACE (IComponent) DEF_INTERFACE (IAudioProcessor) DEF_INTERFACE (IProcessContextRequirements) return EditControllerEx1::queryInterface (iid, obj); } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg // work around for the name clash of IComponent::setState and IEditController::setState #if defined(PROJECT_INCLUDES_VSTEDITCONTROLLER) && PROJECT_INCLUDES_VSTEDITCONTROLLER // make sure that vsteditcontroller.cpp is included by your project //------------------------------------------------------------------------ Steinberg::tresult PLUGIN_API Steinberg::Vst::EditController::setEditorState ( Steinberg::IBStream* /*state*/) { return Steinberg::kNotImplemented; } //------------------------------------------------------------------------ Steinberg::tresult PLUGIN_API Steinberg::Vst::EditController::getEditorState ( Steinberg::IBStream* /*state*/) { return Steinberg::kNotImplemented; } #else // make sure that vsteditcontroller.cpp is otherwise excluded from your project #define setState setEditorState #define getState getEditorState #include "public.sdk/source/vst/vsteditcontroller.cpp" #undef setState #undef getState #endif // PROJECT_INCLUDES_VSTEDITCONTROLLER qtractor-1.5.11/src/vst3/public.sdk/source/vst/PaxHeaders/vstnoteexpressiontypes.cpp0000644000000000000000000000012715124701711025772 xustar0029 mtime=1767080905.28721084 29 atime=1767080905.28721084 29 ctime=1767080905.28721084 qtractor-1.5.11/src/vst3/public.sdk/source/vst/vstnoteexpressiontypes.cpp0000644000175000001440000002601415124701711025761 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/vstnoteexpressiontypes.cpp // Created by : Steinberg, 12/2010 // Description : VST Note Expression Type Info Implementation // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "vstnoteexpressiontypes.h" #include "vstparameters.h" #include "base/source/fstring.h" #include "pluginterfaces/base/futils.h" #include "pluginterfaces/base/ustring.h" #include namespace Steinberg { namespace Vst { //----------------------------------------------------------------------------- NoteExpressionType::NoteExpressionType () : precision (4) { memset (&info, 0, sizeof (info)); } //----------------------------------------------------------------------------- NoteExpressionType::NoteExpressionType (const NoteExpressionTypeInfo& _info) : precision (4) { memcpy (&info, &_info, sizeof (info)); } //----------------------------------------------------------------------------- NoteExpressionType::NoteExpressionType (NoteExpressionTypeID _typeId, const TChar* _title, const TChar* _shortTitle, const TChar* _units, int32 _unitId, NoteExpressionValue _defaultValue, NoteExpressionValue _minimum, NoteExpressionValue _maximum, int32 _stepCount, int32 _flags, int32 _precision) : precision (_precision) { memset (&info, 0, sizeof (info)); info.typeId = _typeId; if (_title) UString (info.title, str16BufferSize (String128)).assign (_title); if (_shortTitle) UString (info.shortTitle, str16BufferSize (String128)).assign (_shortTitle); if (_units) UString (info.shortTitle, str16BufferSize (String128)).assign (_units); info.unitId = _unitId; info.valueDesc.defaultValue = _defaultValue; info.valueDesc.minimum = _minimum; info.valueDesc.maximum = _maximum; info.valueDesc.stepCount = _stepCount; info.flags = _flags; } //----------------------------------------------------------------------------- NoteExpressionType::NoteExpressionType (NoteExpressionTypeID _typeId, const TChar* _title, const TChar* _shortTitle, const TChar* _units, int32 _unitId, Parameter* _associatedParameter, int32 _flags) : associatedParameter (_associatedParameter), precision (4) { memset (&info, 0, sizeof (info)); info.typeId = _typeId; if (_title) UString (info.title, str16BufferSize (String128)).assign (_title); if (_shortTitle) UString (info.shortTitle, str16BufferSize (String128)).assign (_shortTitle); if (_units) UString (info.shortTitle, str16BufferSize (String128)).assign (_units); info.unitId = _unitId; info.valueDesc.defaultValue = 0.5; info.valueDesc.minimum = 0.; info.valueDesc.maximum = 1.; info.flags = _flags; if (_associatedParameter) { info.valueDesc.stepCount = _associatedParameter->getInfo ().stepCount; info.valueDesc.defaultValue = _associatedParameter->getInfo ().defaultNormalizedValue; info.associatedParameterId = associatedParameter->getInfo ().id; info.flags |= NoteExpressionTypeInfo::kAssociatedParameterIDValid; } } //----------------------------------------------------------------------------- tresult NoteExpressionType::getStringByValue (NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/) { if (associatedParameter) { associatedParameter->toString (valueNormalized, string); return kResultTrue; } UString128 wrapper; if (info.valueDesc.stepCount > 0) { int32 value = Min (info.valueDesc.stepCount, (int32) (valueNormalized * (info.valueDesc.stepCount + 1))); wrapper.printInt (value); } else { wrapper.printFloat (valueNormalized, precision); } wrapper.copyTo (string, 128); return kResultTrue; } //----------------------------------------------------------------------------- tresult NoteExpressionType::getValueByString (const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/) { if (associatedParameter) { return associatedParameter->fromString (string, valueNormalized) ? kResultTrue : kResultFalse; } String wrapper (string); if (info.valueDesc.stepCount > 0) { int32 value; if (wrapper.scanInt32 (value) && value <= info.valueDesc.stepCount) { valueNormalized = (NoteExpressionValue)value / (NoteExpressionValue)info.valueDesc.stepCount; return kResultTrue; } return kResultFalse; } double value; wrapper.scanFloat (value); if (value < info.valueDesc.minimum) return kResultFalse; if (value > info.valueDesc.maximum) return kResultFalse; valueNormalized = value; return kResultTrue; } //----------------------------------------------------------------------------- tresult NoteExpressionType::getPhysicalUIType (PhysicalUITypeID& _physicalUITypeID /*out*/) const { _physicalUITypeID = physicalUITypeID; return kResultTrue; } //----------------------------------------------------------------------------- tresult NoteExpressionType::setPhysicalUITypeID (PhysicalUITypeID _physicalUITypeID /*in*/) { physicalUITypeID = _physicalUITypeID; return kResultTrue; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- RangeNoteExpressionType::RangeNoteExpressionType ( NoteExpressionTypeID _typeId, const TChar* _title, const TChar* _shortTitle, const TChar* _units, int32 _unitId, NoteExpressionValue _defaultPlainValue, NoteExpressionValue _plainMin, NoteExpressionValue _plainMax, int32 _flags, int32 _precision) : NoteExpressionType (_typeId, _title, _shortTitle, _units, _unitId, 0, 0, 1, 0, _flags, _precision) , plainMin (_plainMin) , plainMax (_plainMax) { info.valueDesc.defaultValue = (_defaultPlainValue - getMin ()) / (getMax () - getMin ()); } //----------------------------------------------------------------------------- tresult RangeNoteExpressionType::getStringByValue (NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/) { NoteExpressionValue plain = valueNormalized * (getMax () - getMin ()) + getMin (); UString128 wrapper; wrapper.printFloat (plain, precision); wrapper.copyTo (string, 128); return kResultTrue; } //----------------------------------------------------------------------------- tresult RangeNoteExpressionType::getValueByString (const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/) { String wrapper (string); double value = 0.; if (wrapper.scanFloat (value)) { value = (value - getMin ()) / (getMax () - getMin ()); if (value >= 0. && value <= 1.) { valueNormalized = value; return kResultTrue; } } return kResultFalse; } //----------------------------------------------------------------------------- NoteExpressionTypeContainer::NoteExpressionTypeContainer () { } //----------------------------------------------------------------------------- NoteExpressionTypeContainer::NoteExprTypeVector::const_iterator NoteExpressionTypeContainer::find ( NoteExpressionTypeID typeId) const { for (auto it = noteExps.begin (), end = noteExps.end (); it != end; ++it) { if ((*it)->getInfo ().typeId == typeId) { return it; } } return noteExps.end (); } //----------------------------------------------------------------------------- bool NoteExpressionTypeContainer::addNoteExpressionType (NoteExpressionType* noteExpType) { noteExps.emplace_back (noteExpType, false); return true; } //----------------------------------------------------------------------------- bool NoteExpressionTypeContainer::removeNoteExpressionType (NoteExpressionTypeID typeId) { auto it = find (typeId); if (it != noteExps.end ()) { noteExps.erase (it); return true; } return false; } //----------------------------------------------------------------------------- void NoteExpressionTypeContainer::removeAll () { noteExps.clear (); } //----------------------------------------------------------------------------- NoteExpressionType* NoteExpressionTypeContainer::getNoteExpressionType (NoteExpressionTypeID typeId) { auto it = find (typeId); if (it != noteExps.end ()) return (*it); return nullptr; } //----------------------------------------------------------------------------- int32 NoteExpressionTypeContainer::getNoteExpressionCount () { return static_cast (noteExps.size ()); } //----------------------------------------------------------------------------- tresult NoteExpressionTypeContainer::getNoteExpressionInfo (int32 noteExpressionIndex, NoteExpressionTypeInfo& info /*out*/) { if (noteExpressionIndex < 0 || noteExpressionIndex >= static_cast (noteExps.size ())) return kInvalidArgument; std::memcpy (&info, ¬eExps[noteExpressionIndex]->getInfo (), sizeof (info)); return kResultTrue; } //----------------------------------------------------------------------------- tresult NoteExpressionTypeContainer::getNoteExpressionStringByValue ( NoteExpressionTypeID id, NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/) { NoteExpressionType* noteExpType = getNoteExpressionType (id); if (noteExpType) { return noteExpType->getStringByValue (valueNormalized, string); } return kResultFalse; } //----------------------------------------------------------------------------- tresult NoteExpressionTypeContainer::getNoteExpressionValueByString ( NoteExpressionTypeID id, const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/) { NoteExpressionType* noteExpType = getNoteExpressionType (id); if (noteExpType) { return noteExpType->getValueByString (string, valueNormalized); } return kResultFalse; } //----------------------------------------------------------------------------- tresult NoteExpressionTypeContainer::getMappedNoteExpression ( const PhysicalUITypeID physicalUITypeID, NoteExpressionTypeID& id /*out*/) { id = kInvalidTypeID; for (auto& item : noteExps) { PhysicalUITypeID tmp; if (item->getPhysicalUIType (tmp) == kResultTrue) { if (tmp == physicalUITypeID) { id = item->getInfo ().typeId; break; } } } return kResultTrue; } //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/PaxHeaders/base0000644000000000000000000000013215124701742015042 xustar0030 mtime=1767080930.090106392 30 atime=1767080892.816263357 30 ctime=1767080930.090106392 qtractor-1.5.11/src/vst3/base/0000755000175000001440000000000015124701742015107 5ustar00rncbcusersqtractor-1.5.11/src/vst3/base/PaxHeaders/LICENSE.txt0000644000000000000000000000013215124701711016736 xustar0030 mtime=1767080905.184211274 30 atime=1767080905.184211274 30 ctime=1767080905.184211274 qtractor-1.5.11/src/vst3/base/LICENSE.txt0000644000175000001440000000235115124701711016727 0ustar00rncbcusers//----------------------------------------------------------------------------- MIT License Copyright (c) 2025, Steinberg Media Technologies GmbH Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //---------------------------------------------------------------------------------qtractor-1.5.11/src/vst3/base/PaxHeaders/thread0000644000000000000000000000013215124701711016305 xustar0030 mtime=1767080905.186924355 30 atime=1767080905.186211266 30 ctime=1767080905.186924355 qtractor-1.5.11/src/vst3/base/thread/0000755000175000001440000000000015124701711016352 5ustar00rncbcusersqtractor-1.5.11/src/vst3/base/thread/PaxHeaders/include0000644000000000000000000000013215124701711017730 xustar0030 mtime=1767080905.186924355 30 atime=1767080905.186211266 30 ctime=1767080905.186924355 qtractor-1.5.11/src/vst3/base/thread/include/0000755000175000001440000000000015124701711017775 5ustar00rncbcusersqtractor-1.5.11/src/vst3/base/thread/include/PaxHeaders/flock.h0000644000000000000000000000013215124701711021254 xustar0030 mtime=1767080905.186924355 30 atime=1767080905.186875972 30 ctime=1767080905.186924355 qtractor-1.5.11/src/vst3/base/thread/include/flock.h0000644000175000001440000001131615124701711021246 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/thread/include/flock.h // Created by : Steinberg, 1995 // Description : locks // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- //---------------------------------------------------------------------------------- /** @file base/thread/include/flock.h Locks. */ /** @defgroup baseLocks Locks */ //---------------------------------------------------------------------------------- #pragma once #include "base/source/fobject.h" #include "pluginterfaces/base/ftypes.h" #if SMTG_PTHREADS #include #elif SMTG_OS_WINDOWS struct CRITSECT // CRITICAL_SECTION { void* DebugInfo; // PRTL_CRITICAL_SECTION_DEBUG DebugInfo; Steinberg::int32 LockCount; // LONG LockCount; Steinberg::int32 RecursionCount; // LONG RecursionCount; void* OwningThread; // HANDLE OwningThread void* LockSemaphore; // HANDLE LockSemaphore Steinberg::int32 SpinCount; // ULONG_PTR SpinCount }; #endif namespace Steinberg { namespace Base { namespace Thread { //------------------------------------------------------------------------ /** Lock interface declaration. @ingroup baseLocks */ //------------------------------------------------------------------------ struct ILock { //------------------------------------------------------------------------ virtual ~ILock () {} /** Enables lock. */ virtual void lock () = 0; /** Disables lock. */ virtual void unlock () = 0; /** Tries to disable lock. */ virtual bool trylock () = 0; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ /** FLock declaration. @ingroup baseLocks */ //------------------------------------------------------------------------ class FLock : public ILock { public: //------------------------------------------------------------------------ /** Lock constructor. * @param name lock name */ FLock (const char8* name = "FLock"); /** Lock destructor. */ ~FLock () SMTG_OVERRIDE; //-- ILock ----------------------------------------------------------- void lock () SMTG_OVERRIDE; void unlock () SMTG_OVERRIDE; bool trylock () SMTG_OVERRIDE; //------------------------------------------------------------------------ protected: #if SMTG_PTHREADS pthread_mutex_t mutex; ///< Mutex object #elif SMTG_OS_WINDOWS CRITSECT section; ///< Critical section object #endif }; //------------------------------------------------------------------------ /** FLockObj declaration. Reference counted lock @ingroup baseLocks */ //------------------------------------------------------------------------ class FLockObject : public FObject, public FLock { public: OBJ_METHODS (FLockObject, FObject) }; //------------------------------------------------------------------------ /** FGuard - automatic object for locks. @ingroup baseLocks */ //------------------------------------------------------------------------ class FGuard { public: //------------------------------------------------------------------------ /** FGuard constructor. * @param _lock guard this lock */ FGuard (ILock& _lock) : lock (_lock) { lock.lock (); } /** FGuard destructor. */ ~FGuard () { lock.unlock (); } //------------------------------------------------------------------------ private: ILock& lock; ///< guarded lock }; //------------------------------------------------------------------------ /** Conditional Guard - Locks only if valid lock is passed. @ingroup baseLocks */ //------------------------------------------------------------------------ class FConditionalGuard { public: //------------------------------------------------------------------------ /** FConditionGuard constructor. * @param _lock guard this lock */ FConditionalGuard (FLock* _lock) : lock (_lock) { if (lock) lock->lock (); } /** FConditionGuard destructor. */ ~FConditionalGuard () { if (lock) lock->unlock (); } //------------------------------------------------------------------------ private: FLock* lock; ///< guarded lock }; //------------------------------------------------------------------------ } // namespace Thread } // namespace Base } // namespace Steinberg qtractor-1.5.11/src/vst3/base/thread/include/PaxHeaders/fcondition.h0000644000000000000000000000013215124701711022312 xustar0030 mtime=1767080905.186875972 30 atime=1767080905.186875972 30 ctime=1767080905.186875972 qtractor-1.5.11/src/vst3/base/thread/include/fcondition.h0000644000175000001440000000553515124701711022312 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/thread/include/fcondition.h // Created by : Steinberg, 1995 // Description : the threads and locks and signals... // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- //---------------------------------------------------------------------------------- /** @file base/thread/include/fcondition.h signal. */ /** @defgroup baseThread Thread Handling */ //---------------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ftypes.h" #if SMTG_PTHREADS #include #endif //------------------------------------------------------------------------ namespace Steinberg { namespace Base { namespace Thread { //------------------------------------------------------------------------ /** FCondition - wraps the signal and wait calls in win32 @ingroup baseThread */ //------------------------------------------------------------------------ class FCondition { public: //------------------------------------------------------------------------ /** Condition constructor. * @param name name of condition */ FCondition (const char8* name = nullptr /* "FCondition" */ ); /** Condition destructor. */ ~FCondition (); /** Signals one thread. */ void signal (); /** Signals all threads. */ void signalAll (); /** Waits for condition. */ void wait (); /** Waits for condition with timeout * @param timeout time out in milliseconds * @return false if timed out */ bool waitTimeout (int32 timeout = -1); /** Resets condition. */ void reset (); #if SMTG_OS_WINDOWS /** Gets condition handle. * @return handle */ void* getHandle () { return event; } #endif //------------------------------------------------------------------------ private: #if SMTG_PTHREADS pthread_mutex_t mutex; ///< Mutex object pthread_cond_t cond; ///< Condition object int32 state; ///< Use to hold the state of the signal int32 waiters; ///< Number of waiters #if DEVELOPMENT int32 waits; ///< Waits count int32 signalCount; ///< Signals count #endif #elif SMTG_OS_WINDOWS void* event; ///< Event handle #endif }; //------------------------------------------------------------------------ } // namespace Thread } // namespace Base } // namespace Steinberg qtractor-1.5.11/src/vst3/base/thread/PaxHeaders/source0000644000000000000000000000013215124701711017605 xustar0030 mtime=1767080905.187046498 30 atime=1767080905.186924355 30 ctime=1767080905.187046498 qtractor-1.5.11/src/vst3/base/thread/source/0000755000175000001440000000000015124701711017652 5ustar00rncbcusersqtractor-1.5.11/src/vst3/base/thread/source/PaxHeaders/fcondition.cpp0000644000000000000000000000013215124701711022522 xustar0030 mtime=1767080905.186924355 30 atime=1767080905.186924355 30 ctime=1767080905.186924355 qtractor-1.5.11/src/vst3/base/thread/source/fcondition.cpp0000644000175000001440000001275615124701711022525 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/thread/source/fcondition.cpp // Created by : Steinberg, 1995 // Description : signals... // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #define DEBUG_LOCK 0 #include "base/thread/include/fcondition.h" #include "base/source/fdebug.h" #include #include //------------------------------------------------------------------------ #if SMTG_PTHREADS //------------------------------------------------------------------------ #if __MACH__ extern "C" { #include #include #include #include #include #include #include #include #include #include #include } #include #else #include #endif #include #if SMTG_OS_MACOS #include #if !SMTG_OS_IOS #include #endif #endif #include //------------------------------------------------------------------------ #elif SMTG_OS_WINDOWS #include #endif namespace Steinberg { namespace Base { namespace Thread { //------------------------------------------------------------------------ /** FCondition CTOR. * @param name - can be used to set the name of the event. */ FCondition::FCondition (const char8* name) { #if SMTG_PTHREADS (void)name; // unused pthread_mutex_init (&mutex, 0); pthread_cond_init (&cond, 0); state = 0; waiters = 0; #if DEVELOPMENT waits = 0; signalCount = 0; #endif #elif SMTG_OS_WINDOWS // use name if existing event = CreateEventA (nullptr, FALSE, FALSE, name); #endif } //------------------------------------------------------------------------ FCondition::~FCondition () { #if SMTG_PTHREADS pthread_mutex_destroy (&mutex); pthread_cond_destroy (&cond); #elif SMTG_OS_WINDOWS CloseHandle (event); #endif } //------------------------------------------------------------------------ void FCondition::signal () { #if SMTG_PTHREADS pthread_mutex_lock (&mutex); state = 1; #if DEVELOPMENT signalCount++; #endif pthread_cond_signal (&cond); pthread_mutex_unlock (&mutex); #elif SMTG_OS_WINDOWS BOOL result = PulseEvent (event); if (!result) { SMTG_PRINTSYSERROR; } #endif } //------------------------------------------------------------------------ void FCondition::signalAll () { #if SMTG_PTHREADS pthread_mutex_lock (&mutex); state = waiters + 1; #if DEVELOPMENT signalCount++; #endif pthread_cond_broadcast (&cond); pthread_mutex_unlock (&mutex); #elif SMTG_OS_WINDOWS BOOL result = SetEvent (event); if (!result) { SMTG_PRINTSYSERROR; } #endif } //------------------------------------------------------------------------ void FCondition::wait () { #if SMTG_PTHREADS pthread_mutex_lock (&mutex); #if DEVELOPMENT waits++; #endif waiters++; while (!state) pthread_cond_wait (&cond, &mutex); if (--waiters == 0) state = 0; else --state; pthread_mutex_unlock (&mutex); #elif SMTG_OS_WINDOWS WaitForSingleObject (event, INFINITE); #endif } //------------------------------------------------------------------------ bool FCondition::waitTimeout (int32 milliseconds) { #if SMTG_PTHREADS // this is the implementation running on mac (2018-07-18) if (milliseconds == -1) { // infinite timeout wait (); return true; } struct timespec time; #if __MACH__ // this is the implementation running on mac (2018-07-18) time.tv_sec = milliseconds / 1000; time.tv_nsec = 1000000 * (milliseconds - (time.tv_sec * 1000)); pthread_mutex_lock (&mutex); #if DEVELOPMENT waits++; #endif // this is the implementation running on mac (2018-07-18) waiters++; bool result = true; while (!state) { int32 err = pthread_cond_timedwait_relative_np (&cond, &mutex, &time); if (err == ETIMEDOUT) { result = false; break; } else result = true; } if (--waiters == 0) state = 0; else --state; pthread_mutex_unlock (&mutex); return result; #else // dead code? not compiled in unit test and sequencer (2018-07-18) clock_gettime (CLOCK_REALTIME, &time); time.tv_nsec += milliseconds * 1000; // ????????? pthread_mutex_lock (&mutex); bool result = false; if (pthread_cond_timedwait (&cond, &mutex, &time) == 0) result = true; pthread_mutex_unlock (&mutex); return result; #endif #elif SMTG_OS_WINDOWS if (milliseconds == -1) milliseconds = INFINITE; DWORD result = WaitForSingleObject (event, milliseconds); return result == WAIT_TIMEOUT ? false : true; #endif #if !SMTG_OS_WINDOWS // WARNING ("Return false on time out not implemented!") return true; #endif } //------------------------------------------------------------------------ void FCondition::reset () { #if SMTG_OS_WINDOWS ResetEvent (event); #elif SMTG_PTHREADS state = 0; #endif } //------------------------------------------------------------------------ } // namespace Thread } // namespace Base } // namespace Steinberg qtractor-1.5.11/src/vst3/base/thread/source/PaxHeaders/flock.cpp0000644000000000000000000000013215124701711021464 xustar0030 mtime=1767080905.187046498 30 atime=1767080905.186924355 30 ctime=1767080905.187046498 qtractor-1.5.11/src/vst3/base/thread/source/flock.cpp0000644000175000001440000000621415124701711021457 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/thread/source/flock.cpp // Created by : Steinberg, 1995 // Description : Locks // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #define DEBUG_LOCK 0 #include "base/thread/include/flock.h" #include "base/source/fdebug.h" //------------------------------------------------------------------------ #if SMTG_OS_WINDOWS //------------------------------------------------------------------------ #ifndef WINVER #define WINVER 0x0500 #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT WINVER #endif #include #include #define INIT_CS(cs) \ InitializeCriticalSection ((LPCRITICAL_SECTION)&cs); #endif // SMTG_OS_WINDOWS namespace Steinberg { namespace Base { namespace Thread { //------------------------------------------------------------------------ // FLock implementation //------------------------------------------------------------------------ FLock::FLock (const char8* /*name*/) { #if SMTG_PTHREADS pthread_mutexattr_t mutexAttr; pthread_mutexattr_init (&mutexAttr); pthread_mutexattr_settype (&mutexAttr, PTHREAD_MUTEX_RECURSIVE); if (pthread_mutex_init (&mutex, &mutexAttr) != 0) {SMTG_WARNING("mutex_init failed")} pthread_mutexattr_destroy (&mutexAttr); #elif SMTG_OS_WINDOWS INIT_CS (section) #else #warning implement FLock! #endif } //------------------------------------------------------------------------ FLock::~FLock () { #if SMTG_PTHREADS pthread_mutex_destroy (&mutex); #elif SMTG_OS_WINDOWS DeleteCriticalSection ((LPCRITICAL_SECTION)§ion); #endif } //------------------------------------------------------------------------ void FLock::lock () { #if DEBUG_LOCK FDebugPrint ("FLock::lock () %x\n", this); #endif #if SMTG_PTHREADS pthread_mutex_lock (&mutex); #elif SMTG_OS_WINDOWS EnterCriticalSection ((LPCRITICAL_SECTION)§ion); #endif } //------------------------------------------------------------------------ void FLock::unlock () { #if DEBUG_LOCK FDebugPrint ("FLock::unlock () %x\n", this); #endif #if SMTG_PTHREADS pthread_mutex_unlock (&mutex); #elif SMTG_OS_WINDOWS LeaveCriticalSection ((LPCRITICAL_SECTION)§ion); #endif } //------------------------------------------------------------------------ bool FLock::trylock () { #if SMTG_PTHREADS return pthread_mutex_trylock (&mutex) == 0; #elif SMTG_OS_WINDOWS return TryEnterCriticalSection ((LPCRITICAL_SECTION)§ion) != 0 ? true : false; #else return false; #endif } //------------------------------------------------------------------------ } // namespace Thread } // namespace Base } // namespace Steinberg qtractor-1.5.11/src/vst3/base/PaxHeaders/README.md0000644000000000000000000000013215124701711016372 xustar0030 mtime=1767080905.184211274 30 atime=1767080905.184211274 30 ctime=1767080905.184211274 qtractor-1.5.11/src/vst3/base/README.md0000644000175000001440000000047615124701711016371 0ustar00rncbcusers# Welcome to VST SDK 3 base Here you can find some helper classes useful for developing **VST 3** plug-ins. ## License & Usage guidelines More details are found at [www.steinberg.net/sdklicenses_vst3](http://www.steinberg.net/sdklicenses_vst3) ---- Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) qtractor-1.5.11/src/vst3/base/PaxHeaders/source0000644000000000000000000000013215124701711016336 xustar0030 mtime=1767080905.186211266 30 atime=1767080905.184211274 30 ctime=1767080905.186211266 qtractor-1.5.11/src/vst3/base/source/0000755000175000001440000000000015124701711016403 5ustar00rncbcusersqtractor-1.5.11/src/vst3/base/source/PaxHeaders/fstreamer.cpp0000644000000000000000000000012715124701711021113 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fstreamer.cpp0000644000175000001440000004312115124701711021100 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fstreamer.cpp // Created by : Steinberg, 15.12.2005 // Description : Extract of typed stream i/o methods from FStream // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "fstreamer.h" #include "base/source/fstring.h" #include "base/source/fbuffer.h" #include "pluginterfaces/base/ibstream.h" #ifndef UNICODE #include "pluginterfaces/base/futils.h" #endif namespace Steinberg { //------------------------------------------------------------------------ // IBStreamer //------------------------------------------------------------------------ IBStreamer::IBStreamer (IBStream* stream, int16 _byteOrder) : FStreamer (_byteOrder) , stream (stream) {} //------------------------------------------------------------------------ TSize IBStreamer::readRaw (void* buffer, TSize size) { int32 numBytesRead = 0; stream->read (buffer, (int32)size, &numBytesRead); return numBytesRead; } //------------------------------------------------------------------------ TSize IBStreamer::writeRaw (const void* buffer, TSize size) { int32 numBytesWritten = 0; stream->write ((void*)buffer, (int32)size, &numBytesWritten); return numBytesWritten; } //------------------------------------------------------------------------ int64 IBStreamer::seek (int64 pos, FSeekMode mode) { int64 result = -1; stream->seek (pos, mode, &result); return result; } //------------------------------------------------------------------------ int64 IBStreamer::tell () { int64 pos = 0; stream->tell (&pos); return pos; } //------------------------------------------------------------------------ // FStreamSizeHolder Implementation //------------------------------------------------------------------------ FStreamSizeHolder::FStreamSizeHolder (FStreamer &s) : stream (s), sizePos (-1) {} //------------------------------------------------------------------------ void FStreamSizeHolder::beginWrite () { sizePos = stream.tell (); stream.writeInt32 (0L); } //------------------------------------------------------------------------ int32 FStreamSizeHolder::endWrite () { if (sizePos < 0) return 0; int64 currentPos = stream.tell (); stream.seek (sizePos, kSeekSet); int32 size = int32 (currentPos - sizePos - sizeof (int32)); stream.writeInt32 (size); stream.seek (currentPos, kSeekSet); return size; } //------------------------------------------------------------------------ int32 FStreamSizeHolder::beginRead () { sizePos = stream.tell (); int32 size = 0; stream.readInt32 (size); sizePos += size + sizeof (int32); return size; } //------------------------------------------------------------------------ void FStreamSizeHolder::endRead () { if (sizePos >= 0) stream.seek (sizePos, kSeekSet); } //------------------------------------------------------------------------ // FStreamer //------------------------------------------------------------------------ FStreamer::FStreamer (int16 _byteOrder) : byteOrder (_byteOrder) {} // int8 / char ----------------------------------------------------------- //------------------------------------------------------------------------ bool FStreamer::writeChar8 (char8 c) { return writeRaw ((void*)&c, sizeof (char8)) == sizeof (char8); } //------------------------------------------------------------------------ bool FStreamer::readChar8 (char8& c) { return readRaw ((void*)&c, sizeof (char8)) == sizeof (char8); } //------------------------------------------------------------------------ bool FStreamer::writeUChar8 (unsigned char c) { return writeRaw ((void*)&c, sizeof (unsigned char)) == sizeof (unsigned char); } //------------------------------------------------------------------------ bool FStreamer::readUChar8 (unsigned char& c) { return readRaw ((void*)&c, sizeof (unsigned char)) == sizeof (unsigned char); } //------------------------------------------------------------------------ bool FStreamer::writeChar16 (char16 c) { if (BYTEORDER != byteOrder) SWAP_16 (c); return writeRaw ((void*)&c, sizeof (char16)) == sizeof (char16); } //------------------------------------------------------------------------ bool FStreamer::readChar16 (char16& c) { if (readRaw ((void*)&c, sizeof (char16)) == sizeof (char16)) { if (BYTEORDER != byteOrder) SWAP_16 (c); return true; } c = 0; return false; } //------------------------------------------------------------------------ bool FStreamer::writeInt8 (int8 c) { return writeRaw ((void*)&c, sizeof (int8)) == sizeof (int8); } //------------------------------------------------------------------------ bool FStreamer::readInt8 (int8& c) { return readRaw ((void*)&c, sizeof (int8)) == sizeof (int8); } //------------------------------------------------------------------------ bool FStreamer::writeInt8u (uint8 c) { return writeRaw ((void*)&c, sizeof (uint8)) == sizeof (uint8); } //------------------------------------------------------------------------ bool FStreamer::readInt8u (uint8& c) { return readRaw ((void*)&c, sizeof (uint8)) == sizeof (uint8); } // int16 ----------------------------------------------------------------- //------------------------------------------------------------------------ bool FStreamer::writeInt16 (int16 i) { if (BYTEORDER != byteOrder) SWAP_16 (i); return writeRaw ((void*)&i, sizeof (int16)) == sizeof (int16); } //------------------------------------------------------------------------ bool FStreamer::readInt16 (int16& i) { if (readRaw ((void*)&i, sizeof (int16)) == sizeof (int16)) { if (BYTEORDER != byteOrder) SWAP_16 (i); return true; } i = 0; return false; } //------------------------------------------------------------------------ bool FStreamer::writeInt16Array (const int16* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!writeInt16 (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::readInt16Array (int16* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!readInt16 (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::writeInt16u (uint16 i) { if (BYTEORDER != byteOrder) SWAP_16 (i); return writeRaw ((void*)&i, sizeof (uint16)) == sizeof (uint16); } //------------------------------------------------------------------------ bool FStreamer::readInt16u (uint16& i) { if (readRaw ((void*)&i, sizeof (uint16)) == sizeof (uint16)) { if (BYTEORDER != byteOrder) SWAP_16 (i); return true; } i = 0; return false; } //------------------------------------------------------------------------ bool FStreamer::writeInt16uArray (const uint16* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!writeInt16u (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::readInt16uArray (uint16* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!readInt16u (array[i])) return false; } return true; } // int32 ----------------------------------------------------------------- //------------------------------------------------------------------------ bool FStreamer::writeInt32 (int32 i) { if (BYTEORDER != byteOrder) SWAP_32 (i); return writeRaw ((void*)&i, sizeof (int32)) == sizeof (int32); } //------------------------------------------------------------------------ bool FStreamer::readInt32 (int32& i) { if (readRaw ((void*)&i, sizeof (int32)) == sizeof (int32)) { if (BYTEORDER != byteOrder) SWAP_32 (i); return true; } i = 0; return false; } //------------------------------------------------------------------------ bool FStreamer::writeInt32Array (const int32* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!writeInt32 (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::readInt32Array (int32* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!readInt32 (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::writeInt32u (uint32 i) { if (BYTEORDER != byteOrder) SWAP_32 (i); return writeRaw ((void*)&i, sizeof (uint32)) == sizeof (uint32); } //------------------------------------------------------------------------ bool FStreamer::readInt32u (uint32& i) { if (readRaw ((void*)&i, sizeof (uint32)) == sizeof (uint32)) { if (BYTEORDER != byteOrder) SWAP_32 (i); return true; } i = 0; return false; } //------------------------------------------------------------------------ bool FStreamer::writeInt32uArray (const uint32* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!writeInt32u (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::readInt32uArray (uint32* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!readInt32u (array[i])) return false; } return true; } // int64 ----------------------------------------------------------------- //------------------------------------------------------------------------ bool FStreamer::writeInt64 (int64 i) { if (BYTEORDER != byteOrder) SWAP_64 (i); return writeRaw ((void*)&i, sizeof (int64)) == sizeof (int64); } //------------------------------------------------------------------------ bool FStreamer::readInt64 (int64& i) { if (readRaw ((void*)&i, sizeof (int64)) == sizeof (int64)) { if (BYTEORDER != byteOrder) SWAP_64 (i); return true; } i = 0; return false; } //------------------------------------------------------------------------ bool FStreamer::writeInt64Array (const int64* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!writeInt64 (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::readInt64Array (int64* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!readInt64 (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::writeInt64u (uint64 i) { if (BYTEORDER != byteOrder) SWAP_64 (i); return writeRaw ((void*)&i, sizeof (uint64)) == sizeof (uint64); } //------------------------------------------------------------------------ bool FStreamer::readInt64u (uint64& i) { if (readRaw ((void*)&i, sizeof (uint64)) == sizeof (uint64)) { if (BYTEORDER != byteOrder) SWAP_64 (i); return true; } i = 0; return false; } //------------------------------------------------------------------------ bool FStreamer::writeInt64uArray (const uint64* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!writeInt64u (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::readInt64uArray (uint64* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!readInt64u (array[i])) return false; } return true; } // float / double -------------------------------------------------------- //------------------------------------------------------------------------ bool FStreamer::writeFloat (float f) { if (BYTEORDER != byteOrder) SWAP_32 (f); return writeRaw ((void*)&f, sizeof (float)) == sizeof (float); } //------------------------------------------------------------------------ bool FStreamer::readFloat (float& f) { if (readRaw ((void*)&f, sizeof (float)) == sizeof (float)) { if (BYTEORDER != byteOrder) SWAP_32 (f); return true; } f = 0.f; return false; } //------------------------------------------------------------------------ bool FStreamer::writeFloatArray (const float* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!writeFloat (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::readFloatArray (float* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!readFloat (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::writeDouble (double d) { if (BYTEORDER != byteOrder) SWAP_64 (d); return writeRaw ((void*)&d, sizeof (double)) == sizeof (double); } //------------------------------------------------------------------------ bool FStreamer::readDouble (double& d) { if (readRaw ((void*)&d, sizeof (double)) == sizeof (double)) { if (BYTEORDER != byteOrder) SWAP_64 (d); return true; } d = 0.0; return false; } //------------------------------------------------------------------------ bool FStreamer::writeDoubleArray (const double* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!writeDouble (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::readDoubleArray (double* array, int32 count) { for (int32 i = 0; i < count; i++) { if (!readDouble (array[i])) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::readBool (bool& b) { int16 v = 0; bool res = readInt16 (v); b = (v != 0); return res; } //------------------------------------------------------------------------ bool FStreamer::writeBool (bool b) { return writeInt16 ((int16)b); } //------------------------------------------------------------------------ TSize FStreamer::writeString8 (const char8* ptr, bool terminate) { TSize size = strlen (ptr); if (terminate) // write \0 size++; return writeRaw ((void*)ptr, size); } //------------------------------------------------------------------------ TSize FStreamer::readString8 (char8* ptr, TSize size) { if (size < 1 || ptr == nullptr) return 0; TSize i = 0; char8 c = 0; while (i < size) { if (readRaw ((void*)&c, sizeof (char)) != sizeof (char)) break; ptr[i] = c; if (c == '\n' || c == '\0') break; i++; } // remove at end \n (LF) or \r\n (CR+LF) if (c == '\n') { if (i > 0 && ptr[i - 1] == '\r') i--; } if (i >= size) i = size - 1; ptr[i] = 0; return i; } //------------------------------------------------------------------------ bool FStreamer::writeStringUtf8 (const tchar* ptr) { bool isUtf8 = false; String str (ptr); if (str.isAsciiString () == false) { str.toMultiByte (kCP_Utf8); isUtf8 = true; } else { str.toMultiByte (); } if (isUtf8) if (writeRaw (kBomUtf8, kBomUtf8Length) != kBomUtf8Length) return false; TSize size = str.length () + 1; if (writeRaw (str.text8 (), size) != size) return false; return true; } //------------------------------------------------------------------------ int32 FStreamer::readStringUtf8 (tchar* ptr, int32 nChars) { char8 c = 0; ptr [0] = 0; Buffer tmp; tmp.setDelta (1024); while (true) { if (readRaw ((void*)&c, sizeof (char)) != sizeof (char)) break; tmp.put (c); if (c == '\0') break; } char8* source = tmp.str8 (); uint32 codePage = kCP_Default; // for legacy take default page if no utf8 bom is present... if (tmp.getFillSize () > 2) { if (memcmp (source, kBomUtf8, kBomUtf8Length) == 0) { codePage = kCP_Utf8; source += 3; } } if (tmp.getFillSize () > 1) { #ifdef UNICODE ConstString::multiByteToWideString (ptr, source, nChars, codePage); #else if (codePage == kCP_Utf8) { Buffer wideBuffer (tmp.getFillSize () * 3); ConstString::multiByteToWideString (wideBuffer.wcharPtr (), source, wideBuffer.getSize () / 2, kCP_Utf8); ConstString::wideStringToMultiByte (ptr, wideBuffer.wcharPtr (), nChars); } else { memcpy (ptr, source, Min (nChars, tmp.getFillSize ())); } #endif } ptr[nChars - 1] = 0; return ConstString (ptr).length (); } //------------------------------------------------------------------------ bool FStreamer::writeStr8 (const char8* s) { int32 length = (s) ? (int32) strlen (s) + 1 : 0; if (!writeInt32 (length)) return false; if (length > 0) return writeRaw (s, sizeof (char8) * length) == static_cast(sizeof (char8) * length); return true; } //------------------------------------------------------------------------ int32 FStreamer::getStr8Size (const char8* s) { return sizeof (int32) + (int32)strlen (s) + 1; } //------------------------------------------------------------------------ char8* FStreamer::readStr8 () { int32 length; if (!readInt32 (length)) return nullptr; // check corruption if (length > 262144) return nullptr; char8* s = (length > 0) ? NEWSTR8 (length) : nullptr; if (s) readRaw (s, length * sizeof (char8)); return s; } //------------------------------------------------------------------------ bool FStreamer::skip (uint32 bytes) { int8 tmp; while (bytes-- > 0) { if (readInt8 (tmp) == false) return false; } return true; } //------------------------------------------------------------------------ bool FStreamer::pad (uint32 bytes) { while (bytes-- > 0) { if (writeInt8 (0) == false) return false; } return true; } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fstring.cpp0000644000000000000000000000012715124701711020577 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fstring.cpp0000644000175000001440000027261615124701711020601 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fstring.cpp // Created by : Steinberg, 2008 // Description : String class // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "base/source/fstring.h" #include "base/source/fdebug.h" #include "pluginterfaces/base/futils.h" #include "pluginterfaces/base/fvariant.h" #include #include #include #include #include #include #include #include #include #if SMTG_OS_WINDOWS #ifndef NOMINMAX #define NOMINMAX #endif #include #ifdef _MSC_VER #pragma warning(disable : 4244) #pragma warning(disable : 4267) #pragma warning(disable : 4996) #if DEVELOPMENT #include #define malloc(s) _malloc_dbg (s, _NORMAL_BLOCK, __FILE__, __LINE__) #define realloc(p, s) _realloc_dbg (p, s, _NORMAL_BLOCK, __FILE__, __LINE__) #define free(p) _free_dbg (p, _NORMAL_BLOCK) #endif // DEVELOPMENT #endif // _MSC_VER #endif // SMTG_OS_WINDOWS #ifndef kPrintfBufferSize #define kPrintfBufferSize 4096 #endif #if SMTG_OS_MACOS #include #include #include #include #if defined(__GNUC__) && (__GNUC__ >= 4) && !__LP64__ // on 32 bit Mac OS X we can safely ignore the format warnings as sizeof(int) == sizeof(long) #pragma GCC diagnostic ignored "-Wformat" #endif #define SMTG_ENABLE_DEBUG_CFALLOCATOR 0 #define SMTG_DEBUG_CFALLOCATOR (DEVELOPMENT && SMTG_ENABLE_DEBUG_CFALLOCATOR) #if SMTG_DEBUG_CFALLOCATOR #include #include #endif namespace Steinberg { #if SMTG_DEBUG_CFALLOCATOR static CFAllocatorRef kCFAllocator = NULL; struct CFStringDebugAllocator : CFAllocatorContext { CFStringDebugAllocator () { version = 0; info = this; retain = nullptr; release = nullptr; copyDescription = nullptr; allocate = allocateCallBack; reallocate = reallocateCallBack; deallocate = deallocateCallBack; preferredSize = preferredSizeCallBack; numAllocations = allocationSize = numDeallocations = 0; cfAllocator = CFAllocatorCreate (kCFAllocatorUseContext, this); Dl_info info; if (dladdr ((const void*)CFStringDebugAllocator::allocateCallBack, &info)) { moduleName = info.dli_fname; } kCFAllocator = cfAllocator; } ~CFStringDebugAllocator () { kCFAllocator = kCFAllocatorDefault; CFRelease (cfAllocator); FDebugPrint ("CFStringDebugAllocator (%s):\n", moduleName.text8 ()); FDebugPrint ("\tNumber of allocations : %u\n", numAllocations); FDebugPrint ("\tNumber of deallocations: %u\n", numDeallocations); FDebugPrint ("\tAllocated Bytes : %u\n", allocationSize); } String moduleName; CFAllocatorRef cfAllocator; volatile int64_t numAllocations; volatile int64_t numDeallocations; volatile int64_t allocationSize; void* doAllocate (CFIndex allocSize, CFOptionFlags hint) { void* ptr = CFAllocatorAllocate (kCFAllocatorDefault, allocSize, hint); OSAtomicIncrement64 (&numAllocations); OSAtomicAdd64 (allocSize, &allocationSize); return ptr; } void* doReallocate (void* ptr, CFIndex newsize, CFOptionFlags hint) { void* newPtr = CFAllocatorReallocate (kCFAllocatorDefault, ptr, newsize, hint); return newPtr; } void doDeallocate (void* ptr) { CFAllocatorDeallocate (kCFAllocatorDefault, ptr); OSAtomicIncrement64 (&numDeallocations); } CFIndex getPreferredSize (CFIndex size, CFOptionFlags hint) { return CFAllocatorGetPreferredSizeForSize (kCFAllocatorDefault, size, hint); } static void* allocateCallBack (CFIndex allocSize, CFOptionFlags hint, void* info) { return static_cast (info)->doAllocate (allocSize, hint); } static void* reallocateCallBack (void* ptr, CFIndex newsize, CFOptionFlags hint, void* info) { return static_cast (info)->doReallocate (ptr, newsize, hint); } static void deallocateCallBack (void* ptr, void* info) { static_cast (info)->doDeallocate (ptr); } static CFIndex preferredSizeCallBack (CFIndex size, CFOptionFlags hint, void* info) { return static_cast (info)->getPreferredSize (size, hint); } }; static CFStringDebugAllocator gDebugAllocator; #else static const CFAllocatorRef kCFAllocator = ::kCFAllocatorDefault; #endif // SMTG_DEBUG_CFALLOCATOR } //----------------------------------------------------------------------------- static void* toCFStringRef (const Steinberg::char8* source, Steinberg::uint32 encoding) { if (encoding == 0xFFFF) encoding = kCFStringEncodingASCII; if (source) return (void*)CFStringCreateWithCString (Steinberg::kCFAllocator, source, encoding); else return (void*)CFStringCreateWithCString (Steinberg::kCFAllocator, "", encoding); } //----------------------------------------------------------------------------- static bool fromCFStringRef (Steinberg::char8* dest, Steinberg::int32 destSize, const void* cfStr, Steinberg::uint32 encoding) { CFIndex usedBytes; CFRange range = {0, CFStringGetLength ((CFStringRef)cfStr)}; bool result = CFStringGetBytes ((CFStringRef)cfStr, range, encoding, '?', false, (UInt8*)dest, destSize, &usedBytes); dest[usedBytes] = 0; return result; } #endif // SMTG_OS_MACOS #if SMTG_OS_WINDOWS //----------------------------------------------------------------------------- static inline int stricmp16 (const Steinberg::tchar* s1, const Steinberg::tchar* s2) { return wcsicmp (Steinberg::wscast (s1), Steinberg::wscast (s2)); } //----------------------------------------------------------------------------- static inline int strnicmp16 (const Steinberg::tchar* s1, const Steinberg::tchar* s2, size_t l) { return wcsnicmp (Steinberg::wscast (s1), Steinberg::wscast (s2), l); } //----------------------------------------------------------------------------- static inline int vsnwprintf (Steinberg::char16* buffer, size_t bufferSize, const Steinberg::char16* format, va_list args) { return _vsnwprintf (Steinberg::wscast (buffer), bufferSize, Steinberg::wscast (format), args); } //----------------------------------------------------------------------------- static inline Steinberg::int32 sprintf16 (Steinberg::char16* str, const Steinberg::char16* format, ...) { va_list marker; va_start (marker, format); return vsnwprintf (str, static_cast (-1), format, marker); } #elif SMTG_OS_LINUX #include #include #include #include #include #include #include using ConverterFacet = std::codecvt_utf8_utf16; using Converter = std::wstring_convert; //------------------------------------------------------------------------ static ConverterFacet& converterFacet () { static ConverterFacet gFacet; return gFacet; } //------------------------------------------------------------------------ static Converter& converter () { static Converter gConverter; return gConverter; } //----------------------------------------------------------------------------- static inline int stricasecmp (const Steinberg::char8* s1, const Steinberg::char8* s2) { return ::strcasecmp (s1, s2); } //----------------------------------------------------------------------------- static inline int strnicasecmp (const Steinberg::char8* s1, const Steinberg::char8* s2, size_t n) { return ::strncasecmp (s1, s2, n); } //----------------------------------------------------------------------------- static inline int stricmp16 (const Steinberg::char16* s1, const Steinberg::char16* s2) { auto str1 = converter ().to_bytes (s1); auto str2 = converter ().to_bytes (s2); return stricasecmp (str1.data (), str2.data ()); } //----------------------------------------------------------------------------- static inline int strnicmp16 (const Steinberg::char16* s1, const Steinberg::char16* s2, int n) { auto str1 = converter ().to_bytes (s1); auto str2 = converter ().to_bytes (s2); return strnicasecmp (str1.data (), str2.data (), n); } //----------------------------------------------------------------------------- static inline int sprintf16 (Steinberg::char16* wcs, const Steinberg::char16* format, ...) { assert (false && "DEPRECATED No Linux implementation"); return 0; } //----------------------------------------------------------------------------- static inline int vsnwprintf (Steinberg::char16* wcs, size_t maxlen, const Steinberg::char16* format, va_list args) { Steinberg::char8 str8[kPrintfBufferSize]; auto format_utf8 = converter ().to_bytes (format); auto len = vsnprintf (str8, kPrintfBufferSize, format_utf8.data (), args); auto tmp_str = converter ().from_bytes (str8, str8 + len); auto target_len = std::min (tmp_str.size (), maxlen - 1); tmp_str.copy (wcs, target_len); wcs[target_len] = '\0'; return tmp_str.size (); } //----------------------------------------------------------------------------- static inline Steinberg::char16* strrchr16 (const Steinberg::char16* str, Steinberg::char16 c) { assert (false && "DEPRECATED No Linux implementation"); return nullptr; } #elif SMTG_OS_MACOS #define tstrtoi64 strtoll #define stricmp strcasecmp #define strnicmp strncasecmp //----------------------------------------------------------------------------- static inline Steinberg::int32 strnicmp16 (const Steinberg::char16* str1, const Steinberg::char16* str2, size_t size) { if (size == 0) return 0; CFIndex str1Len = Steinberg::strlen16 (str1); CFIndex str2Len = Steinberg::strlen16 (str2); if (static_cast (size) < str2Len) // range is not applied to second string str2Len = size; CFStringRef cfStr1 = CFStringCreateWithCharactersNoCopy ( Steinberg::kCFAllocator, (UniChar*)str1, str1Len, kCFAllocatorNull); CFStringRef cfStr2 = CFStringCreateWithCharactersNoCopy ( Steinberg::kCFAllocator, (UniChar*)str2, str2Len, kCFAllocatorNull); CFComparisonResult result = CFStringCompareWithOptions (cfStr1, cfStr2, CFRangeMake (0, size), kCFCompareCaseInsensitive); CFRelease (cfStr1); CFRelease (cfStr2); switch (result) { case kCFCompareEqualTo: return 0; case kCFCompareLessThan: return -1; case kCFCompareGreaterThan: default: return 1; }; } //----------------------------------------------------------------------------- static inline Steinberg::int32 stricmp16 (const Steinberg::char16* str1, CFIndex str1Len, const Steinberg::char16* str2, CFIndex str2Len) { CFStringRef cfStr1 = CFStringCreateWithCharactersNoCopy ( Steinberg::kCFAllocator, (UniChar*)str1, str1Len, kCFAllocatorNull); CFStringRef cfStr2 = CFStringCreateWithCharactersNoCopy ( Steinberg::kCFAllocator, (UniChar*)str2, str2Len, kCFAllocatorNull); CFComparisonResult result = CFStringCompare (cfStr1, cfStr2, kCFCompareCaseInsensitive); CFRelease (cfStr1); CFRelease (cfStr2); switch (result) { case kCFCompareEqualTo: return 0; case kCFCompareLessThan: return -1; case kCFCompareGreaterThan: default: return 1; }; } //----------------------------------------------------------------------------- static inline Steinberg::int32 stricmp16 (const Steinberg::ConstString& str1, const Steinberg::ConstString& str2) { return stricmp16 (str1.text16 (), str1.length (), str2.text16 (), str2.length ()); } //----------------------------------------------------------------------------- static inline Steinberg::int32 stricmp16 (const Steinberg::char16* str1, const Steinberg::char16* str2) { CFIndex str1Len = Steinberg::strlen16 (str1); CFIndex str2Len = Steinberg::strlen16 (str2); return stricmp16 (str1, str1Len, str2, str2Len); } //----------------------------------------------------------------------------- static inline Steinberg::char16* strrchr16 (const Steinberg::char16* str, Steinberg::char16 c) { Steinberg::int32 len = Steinberg::ConstString (str).length (); while (len > 0) { if (str[len] == c) return const_cast (str + len); len--; } return 0; } //----------------------------------------------------------------------------- static inline Steinberg::int32 vsnwprintf (Steinberg::char16* str, Steinberg::int32 size, const Steinberg::char16* format, va_list ap) { // wrapped using CoreFoundation's CFString CFMutableStringRef formatString = (CFMutableStringRef)Steinberg::ConstString (format).toCFStringRef (0xFFFF, true); CFStringFindAndReplace (formatString, CFSTR ("%s"), CFSTR ("%S"), CFRangeMake (0, CFStringGetLength (formatString)), 0); CFStringRef resultString = CFStringCreateWithFormatAndArguments (Steinberg::kCFAllocator, 0, formatString, ap); CFRelease (formatString); if (resultString) { Steinberg::String res; res.fromCFStringRef (resultString); res.copyTo16 (str, 0, size); CFRelease (resultString); return 0; } return 1; } //----------------------------------------------------------------------------- static inline Steinberg::int32 sprintf16 (Steinberg::char16* str, const Steinberg::char16* format, ...) { va_list marker; va_start (marker, format); return vsnwprintf (str, -1, format, marker); } #endif // SMTG_OS_LINUX /* UTF-8 EF BB BF UTF-16 Big Endian FE FF UTF-16 Little Endian FF FE UTF-32 Big Endian 00 00 FE FF UTF-32 Little Endian FF FE 00 00 */ namespace Steinberg { //----------------------------------------------------------------------------- static inline bool isCaseSensitive (ConstString::CompareMode mode) { return mode == ConstString::kCaseSensitive; } //----------------------------------------------------------------------------- // ConstString //----------------------------------------------------------------------------- ConstString::ConstString (const char8* str, int32 length) : buffer8 ((char8*)str) , len (length < 0 ? (str ? static_cast (strlen (str)) : 0) : length) , isWide (0) { } //----------------------------------------------------------------------------- ConstString::ConstString (const char16* str, int32 length) : buffer16 ((char16*)str), len (length < 0 ? (str ? strlen16 (str) : 0) : length), isWide (1) { } //----------------------------------------------------------------------------- ConstString::ConstString (const ConstString& str, int32 offset, int32 length) : buffer (str.buffer) , len (length < 0 ? (str.len - (offset > 0 ? offset : 0)) : length) , isWide (str.isWide) { if (offset > 0) { if (isWide) buffer16 += offset; else buffer8 += offset; } } //----------------------------------------------------------------------------- ConstString::ConstString (const FVariant& var) : buffer (nullptr), len (0), isWide (0) { switch (var.getType ()) { case FVariant::kString8: buffer8 = (char8*)var.getString8 (); len = buffer8 ? strlen8 (buffer8) : 0; isWide = false; break; case FVariant::kString16: buffer16 = (char16*)var.getString16 (); len = buffer16 ? strlen16 (buffer16) : 0; isWide = true; break; } } //----------------------------------------------------------------------------- ConstString::ConstString () : buffer (nullptr), len (0), isWide (0) { } //----------------------------------------------------------------------------- bool ConstString::testChar8 (uint32 index, char8 c) const { if (index >= len) return c == 0; if (isWide) { // make c wide char8 src[] = {c, 0}; char16 dest[2] = {0}; if (multiByteToWideString (dest, src, 2) > 0) return buffer16[index] == dest[0]; return false; } return buffer8[index] == c; } //----------------------------------------------------------------------------- bool ConstString::testChar16 (uint32 index, char16 c) const { if (index >= len) return c == 0; if (!isWide) { // make c ansi char16 src[] = {c, 0}; char8 dest[8] = {0}; if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0) return buffer8[index] == dest[0]; return false; } return buffer16[index] == c; } //----------------------------------------------------------------------------- bool ConstString::extract (String& result, uint32 idx, int32 n) const { // AddressSanitizer : when extracting part of "this" on itself, it can lead to // heap-use-after-free. SMTG_ASSERT (this != static_cast (&result)) if (len == 0 || idx >= len) return false; if ((idx + n > len) || n < 0) n = len - idx; if (isWide) result.assign (buffer16 + idx, n); else result.assign (buffer8 + idx, n); return true; } //----------------------------------------------------------------------------- int32 ConstString::copyTo8 (char8* str, uint32 idx, int32 n) const { if (!str) return 0; if (isWide) { String tmp (text16 ()); if (tmp.toMultiByte () == false) return 0; return tmp.copyTo8 (str, idx, n); } if (isEmpty () || idx >= len || !buffer8) { str[0] = 0; return 0; } if ((idx + n > len) || n < 0) n = len - idx; memcpy (str, &(buffer8[idx]), n * sizeof (char8)); str[n] = 0; return n; } //----------------------------------------------------------------------------- int32 ConstString::copyTo16 (char16* str, uint32 idx, int32 n) const { if (!str) return 0; if (!isWide) { String tmp (text8 ()); if (tmp.toWideString () == false) return 0; return tmp.copyTo16 (str, idx, n); } if (isEmpty () || idx >= len || !buffer16) { str[0] = 0; return 0; } if ((idx + n > len) || n < 0) n = len - idx; memcpy (str, &(buffer16[idx]), n * sizeof (char16)); str[n] = 0; return n; } //----------------------------------------------------------------------------- int32 ConstString::copyTo (tchar* str, uint32 idx, int32 n) const { #ifdef UNICODE return copyTo16 (str, idx, n); #else return copyTo8 (str, idx, n); #endif } //----------------------------------------------------------------------------- void ConstString::copyTo (IStringResult* result) const { if (isWideString () == false) { result->setText (text8 ()); } else { FUnknownPtr iStr (result); if (iStr) { iStr->setText16 (text16 ()); } else { String tmp (*this); tmp.toMultiByte (); result->setText (tmp.text8 ()); } } } //----------------------------------------------------------------------------- void ConstString::copyTo (IString& string) const { if (isWideString ()) string.setText16 (text16 ()); else string.setText8 (text8 ()); } //----------------------------------------------------------------------------- int32 ConstString::compare (const ConstString& str, int32 n, CompareMode mode) const { if (n == 0) return 0; if (str.isEmpty ()) { if (isEmpty ()) return 0; return 1; } if (isEmpty ()) return -1; if (!isWide && !str.isWide) { if (n < 0) { if (isCaseSensitive (mode)) return strcmp (*this, str); return stricmp (*this, str); } if (isCaseSensitive (mode)) return strncmp (*this, str, n); return strnicmp (*this, str, n); } if (isWide && str.isWide) { if (n < 0) { if (isCaseSensitive (mode)) return strcmp16 (*this, str); return stricmp16 (*this, str); } if (isCaseSensitive (mode)) return strncmp16 (*this, str, n); return strnicmp16 (*this, str, n); } return compareAt (0, str, n, mode); } //----------------------------------------------------------------------------- int32 ConstString::compare (const ConstString& str, CompareMode mode) const { return compare (str, -1, mode); } //----------------------------------------------------------------------------- int32 ConstString::compareAt (uint32 index, const ConstString& str, int32 n, CompareMode mode) const { if (n == 0) return 0; if (str.isEmpty ()) { if (isEmpty ()) return 0; return 1; } if (isEmpty ()) return -1; if (!isWide && !str.isWide) { char8* toCompare = buffer8; if (index > 0) { if (index >= len) { if (str.isEmpty ()) return 0; return -1; } toCompare += index; } if (n < 0) { if (isCaseSensitive (mode)) return strcmp (toCompare, str); return stricmp (toCompare, str); } if (isCaseSensitive (mode)) return strncmp (toCompare, str, n); return strnicmp (toCompare, str, n); } if (isWide && str.isWide) { char16* toCompare = buffer16; if (index > 0) { if (index >= len) { if (str.isEmpty ()) return 0; return -1; } toCompare += index; } if (n < 0) { if (isCaseSensitive (mode)) return strcmp16 (toCompare, str.text16 ()); return stricmp16 (toCompare, str.text16 ()); } if (isCaseSensitive (mode)) return strncmp16 (toCompare, str.text16 (), n); return strnicmp16 (toCompare, str.text16 (), n); } if (isWide) { String tmp (str.text8 ()); if (tmp.toWideString () == false) return -1; return compareAt (index, tmp, n, mode); } String tmp (text8 ()); if (tmp.toWideString () == false) return 1; return tmp.compareAt (index, str, n, mode); } //------------------------------------------------------------------------ Steinberg::int32 ConstString::naturalCompare (const ConstString& str, CompareMode mode /*= kCaseSensitive*/) const { if (str.isEmpty ()) { if (isEmpty ()) return 0; return 1; } if (isEmpty ()) return -1; if (!isWide && !str.isWide) return strnatcmp8 (buffer8, str.text8 (), isCaseSensitive (mode)); if (isWide && str.isWide) return strnatcmp16 (buffer16, str.text16 (), isCaseSensitive (mode)); if (isWide) { String tmp (str.text8 ()); tmp.toWideString (); return strnatcmp16 (buffer16, tmp.text16 (), isCaseSensitive (mode)); } String tmp (text8 ()); tmp.toWideString (); return strnatcmp16 (tmp.text16 (), str.text16 (), isCaseSensitive (mode)); } //----------------------------------------------------------------------------- bool ConstString::startsWith (const ConstString& str, CompareMode mode /*= kCaseSensitive*/) const { if (str.isEmpty ()) { return isEmpty (); } if (isEmpty ()) { return false; } if (length () < str.length ()) { return false; } if (!isWide && !str.isWide) { if (isCaseSensitive (mode)) return strncmp (buffer8, str.buffer8, str.length ()) == 0; return strnicmp (buffer8, str.buffer8, str.length ()) == 0; } if (isWide && str.isWide) { if (isCaseSensitive (mode)) return strncmp16 (buffer16, str.buffer16, str.length ()) == 0; return strnicmp16 (buffer16, str.buffer16, str.length ()) == 0; } if (isWide) { String tmp (str.text8 ()); tmp.toWideString (); if (tmp.length () > length ()) return false; if (isCaseSensitive (mode)) return strncmp16 (buffer16, tmp.buffer16, tmp.length ()) == 0; return strnicmp16 (buffer16, tmp.buffer16, tmp.length ()) == 0; } String tmp (text8 ()); tmp.toWideString (); if (str.length () > tmp.length ()) return false; if (isCaseSensitive (mode)) return strncmp16 (tmp.buffer16, str.buffer16, str.length ()) == 0; return strnicmp16 (tmp.buffer16, str.buffer16, str.length ()) == 0; } //----------------------------------------------------------------------------- bool ConstString::endsWith (const ConstString& str, CompareMode mode /*= kCaseSensitive*/) const { if (str.isEmpty ()) { return isEmpty (); } if (isEmpty ()) { return false; } if (length () < str.length ()) { return false; } if (!isWide && !str.isWide) { if (isCaseSensitive (mode)) return strncmp (buffer8 + (length () - str.length ()), str.buffer8, str.length ()) == 0; return strnicmp (buffer8 + (length () - str.length ()), str.buffer8, str.length ()) == 0; } if (isWide && str.isWide) { if (isCaseSensitive (mode)) return strncmp16 (buffer16 + (length () - str.length ()), str.buffer16, str.length ()) == 0; return strnicmp16 (buffer16 + (length () - str.length ()), str.buffer16, str.length ()) == 0; } if (isWide) { String tmp (str.text8 ()); tmp.toWideString (); if (tmp.length () > length ()) return false; if (isCaseSensitive (mode)) return strncmp16 (buffer16 + (length () - tmp.length ()), tmp.buffer16, tmp.length ()) == 0; return strnicmp16 (buffer16 + (length () - tmp.length ()), tmp.buffer16, tmp.length ()) == 0; } String tmp (text8 ()); tmp.toWideString (); if (str.length () > tmp.length ()) return false; if (isCaseSensitive (mode)) return strncmp16 (tmp.buffer16 + (tmp.length () - str.length ()), str.buffer16, str.length ()) == 0; return strnicmp16 (tmp.buffer16 + (tmp.length () - str.length ()), str.buffer16, str.length ()) == 0; } //----------------------------------------------------------------------------- bool ConstString::contains (const ConstString& str, CompareMode m) const { return findFirst (str, -1, m) != -1; } //----------------------------------------------------------------------------- int32 ConstString::findNext (int32 startIndex, const ConstString& str, int32 n, CompareMode mode, int32 endIndex) const { uint32 endLength = len; if (endIndex > -1 && (uint32)endIndex < len) endLength = endIndex + 1; if (isWide && str.isWide) { if (startIndex < 0) startIndex = 0; uint32 stringLength = str.length (); n = n < 0 ? stringLength : Min (n, stringLength); if (n > 0) { uint32 i = 0; if (isCaseSensitive (mode)) { for (i = startIndex; i < endLength; i++) if (strncmp16 (buffer16 + i, str, n) == 0) return i; } else { for (i = startIndex; i < endLength; i++) if (strnicmp16 (buffer16 + i, str, n) == 0) return i; } } return -1; } if (!isWide && !str.isWide) { uint32 stringLength = str.length (); n = n < 0 ? stringLength : Min (n, stringLength); if (startIndex < 0) startIndex = 0; if (n > 0) { uint32 i = 0; if (isCaseSensitive (mode)) { for (i = startIndex; i < endLength; i++) if (strncmp (buffer8 + i, str, n) == 0) return i; } else { for (i = startIndex; i < endLength; i++) if (strnicmp (buffer8 + i, str, n) == 0) return i; } } return -1; } String tmp; if (isWide) { tmp = str.text8 (); tmp.toWideString (); return findNext (startIndex, tmp, n, mode, endIndex); } tmp = text8 (); tmp.toWideString (); return tmp.findNext (startIndex, str, n, mode, endIndex); } //------------------------------------------------------------------------------------------------ int32 ConstString::findNext (int32 startIndex, char8 c, CompareMode mode, int32 endIndex) const { uint32 endLength = len; if (endIndex > -1 && (uint32)endIndex < len) endLength = endIndex + 1; if (isWide) { char8 src[] = {c, 0}; char16 dest[8] = {0}; if (multiByteToWideString (dest, src, 2) > 0) return findNext (startIndex, dest[0], mode, endIndex); return -1; } if (startIndex < 0) startIndex = 0; uint32 i; if (isCaseSensitive (mode)) { for (i = startIndex; i < endLength; i++) { if (buffer8[i] == c) return i; } } else { c = toLower (c); for (i = startIndex; i < endLength; i++) { if (toLower (buffer8[i]) == c) return i; } } return -1; } //----------------------------------------------------------------------------- int32 ConstString::findNext (int32 startIndex, char16 c, CompareMode mode, int32 endIndex) const { uint32 endLength = len; if (endIndex > -1 && (uint32)endIndex < len) endLength = endIndex + 1; if (!isWide) { char16 src[] = {c, 0}; char8 dest[8] = {0}; if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0) return findNext (startIndex, dest[0], mode, endIndex); return -1; } uint32 i; if (startIndex < 0) startIndex = 0; if (isCaseSensitive (mode)) { for (i = startIndex; i < endLength; i++) { if (buffer16[i] == c) return i; } } else { c = toLower (c); for (i = startIndex; i < endLength; i++) { if (toLower (buffer16[i]) == c) return i; } } return -1; } //----------------------------------------------------------------------------- int32 ConstString::findPrev (int32 startIndex, char8 c, CompareMode mode) const { if (len == 0) return -1; if (isWide) { char8 src[] = {c, 0}; char16 dest[8] = {0}; if (multiByteToWideString (dest, src, 2) > 0) return findPrev (startIndex, dest[0], mode); return -1; } if (startIndex < 0 || startIndex > (int32)len) startIndex = len; int32 i; if (isCaseSensitive (mode)) { for (i = startIndex; i >= 0; i--) { if (buffer8[i] == c) return i; } } else { c = toLower (c); for (i = startIndex; i >= 0; i--) { if (toLower (buffer8[i]) == c) return i; } } return -1; } //----------------------------------------------------------------------------- int32 ConstString::findPrev (int32 startIndex, char16 c, CompareMode mode) const { if (len == 0) return -1; if (!isWide) { char16 src[] = {c, 0}; char8 dest[8] = {0}; if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0) return findPrev (startIndex, dest[0], mode); return -1; } if (startIndex < 0 || startIndex > (int32)len) startIndex = len; int32 i; if (isCaseSensitive (mode)) { for (i = startIndex; i >= 0; i--) { if (buffer16[i] == c) return i; } } else { c = toLower (c); for (i = startIndex; i >= 0; i--) { if (toLower (buffer16[i]) == c) return i; } } return -1; } //----------------------------------------------------------------------------- int32 ConstString::findPrev (int32 startIndex, const ConstString& str, int32 n, CompareMode mode) const { if (isWide && str.isWide) { uint32 stringLength = str.length (); n = n < 0 ? stringLength : Min (n, stringLength); if (startIndex < 0 || startIndex >= (int32)len) startIndex = len - 1; if (n > 0) { int32 i = 0; if (isCaseSensitive (mode)) { for (i = startIndex; i >= 0; i--) if (strncmp16 (buffer16 + i, str, n) == 0) return i; } else { for (i = startIndex; i >= 0; i--) if (strnicmp16 (buffer16 + i, str, n) == 0) return i; } } return -1; } if (!isWide && !str.isWide) { uint32 stringLength = str.length (); n = n < 0 ? stringLength : Min (n, stringLength); if (startIndex < 0 || startIndex >= (int32)len) startIndex = len - 1; if (n > 0) { int32 i = 0; if (isCaseSensitive (mode)) { for (i = startIndex; i >= 0; i--) if (strncmp (buffer8 + i, str, n) == 0) return i; } else { for (i = startIndex; i >= 0; i--) if (strnicmp (buffer8 + i, str, n) == 0) return i; } } return -1; } if (isWide) { String tmp (str.text8 ()); tmp.toWideString (); return findPrev (startIndex, tmp, n, mode); } String tmp (text8 ()); tmp.toWideString (); return tmp.findPrev (startIndex, str, n, mode); } //----------------------------------------------------------------------------- int32 ConstString::countOccurences (char8 c, uint32 startIndex, CompareMode mode) const { if (isWide) { char8 src[] = {c, 0}; char16 dest[8] = {0}; if (multiByteToWideString (dest, src, 2) > 0) return countOccurences (dest[0], startIndex, mode); return -1; } int32 result = 0; int32 next = startIndex; while (true) { next = findNext (next, c, mode); if (next >= 0) { next++; result++; } else break; } return result; } //----------------------------------------------------------------------------- int32 ConstString::countOccurences (char16 c, uint32 startIndex, CompareMode mode) const { if (!isWide) { char16 src[] = {c, 0}; char8 dest[8] = {0}; if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0) return countOccurences (dest[0], startIndex, mode); return -1; } int32 result = 0; int32 next = startIndex; while (true) { next = findNext (next, c, mode); if (next >= 0) { next++; result++; } else break; } return result; } //----------------------------------------------------------------------------- int32 ConstString::getFirstDifferent (const ConstString& str, CompareMode mode) const { if (str.isWide != isWide) { if (isWide) { String tmp (str.text8 ()); if (tmp.toWideString () == false) return -1; return getFirstDifferent (tmp, mode); } String tmp (text8 ()); if (tmp.toWideString () == false) return -1; return tmp.getFirstDifferent (str, mode); } uint32 len1 = len; uint32 len2 = str.len; uint32 i; if (isWide) { if (isCaseSensitive (mode)) { for (i = 0; i <= len1 && i <= len2; i++) { if (buffer16[i] != str.buffer16[i]) return i; } } else { for (i = 0; i <= len1 && i <= len2; i++) { if (toLower (buffer16[i]) != toLower (str.buffer16[i])) return i; } } } else { if (isCaseSensitive (mode)) { for (i = 0; i <= len1 && i <= len2; i++) { if (buffer8[i] != str.buffer8[i]) return i; } } else { for (i = 0; i <= len1 && i <= len2; i++) { if (toLower (buffer8[i]) != toLower (str.buffer8[i])) return i; } } } return -1; } //----------------------------------------------------------------------------- bool ConstString::scanInt64 (int64& value, uint32 offset, bool scanToEnd) const { if (isEmpty () || offset >= len) return false; if (isWide) return scanInt64_16 (buffer16 + offset, value, scanToEnd); return scanInt64_8 (buffer8 + offset, value, scanToEnd); } //----------------------------------------------------------------------------- bool ConstString::scanUInt64 (uint64& value, uint32 offset, bool scanToEnd) const { if (isEmpty () || offset >= len) return false; if (isWide) return scanUInt64_16 (buffer16 + offset, value, scanToEnd); return scanUInt64_8 (buffer8 + offset, value, scanToEnd); } //----------------------------------------------------------------------------- bool ConstString::scanHex (uint8& value, uint32 offset, bool scanToEnd) const { if (isEmpty () || offset >= len) return false; if (isWide) return scanHex_16 (buffer16 + offset, value, scanToEnd); return scanHex_8 (buffer8 + offset, value, scanToEnd); } //----------------------------------------------------------------------------- bool ConstString::scanInt32 (int32& value, uint32 offset, bool scanToEnd) const { if (isEmpty () || offset >= len) return false; if (isWide) return scanInt32_16 (buffer16 + offset, value, scanToEnd); return scanInt32_8 (buffer8 + offset, value, scanToEnd); } //----------------------------------------------------------------------------- bool ConstString::scanUInt32 (uint32& value, uint32 offset, bool scanToEnd) const { if (isEmpty () || offset >= len) return false; if (isWide) return scanUInt32_16 (buffer16 + offset, value, scanToEnd); return scanUInt32_8 (buffer8 + offset, value, scanToEnd); } //----------------------------------------------------------------------------- bool ConstString::scanInt64_8 (const char8* text, int64& value, bool scanToEnd) { while (text && text[0]) { if (sscanf (text, "%" FORMAT_INT64A, &value) == 1) return true; if (scanToEnd == false) return false; text++; } return false; } //----------------------------------------------------------------------------- bool ConstString::scanInt64_16 (const char16* text, int64& value, bool scanToEnd) { if (text && text[0]) { String str (text); str.toMultiByte (kCP_Default); return scanInt64_8 (str, value, scanToEnd); } return false; } //----------------------------------------------------------------------------- bool ConstString::scanUInt64_8 (const char8* text, uint64& value, bool scanToEnd) { while (text && text[0]) { if (sscanf (text, "%" FORMAT_UINT64A, &value) == 1) return true; if (scanToEnd == false) return false; text++; } return false; } //----------------------------------------------------------------------------- bool ConstString::scanUInt64_16 (const char16* text, uint64& value, bool scanToEnd) { if (text && text[0]) { String str (text); str.toMultiByte (kCP_Default); return scanUInt64_8 (str, value, scanToEnd); } return false; } //----------------------------------------------------------------------------- bool ConstString::scanInt64 (const tchar* text, int64& value, bool scanToEnd) { #ifdef UNICODE return scanInt64_16 (text, value, scanToEnd); #else return scanInt64_8 (text, value, scanToEnd); #endif } //----------------------------------------------------------------------------- bool ConstString::scanUInt64 (const tchar* text, uint64& value, bool scanToEnd) { #ifdef UNICODE return scanUInt64_16 (text, value, scanToEnd); #else return scanUInt64_8 (text, value, scanToEnd); #endif } //----------------------------------------------------------------------------- bool ConstString::scanHex_8 (const char8* text, uint8& value, bool scanToEnd) { while (text && text[0]) { unsigned int v; // scanf expects an unsigned int for %x if (sscanf (text, "%x", &v) == 1) { value = (uint8)v; return true; } if (scanToEnd == false) return false; text++; } return false; } //----------------------------------------------------------------------------- bool ConstString::scanHex_16 (const char16* text, uint8& value, bool scanToEnd) { if (text && text[0]) { String str (text); str.toMultiByte (kCP_Default); // scanf uses default codepage return scanHex_8 (str, value, scanToEnd); } return false; } //----------------------------------------------------------------------------- bool ConstString::scanHex (const tchar* text, uint8& value, bool scanToEnd) { #ifdef UNICODE return scanHex_16 (text, value, scanToEnd); #else return scanHex_8 (text, value, scanToEnd); #endif } //----------------------------------------------------------------------------- bool ConstString::scanFloat (double& value, uint32 offset, bool scanToEnd) const { if (isEmpty () || offset >= len) return false; String str (*this); int32 pos = -1; if (isWide) { if ((pos = str.findNext (offset, STR (','))) >= 0 && ((uint32)pos) >= offset) str.setChar (pos, STR ('.')); str.toMultiByte (kCP_Default); // scanf uses default codepage } else { if ((pos = str.findNext (offset, ',')) >= 0 && ((uint32)pos) >= offset) str.setChar (pos, '.'); } const char8* txt = str.text8 () + offset; while (txt && txt[0]) { if (sscanf (txt, "%lf", &value) == 1) return true; if (scanToEnd == false) return false; txt++; } return false; } //----------------------------------------------------------------------------- char16 ConstString::toLower (char16 c) { #if SMTG_OS_WINDOWS WCHAR temp[2] = {c, 0}; ::CharLowerW (temp); return temp[0]; #elif SMTG_OS_MACOS // only convert characters which in lowercase are also single characters UniChar characters[2] = {0}; characters[0] = c; CFMutableStringRef str = CFStringCreateMutableWithExternalCharactersNoCopy ( kCFAllocator, characters, 1, 2, kCFAllocatorNull); if (str) { CFStringLowercase (str, NULL); CFRelease (str); if (characters[1] == 0) return characters[0]; } return c; #elif SMTG_OS_LINUX assert (false && "DEPRECATED No Linux implementation"); return c; #else return towlower (c); #endif } //----------------------------------------------------------------------------- char16 ConstString::toUpper (char16 c) { #if SMTG_OS_WINDOWS WCHAR temp[2] = {c, 0}; ::CharUpperW (temp); return temp[0]; #elif SMTG_OS_MACOS // only convert characters which in uppercase are also single characters (don't translate a // sharp-s which would result in SS) UniChar characters[2] = {0}; characters[0] = c; CFMutableStringRef str = CFStringCreateMutableWithExternalCharactersNoCopy ( kCFAllocator, characters, 1, 2, kCFAllocatorNull); if (str) { CFStringUppercase (str, NULL); CFRelease (str); if (characters[1] == 0) return characters[0]; } return c; #elif SMTG_OS_LINUX assert (false && "DEPRECATED No Linux implementation"); return c; #else return towupper (c); #endif } //----------------------------------------------------------------------------- char8 ConstString::toLower (char8 c) { if ((c >= 'A') && (c <= 'Z')) return c + ('a' - 'A'); #if SMTG_OS_WINDOWS CHAR temp[2] = {c, 0}; ::CharLowerA (temp); return temp[0]; #else return static_cast (tolower (c)); #endif } //----------------------------------------------------------------------------- char8 ConstString::toUpper (char8 c) { if ((c >= 'a') && (c <= 'z')) return c - ('a' - 'A'); #if SMTG_OS_WINDOWS CHAR temp[2] = {c, 0}; ::CharUpperA (temp); return temp[0]; #else return static_cast (toupper (c)); #endif } //----------------------------------------------------------------------------- bool ConstString::isCharSpace (const char8 character) { return isspace (character) != 0; } //----------------------------------------------------------------------------- bool ConstString::isCharSpace (const char16 character) { switch (character) { case 0x0020: case 0x00A0: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200A: case 0x200B: case 0x202F: case 0x205F: case 0x3000: return true; } return false; } //----------------------------------------------------------------------------- bool ConstString::isCharAlpha (const char8 character) { return isalpha (character) != 0; } //----------------------------------------------------------------------------- bool ConstString::isCharAlpha (const char16 character) { return iswalpha (character) != 0; } //----------------------------------------------------------------------------- bool ConstString::isCharAlphaNum (const char8 character) { return isalnum (character) != 0; } //----------------------------------------------------------------------------- bool ConstString::isCharAlphaNum (const char16 character) { // this may not work on macOSX when another locale is set inside the c-lib return iswalnum (character) != 0; } //----------------------------------------------------------------------------- bool ConstString::isCharDigit (const char8 character) { return isdigit (character) != 0; } //----------------------------------------------------------------------------- bool ConstString::isCharDigit (const char16 character) { // this may not work on macOSX when another locale is set inside the c-lib return iswdigit (character) != 0; } //----------------------------------------------------------------------------- bool ConstString::isCharAscii (char8 character) { return character >= 0; } //----------------------------------------------------------------------------- bool ConstString::isCharAscii (char16 character) { return character < 128; } //----------------------------------------------------------------------------- bool ConstString::isCharUpper (char8 character) { return toUpper (character) == character; } //----------------------------------------------------------------------------- bool ConstString::isCharUpper (char16 character) { return toUpper (character) == character; } //----------------------------------------------------------------------------- bool ConstString::isCharLower (char8 character) { return toLower (character) == character; } //----------------------------------------------------------------------------- bool ConstString::isCharLower (char16 character) { return toLower (character) == character; } //----------------------------------------------------------------------------- bool ConstString::isDigit (uint32 index) const { if (isEmpty () || index >= len) return false; if (isWide) return ConstString::isCharDigit (buffer16[index]); return ConstString::isCharDigit (buffer8[index]); } //----------------------------------------------------------------------------- int32 ConstString::getTrailingNumberIndex (uint32 width) const { if (isEmpty ()) return -1; int32 endIndex = len - 1; int32 i = endIndex; while (isDigit ((uint32)i) && i >= 0) i--; // now either all are digits or i is on the first non digit if (i < endIndex) { if (width > 0 && (endIndex - i != static_cast (width))) return -1; return i + 1; } return -1; } //----------------------------------------------------------------------------- int64 ConstString::getTrailingNumber (int64 fallback) const { int32 index = getTrailingNumberIndex (); int64 number = 0; if (index >= 0) if (scanInt64 (number, index)) return number; return fallback; } //----------------------------------------------------------------------------- void ConstString::toVariant (FVariant& var) const { if (isWide) { var.setString16 (buffer16); } else { var.setString8 (buffer8); } } //----------------------------------------------------------------------------- bool ConstString::isAsciiString () const { uint32 i; if (isWide) { for (i = 0; i < len; i++) if (ConstString::isCharAscii (buffer16[i]) == false) return false; } else { for (i = 0; i < len; i++) if (ConstString::isCharAscii (buffer8[i]) == false) return false; } return true; } #if SMTG_OS_MACOS uint32 kDefaultSystemEncoding = kCFStringEncodingMacRoman; //----------------------------------------------------------------------------- static CFStringEncoding MBCodePageToCFStringEncoding (uint32 codePage) { switch (codePage) { case kCP_ANSI: return kDefaultSystemEncoding; // MacRoman or JIS case kCP_MAC_ROMAN: return kCFStringEncodingMacRoman; case kCP_ANSI_WEL: return kCFStringEncodingWindowsLatin1; case kCP_MAC_CEE: return kCFStringEncodingMacCentralEurRoman; case kCP_Utf8: return kCFStringEncodingUTF8; case kCP_ShiftJIS: return kCFStringEncodingShiftJIS_X0213_00; case kCP_US_ASCII: return kCFStringEncodingASCII; } return kCFStringEncodingASCII; } #endif //----------------------------------------------------------------------------- int32 ConstString::multiByteToWideString (char16* dest, const char8* source, int32 charCount, uint32 sourceCodePage) { if (source == nullptr || source[0] == 0) { if (dest && charCount > 0) { dest[0] = 0; } return 0; } int32 result = 0; #if SMTG_OS_WINDOWS result = MultiByteToWideChar (sourceCodePage, MB_ERR_INVALID_CHARS, source, -1, wscast (dest), charCount); #endif #if SMTG_OS_MACOS CFStringRef cfStr = (CFStringRef)::toCFStringRef (source, MBCodePageToCFStringEncoding (sourceCodePage)); if (cfStr) { CFRange range = {0, CFStringGetLength (cfStr)}; CFIndex usedBytes; if (CFStringGetBytes (cfStr, range, kCFStringEncodingUnicode, ' ', false, (UInt8*)dest, charCount * 2, &usedBytes) > 0) { result = static_cast (usedBytes / 2 + 1); if (dest) dest[usedBytes / 2] = 0; } CFRelease (cfStr); } #endif #if SMTG_OS_LINUX if (sourceCodePage == kCP_ANSI || sourceCodePage == kCP_US_ASCII || sourceCodePage == kCP_Utf8) { if (dest == nullptr) { auto state = std::mbstate_t (); auto maxChars = charCount ? charCount : std::numeric_limits::max () - 1; result = converterFacet ().length (state, source, source + strlen (source), maxChars); } else { auto utf16Str = converter ().from_bytes (source); if (!utf16Str.empty ()) { result = std::min (charCount, utf16Str.size ()); memcpy (dest, utf16Str.data (), result * sizeof (char16)); dest[result] = 0; } } } else { assert (false && "DEPRECATED No Linux implementation"); } #endif SMTG_ASSERT (result > 0) return result; } //----------------------------------------------------------------------------- int32 ConstString::wideStringToMultiByte (char8* dest, const char16* wideString, int32 charCount, uint32 destCodePage) { #if SMTG_OS_WINDOWS return WideCharToMultiByte (destCodePage, 0, wscast (wideString), -1, dest, charCount, nullptr, nullptr); #elif SMTG_OS_MACOS int32 result = 0; if (wideString != 0) { if (dest) { CFStringRef cfStr = CFStringCreateWithCharactersNoCopy ( kCFAllocator, (const UniChar*)wideString, strlen16 (wideString), kCFAllocatorNull); if (cfStr) { if (fromCFStringRef (dest, charCount, cfStr, MBCodePageToCFStringEncoding (destCodePage))) result = static_cast (strlen (dest) + 1); CFRelease (cfStr); } } else { return static_cast (CFStringGetMaximumSizeForEncoding ( strlen16 (wideString), MBCodePageToCFStringEncoding (destCodePage))); } } return result; #elif SMTG_OS_LINUX int32 result = 0; if (destCodePage == kCP_Utf8) { if (dest == nullptr) { auto maxChars = charCount ? charCount : tstrlen (wideString); result = converterFacet ().max_length () * maxChars; } else { auto utf8Str = converter ().to_bytes (wideString); if (!utf8Str.empty ()) { result = std::min (charCount, utf8Str.size ()); memcpy (dest, utf8Str.data (), result * sizeof (char8)); dest[result] = 0; } } } else if (destCodePage == kCP_ANSI || destCodePage == kCP_US_ASCII) { if (dest == nullptr) { result = strlen16 (wideString) + 1; } else { int32 i = 0; for (; i < charCount; ++i) { if (wideString[i] == 0) break; if (wideString[i] <= 0x007F) dest[i] = wideString[i]; else dest[i] = '_'; } dest[i] = 0; result = i; } } else { assert (false && "DEPRECATED No Linux implementation"); } return result; #else assert (false && "DEPRECATED No Linux implementation"); return 0; #endif } //----------------------------------------------------------------------------- bool ConstString::isNormalized (UnicodeNormalization n) { if (isWide == false) return false; #if SMTG_OS_WINDOWS #ifdef UNICODE if (n != kUnicodeNormC) return false; uint32 normCharCount = static_cast (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, nullptr, 0)); return (normCharCount == len); #else return false; #endif #elif SMTG_OS_MACOS if (n != kUnicodeNormC) return false; CFStringRef cfStr = (CFStringRef)toCFStringRef (); CFIndex charCount = CFStringGetLength (cfStr); CFRelease (cfStr); return (charCount == len); #else return false; #endif } //----------------------------------------------------------------------------- // String //----------------------------------------------------------------------------- String::String () { isWide = kWideStringDefault ? 1 : 0; } //----------------------------------------------------------------------------- String::String (const char8* str, MBCodePage codePage, int32 n, bool isTerminated) { isWide = false; if (str) { if (isTerminated && n >= 0 && str[n] != 0) { // isTerminated is not always set correctly isTerminated = false; } if (!isTerminated) { assign (str, n, isTerminated); toWideString (codePage); } else { if (n < 0) n = static_cast (strlen (str)); if (n > 0) _toWideString (str, n, codePage); } } } //----------------------------------------------------------------------------- String::String (const char8* str, int32 n, bool isTerminated) { if (str) assign (str, n, isTerminated); } //----------------------------------------------------------------------------- String::String (const char16* str, int32 n, bool isTerminated) { isWide = 1; if (str) assign (str, n, isTerminated); } //----------------------------------------------------------------------------- String::String (const String& str, int32 n) { isWide = str.isWideString (); if (!str.isEmpty ()) assign (str, n); } //----------------------------------------------------------------------------- String::String (const ConstString& str, int32 n) { isWide = str.isWideString (); if (!str.isEmpty ()) assign (str, n); } //----------------------------------------------------------------------------- String::String (const FVariant& var) { isWide = kWideStringDefault ? 1 : 0; fromVariant (var); } //----------------------------------------------------------------------------- String::String (IString* str) { isWide = str->isWideString (); if (isWide) assign (str->getText16 ()); else assign (str->getText8 ()); } //----------------------------------------------------------------------------- String::~String () { if (buffer) resize (0, false); } #if SMTG_CPP11_STDLIBSUPPORT //----------------------------------------------------------------------------- String::String (String&& str) { *this = std::move (str); } //----------------------------------------------------------------------------- String& String::operator= (String&& str) { SMTG_ASSERT (buffer == nullptr || buffer != str.buffer); tryFreeBuffer (); isWide = str.isWide; buffer = str.buffer; len = str.len; str.buffer = nullptr; str.len = 0; return *this; } #endif //----------------------------------------------------------------------------- void String::updateLength () { if (isWide) len = strlen16 (text16 ()); else len = strlen8 (text8 ()); } //----------------------------------------------------------------------------- bool String::toWideString (uint32 sourceCodePage) { if (!isWide && buffer8 && len > 0) return _toWideString (buffer8, len, sourceCodePage); isWide = true; return true; } //----------------------------------------------------------------------------- bool String::_toWideString (const char8* src, int32 length, uint32 sourceCodePage) { if (!isWide) { if (src && length > 0) { int32 bytesNeeded = multiByteToWideString (nullptr, src, 0, sourceCodePage) * sizeof (char16); if (bytesNeeded) { bytesNeeded += sizeof (char16); char16* newStr = (char16*)malloc (bytesNeeded); if (multiByteToWideString (newStr, src, length + 1, sourceCodePage) < 0) { free (newStr); return false; } if (buffer8) free (buffer8); buffer16 = newStr; isWide = true; updateLength (); } else { return false; } } isWide = true; } return true; } #define SMTG_STRING_CHECK_CONVERSION 1 #define SMTG_STRING_CHECK_CONVERSION_NO_BREAK 1 #if SMTG_STRING_CHECK_CONVERSION_NO_BREAK #define SMTG_STRING_CHECK_MSG FDebugPrint #else #define SMTG_STRING_CHECK_MSG FDebugBreak #endif //----------------------------------------------------------------------------- bool String::checkToMultiByte (uint32 destCodePage) const { if (!isWide || isEmpty ()) return true; #if DEVELOPMENT && SMTG_STRING_CHECK_CONVERSION int debugLen = length (); int debugNonASCII = 0; for (int32 i = 0; i < length (); i++) { if (buffer16[i] > 127) ++debugNonASCII; } String* backUp = nullptr; if (debugNonASCII > 0) backUp = NEW String (*this); #endif // this should be avoided, since it can lead to information loss bool result = const_cast (*this).toMultiByte (destCodePage); #if DEVELOPMENT && SMTG_STRING_CHECK_CONVERSION if (backUp) { String temp (*this); temp.toWideString (destCodePage); if (temp != *backUp) { backUp->toMultiByte (kCP_Utf8); SMTG_STRING_CHECK_MSG ("Indirect string conversion information loss ! %d/%d non ASCII chars: \"%s\" -> \"%s\"\n", debugNonASCII, debugLen, backUp->buffer8, buffer8); } else SMTG_STRING_CHECK_MSG ("Indirect string potential conversion information loss ! %d/%d non ASCII chars result: \"%s\"\n", debugNonASCII, debugLen, buffer8); delete backUp; } #endif return result; } //----------------------------------------------------------------------------- bool String::toMultiByte (uint32 destCodePage) { if (isWide) { if (buffer16 && len > 0) { int32 numChars = wideStringToMultiByte (nullptr, buffer16, 0, destCodePage) + sizeof (char8); char8* newStr = (char8*)malloc (numChars * sizeof (char8)); if (wideStringToMultiByte (newStr, buffer16, numChars, destCodePage) <= 0) { free (newStr); return false; } free (buffer16); buffer8 = newStr; isWide = false; updateLength (); } isWide = false; } else if (destCodePage != kCP_Default) { if (toWideString () == false) return false; return toMultiByte (destCodePage); } return true; } //----------------------------------------------------------------------------- void String::fromUTF8 (const char8* utf8String) { if (buffer8 != utf8String) resize (0, false); _toWideString (utf8String, static_cast (strlen (utf8String)), kCP_Utf8); } //----------------------------------------------------------------------------- bool String::normalize (UnicodeNormalization n) { if (isWide == false) return false; if (buffer16 == nullptr) return true; #if SMTG_OS_WINDOWS #ifdef UNICODE if (n != kUnicodeNormC) return false; uint32 normCharCount = static_cast (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, nullptr, 0)); if (normCharCount == len) return true; char16* newString = (char16*)malloc ((normCharCount + 1) * sizeof (char16)); uint32 converterCount = static_cast (FoldString ( MAP_PRECOMPOSED, wscast (buffer16), len, wscast (newString), normCharCount + 1)); if (converterCount != normCharCount) { free (newString); return false; } newString[converterCount] = 0; free (buffer16); buffer16 = newString; updateLength (); return true; #else return false; #endif #elif SMTG_OS_MACOS CFMutableStringRef origStr = (CFMutableStringRef)toCFStringRef (0xFFFF, true); if (origStr) { CFStringNormalizationForm normForm = kCFStringNormalizationFormD; switch (n) { case kUnicodeNormC: normForm = kCFStringNormalizationFormC; break; case kUnicodeNormD: normForm = kCFStringNormalizationFormD; break; case kUnicodeNormKC: normForm = kCFStringNormalizationFormKC; break; case kUnicodeNormKD: normForm = kCFStringNormalizationFormKD; break; } CFStringNormalize (origStr, normForm); bool result = fromCFStringRef (origStr); CFRelease (origStr); return result; } return false; #else return false; #endif } //----------------------------------------------------------------------------- void String::tryFreeBuffer () { if (buffer) { free (buffer); buffer = nullptr; } } //----------------------------------------------------------------------------- bool String::resize (uint32 newLength, bool wide, bool fill) { if (newLength == 0) { tryFreeBuffer (); len = 0; isWide = wide ? 1 : 0; } else { size_t newCharSize = wide ? sizeof (char16) : sizeof (char8); size_t oldCharSize = (isWide != 0) ? sizeof (char16) : sizeof (char8); size_t newBufferSize = (newLength + 1) * newCharSize; size_t oldBufferSize = (len + 1) * oldCharSize; isWide = wide ? 1 : 0; if (buffer) { if (newBufferSize != oldBufferSize) { void* newstr = realloc (buffer, newBufferSize); if (newstr == nullptr) return false; buffer = newstr; if (isWide) buffer16[newLength] = 0; else buffer8[newLength] = 0; } else if (wide && newCharSize != oldCharSize) buffer16[newLength] = 0; } else { void* newstr = malloc (newBufferSize); if (newstr == nullptr) return false; buffer = newstr; if (isWide) { buffer16[0] = 0; buffer16[newLength] = 0; } else { buffer8[0] = 0; buffer8[newLength] = 0; } } if (fill && len < newLength && buffer) { if (isWide) { char16 c = ' '; for (uint32 i = len; i < newLength; i++) buffer16[i] = c; } else { memset (buffer8 + len, ' ', newLength - len); } } } return true; } //----------------------------------------------------------------------------- bool String::setChar8 (uint32 index, char8 c) { if (index == len && c == 0) return true; if (index >= len) { if (c == 0) { if (resize (index, isWide, true) == false) return false; len = index; return true; } if (resize (index + 1, isWide, true) == false) return false; len = index + 1; } if (index < len && buffer) { if (isWide) { if (c == 0) buffer16[index] = 0; else { char8 src[] = {c, 0}; char16 dest[8] = {0}; if (multiByteToWideString (dest, src, 2) > 0) buffer16[index] = dest[0]; } SMTG_ASSERT (buffer16[len] == 0) } else { buffer8[index] = c; SMTG_ASSERT (buffer8[len] == 0) } if (c == 0) updateLength (); return true; } return false; } //----------------------------------------------------------------------------- bool String::setChar16 (uint32 index, char16 c) { if (index == len && c == 0) return true; if (index >= len) { if (c == 0) { if (resize (index, isWide, true) == false) return false; len = index; return true; } if (resize (index + 1, isWide, true) == false) return false; len = index + 1; } if (index < len && buffer) { if (isWide) { buffer16[index] = c; SMTG_ASSERT (buffer16[len] == 0) } else { SMTG_ASSERT (buffer8[len] == 0) char16 src[] = {c, 0}; char8 dest[8] = {0}; if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0) buffer8[index] = dest[0]; else return false; } if (c == 0) updateLength (); return true; } return false; } //----------------------------------------------------------------------------- String& String::assign (const ConstString& str, int32 n) { if (str.isWideString ()) return assign (str.text16 (), n < 0 ? str.length () : n); return assign (str.text8 (), n < 0 ? str.length () : n); } //----------------------------------------------------------------------------- String& String::assign (const char8* str, int32 n, bool isTerminated) { if (str == buffer8) return *this; if (isTerminated) { uint32 stringLength = (uint32) ((str) ? strlen (str) : 0); n = n < 0 ? stringLength : Min (n, stringLength); } else if (n < 0) return *this; if (resize (n, false)) { if (buffer8 && n > 0 && str) { memcpy (buffer8, str, n * sizeof (char8)); SMTG_ASSERT (buffer8[n] == 0) } isWide = 0; len = n; } return *this; } //----------------------------------------------------------------------------- String& String::assign (const char16* str, int32 n, bool isTerminated) { if (str == buffer16) return *this; if (isTerminated) { uint32 stringLength = (uint32) ((str) ? strlen16 (str) : 0); n = n < 0 ? stringLength : Min (n, stringLength); } else if (n < 0) return *this; if (resize (n, true)) { if (buffer16 && n > 0 && str) { memcpy (buffer16, str, n * sizeof (char16)); SMTG_ASSERT (buffer16[n] == 0) } isWide = 1; len = n; } return *this; } //----------------------------------------------------------------------------- String& String::assign (char8 c, int32 n) { if (resize (n, false)) { if (buffer8 && n > 0) { memset (buffer8, c, n * sizeof (char8)); SMTG_ASSERT (buffer8[n] == 0) } isWide = 0; len = n; } return *this; } //----------------------------------------------------------------------------- String& String::assign (char16 c, int32 n) { if (resize (n, true)) { if (buffer && n > 0) { for (int32 i = 0; i < n; i++) buffer16[i] = c; SMTG_ASSERT (buffer16[n] == 0) } isWide = 1; len = n; } return *this; } //----------------------------------------------------------------------------- String& String::append (const ConstString& str, int32 n) { if (str.isWideString ()) return append (str.text16 (), n); return append (str.text8 (), n); } //----------------------------------------------------------------------------- String& String::append (const char8* str, int32 n) { if (str == buffer8) return *this; if (len == 0) return assign (str, n); if (isWide) { String tmp (str); if (tmp.toWideString () == false) return *this; return append (tmp.buffer16, n); } uint32 stringLength = (uint32) ((str) ? strlen (str) : 0); n = n < 0 ? stringLength : Min (n, stringLength); if (n > 0) { int32 newlen = n + len; if (!resize (newlen, false)) return *this; if (buffer && str) { memcpy (buffer8 + len, str, n * sizeof (char8)); SMTG_ASSERT (buffer8[newlen] == 0) } len += n; } return *this; } //----------------------------------------------------------------------------- String& String::append (const char16* str, int32 n) { if (str == buffer16) return *this; if (len == 0) return assign (str, n); if (!isWide) { if (toWideString () == false) return *this; } uint32 stringLength = (uint32) ((str) ? strlen16 (str) : 0); n = n < 0 ? stringLength : Min (n, stringLength); if (n > 0) { int32 newlen = n + len; if (!resize (newlen, true)) return *this; if (buffer16 && str) { memcpy (buffer16 + len, str, n * sizeof (char16)); SMTG_ASSERT (buffer16[newlen] == 0) } len += n; } return *this; } //----------------------------------------------------------------------------- String& String::append (const char8 c, int32 n) { char8 str[] = {c, 0}; if (n == 1) { return append (str, 1); } if (n > 1) { if (isWide) { String tmp (str); if (tmp.toWideString () == false) return *this; return append (tmp.buffer16[0], n); } int32 newlen = n + len; if (!resize (newlen, false)) return *this; if (buffer) { memset (buffer8 + len, c, n * sizeof (char8)); SMTG_ASSERT (buffer8[newlen] == 0) } len += n; } return *this; } //----------------------------------------------------------------------------- String& String::append (const char16 c, int32 n) { if (n == 1) { char16 str[] = {c, 0}; return append (str, 1); } if (n > 1) { if (!isWide) { if (toWideString () == false) return *this; } int32 newlen = n + len; if (!resize (newlen, true)) return *this; if (buffer16) { for (int32 i = len; i < newlen; i++) buffer16[i] = c; SMTG_ASSERT (buffer16[newlen] == 0) } len += n; } return *this; } //----------------------------------------------------------------------------- String& String::insertAt (uint32 idx, const ConstString& str, int32 n) { if (str.isWideString ()) return insertAt (idx, str.text16 (), n); return insertAt (idx, str.text8 (), n); } //----------------------------------------------------------------------------- String& String::insertAt (uint32 idx, const char8* str, int32 n) { if (idx > len) return *this; if (isWide) { String tmp (str); if (tmp.toWideString () == false) return *this; return insertAt (idx, tmp.buffer16, n); } uint32 stringLength = (uint32) ((str) ? strlen (str) : 0); n = n < 0 ? stringLength : Min (n, stringLength); if (n > 0) { int32 newlen = len + n; if (!resize (newlen, false)) return *this; if (buffer && str) { if (idx < len) memmove (buffer8 + idx + n, buffer8 + idx, (len - idx) * sizeof (char8)); memcpy (buffer8 + idx, str, n * sizeof (char8)); SMTG_ASSERT (buffer8[newlen] == 0) } len += n; } return *this; } //----------------------------------------------------------------------------- String& String::insertAt (uint32 idx, const char16* str, int32 n) { if (idx > len) return *this; if (!isWide) { if (toWideString () == false) return *this; } uint32 stringLength = (uint32) ((str) ? strlen16 (str) : 0); n = n < 0 ? stringLength : Min (n, stringLength); if (n > 0) { int32 newlen = len + n; if (!resize (newlen, true)) return *this; if (buffer && str) { if (idx < len) memmove (buffer16 + idx + n, buffer16 + idx, (len - idx) * sizeof (char16)); memcpy (buffer16 + idx, str, n * sizeof (char16)); SMTG_ASSERT (buffer16[newlen] == 0) } len += n; } return *this; } //----------------------------------------------------------------------------- String& String::replace (uint32 idx, int32 n1, const ConstString& str, int32 n2) { if (str.isWideString ()) return replace (idx, n1, str.text16 (), n2); return replace (idx, n1, str.text8 (), n2); } // "replace" replaces n1 number of characters at the specified index with // n2 characters from the specified string. //----------------------------------------------------------------------------- String& String::replace (uint32 idx, int32 n1, const char8* str, int32 n2) { if (idx > len || str == nullptr) return *this; if (isWide) { String tmp (str); if (tmp.toWideString () == false) return *this; if (tmp.length () == 0 || n2 == 0) return remove (idx, n1); return replace (idx, n1, tmp.buffer16, n2); } if (n1 < 0 || idx + n1 > len) n1 = len - idx; if (n1 == 0) return *this; uint32 stringLength = (uint32) ((str) ? strlen (str) : 0); n2 = n2 < 0 ? stringLength : Min (n2, stringLength); uint32 newlen = len - n1 + n2; if (newlen > len) if (!resize (newlen, false)) return *this; if (buffer) { memmove (buffer8 + idx + n2, buffer8 + idx + n1, (len - (idx + n1)) * sizeof (char8)); memcpy (buffer8 + idx, str, n2 * sizeof (char8)); // cannot be removed because resize is not called called in all cases (newlen > len) buffer8[newlen] = 0; } len = newlen; return *this; } //----------------------------------------------------------------------------- String& String::replace (uint32 idx, int32 n1, const char16* str, int32 n2) { if (idx > len || str == nullptr) return *this; if (!isWide) { if (toWideString () == false) return *this; } if (n1 < 0 || idx + n1 > len) n1 = len - idx; if (n1 == 0) return *this; uint32 stringLength = (uint32) ((str) ? strlen16 (str) : 0); n2 = n2 < 0 ? stringLength : Min (n2, stringLength); uint32 newlen = len - n1 + n2; if (newlen > len) if (!resize (newlen, true)) return *this; if (buffer) { memmove (buffer16 + idx + n2, buffer16 + idx + n1, (len - (idx + n1)) * sizeof (char16)); memcpy (buffer16 + idx, str, n2 * sizeof (char16)); // cannot be removed because resize is not called called in all cases (newlen > len) buffer16[newlen] = 0; } len = newlen; return *this; } //----------------------------------------------------------------------------- int32 String::replace (const char8* toReplace, const char8* toReplaceWith, bool all, CompareMode m) { if (toReplace == nullptr || toReplaceWith == nullptr) return 0; int32 result = 0; int32 idx = findFirst (toReplace, -1, m); if (idx > -1) { int32 toReplaceLen = static_cast (strlen (toReplace)); int32 toReplaceWithLen = static_cast (strlen (toReplaceWith)); while (idx > -1) { replace (idx, toReplaceLen, toReplaceWith, toReplaceWithLen); result++; if (all) idx = findNext (idx + toReplaceWithLen, toReplace, -1, m); else break; } } return result; } //----------------------------------------------------------------------------- int32 String::replace (const char16* toReplace, const char16* toReplaceWith, bool all, CompareMode m) { if (toReplace == nullptr || toReplaceWith == nullptr) return 0; int32 result = 0; int32 idx = findFirst (toReplace, -1, m); if (idx > -1) { int32 toReplaceLen = strlen16 (toReplace); int32 toReplaceWithLen = strlen16 (toReplaceWith); while (idx > -1) { replace (idx, toReplaceLen, toReplaceWith, toReplaceWithLen); result++; if (all) idx = findNext (idx + toReplaceWithLen, toReplace, -1, m); else break; } } return result; } //----------------------------------------------------------------------------- template static bool performReplace (T* str, const T* toReplace, T toReplaceBy) { bool anyReplace = false; T* p = str; while (*p) { const T* rep = toReplace; while (*rep) { if (*p == *rep) { *p = toReplaceBy; anyReplace = true; break; } rep++; } p++; } return anyReplace; } //----------------------------------------------------------------------------- bool String::replaceChars8 (const char8* toReplace, char8 toReplaceBy) { if (isEmpty ()) return false; if (isWide) { String toReplaceW (toReplace); if (toReplaceW.toWideString () == false) return false; char8 src[] = {toReplaceBy, 0}; char16 dest[2] = {0}; if (multiByteToWideString (dest, src, 2) > 0) { return replaceChars16 (toReplaceW.text16 (), dest[0]); } return false; } if (toReplaceBy == 0) toReplaceBy = ' '; return performReplace (buffer8, toReplace, toReplaceBy); } //----------------------------------------------------------------------------- bool String::replaceChars16 (const char16* toReplace, char16 toReplaceBy) { if (isEmpty ()) return false; if (!isWide) { String toReplaceA (toReplace); if (toReplaceA.toMultiByte () == false) return false; if (toReplaceA.length () > 1) { SMTG_WARNING ("cannot replace non ASCII chars on non Wide String") return false; } char16 src[] = {toReplaceBy, 0}; char8 dest[8] = {0}; if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0) return replaceChars8 (toReplaceA.text8 (), dest[0]); return false; } if (toReplaceBy == 0) toReplaceBy = STR16 (' '); return performReplace (buffer16, toReplace, toReplaceBy); } // "remove" removes the specified number of characters from the string // starting at the specified index. //----------------------------------------------------------------------------- String& String::remove (uint32 idx, int32 n) { if (isEmpty () || idx >= len || n == 0) return *this; if ((idx + n > len) || n < 0) n = len - idx; else { int32 toMove = len - idx - n; if (buffer) { if (isWide) memmove (buffer16 + idx, buffer16 + idx + n, toMove * sizeof (char16)); else memmove (buffer8 + idx, buffer8 + idx + n, toMove * sizeof (char8)); } } resize (len - n, isWide); updateLength (); return *this; } //----------------------------------------------------------------------------- bool String::removeSubString (const ConstString& subString, bool allOccurences) { bool removed = false; while (!removed || allOccurences) { int32 idx = findFirst (subString); if (idx < 0) break; remove (idx, subString.length ()); removed = true; } return removed; } //----------------------------------------------------------------------------- template static uint32 performTrim (T* str, uint32 length, F func, bool funcResult) { uint32 toRemoveAtHead = 0; uint32 toRemoveAtTail = 0; T* p = str; while ((*p) && ((func (*p) != 0) == funcResult)) p++; toRemoveAtHead = static_cast (p - str); if (toRemoveAtHead < length) { p = str + length - 1; while (((func (*p) != 0) == funcResult) && (p > str)) { p--; toRemoveAtTail++; } } uint32 newLength = length - (toRemoveAtHead + toRemoveAtTail); if (newLength != length) { if (toRemoveAtHead) memmove (str, str + toRemoveAtHead, newLength * sizeof (T)); } return newLength; } // "trim" trims the leading and trailing unwanted characters from the string. //----------------------------------------------------------------------------- bool String::trim (String::CharGroup group) { if (isEmpty ()) return false; uint32 newLength; switch (group) { case kSpace: if (isWide) newLength = performTrim (buffer16, len, iswspace, true); else newLength = performTrim (buffer8, len, isspace, true); break; case kNotAlphaNum: if (isWide) newLength = performTrim (buffer16, len, iswalnum, false); else newLength = performTrim (buffer8, len, isalnum, false); break; case kNotAlpha: if (isWide) newLength = performTrim (buffer16, len, iswalpha, false); else newLength = performTrim (buffer8, len, isalpha, false); break; default: // Undefined enum value return false; } if (newLength != len) { resize (newLength, isWide); len = newLength; return true; } return false; } //----------------------------------------------------------------------------- template static uint32 performRemove (T* str, uint32 length, F func, bool funcResult) { T* p = str; while (*p) { if ((func (*p) != 0) == funcResult) { size_t toMove = length - (p - str); memmove (p, p + 1, toMove * sizeof (T)); length--; } else p++; } return length; } //----------------------------------------------------------------------------- void String::removeChars (CharGroup group) { if (isEmpty ()) return; uint32 newLength; switch (group) { case kSpace: if (isWide) newLength = performRemove (buffer16, len, iswspace, true); else newLength = performRemove (buffer8, len, isspace, true); break; case kNotAlphaNum: if (isWide) newLength = performRemove (buffer16, len, iswalnum, false); else newLength = performRemove (buffer8, len, isalnum, false); break; case kNotAlpha: if (isWide) newLength = performRemove (buffer16, len, iswalpha, false); else newLength = performRemove (buffer8, len, isalpha, false); break; default: // Undefined enum value return; } if (newLength != len) { resize (newLength, isWide); len = newLength; } } //----------------------------------------------------------------------------- template static uint32 performRemoveChars (T* str, uint32 length, const T* toRemove) { T* p = str; while (*p) { bool found = false; const T* rem = toRemove; while (*rem) { if (*p == *rem) { found = true; break; } rem++; } if (found) { size_t toMove = length - (p - str); memmove (p, p + 1, toMove * sizeof (T)); length--; } else p++; } return length; } //----------------------------------------------------------------------------- bool String::removeChars8 (const char8* toRemove) { if (isEmpty () || toRemove == nullptr) return true; if (isWide) { String wStr (toRemove); if (wStr.toWideString () == false) return false; return removeChars16 (wStr.text16 ()); } uint32 newLength = performRemoveChars (buffer8, len, toRemove); if (newLength != len) { resize (newLength, false); len = newLength; } return true; } //----------------------------------------------------------------------------- bool String::removeChars16 (const char16* toRemove) { if (isEmpty () || toRemove == nullptr) return true; if (!isWide) { String str8 (toRemove); if (str8.toMultiByte () == false) return false; return removeChars8 (str8.text8 ()); } uint32 newLength = performRemoveChars (buffer16, len, toRemove); if (newLength != len) { resize (newLength, true); len = newLength; } return true; } //----------------------------------------------------------------------------- String& String::printf (const char8* format, ...) { char8 string[kPrintfBufferSize]; va_list marker; va_start (marker, format); vsnprintf (string, kPrintfBufferSize - 1, format, marker); return assign (string); } //----------------------------------------------------------------------------- String& String::printf (const char16* format, ...) { char16 string[kPrintfBufferSize]; va_list marker; va_start (marker, format); vsnwprintf (string, kPrintfBufferSize - 1, format, marker); return assign (string); } //----------------------------------------------------------------------------- String& String::vprintf (const char8* format, va_list args) { char8 string[kPrintfBufferSize]; vsnprintf (string, kPrintfBufferSize - 1, format, args); return assign (string); } //----------------------------------------------------------------------------- String& String::vprintf (const char16* format, va_list args) { char16 string[kPrintfBufferSize]; vsnwprintf (string, kPrintfBufferSize - 1, format, args); return assign (string); } //----------------------------------------------------------------------------- String& String::printInt64 (int64 value) { if (isWide) { #if SMTG_CPP11 return String::printf (STR ("%") STR (FORMAT_INT64A), value); #else return String::printf (STR ("%" FORMAT_INT64A), value); #endif } else return String::printf ("%" FORMAT_INT64A, value); } //----------------------------------------------------------------------------- String& String::printFloat (double value, uint32 maxPrecision) { static constexpr auto kMaxAfterCommaResolution = 16; // escape point for integer values, avoid unnecessary complexity later on const bool withinInt64Boundaries = value <= std::numeric_limits::max () && value >= std::numeric_limits::lowest (); if (withinInt64Boundaries && (maxPrecision == 0 || std::round (value) == value)) return printInt64 (value); const auto absValue = std::abs (value); const uint32 valueExponent = absValue >= 1 ? std::log10 (absValue) : -std::log10 (absValue) + 1; maxPrecision = std::min (kMaxAfterCommaResolution - valueExponent, maxPrecision); if (isWide) printf (STR ("%s%dlf"), STR ("%."), maxPrecision); else printf ("%s%dlf", "%.", maxPrecision); if (isWide) printf (text16 (), value); else printf (text8 (), value); // trim trail zeros for (int32 i = length () - 1; i >= 0; i--) { if ((isWide && testChar16 (i, '0')) || testChar8 (i, '0')) remove (i); else if ((isWide && testChar16 (i, '.')) || testChar8 (i, '.')) { remove (i); break; } else break; } return *this; } //----------------------------------------------------------------------------- bool String::incrementTrailingNumber (uint32 width, tchar separator, uint32 minNumber, bool applyOnlyFormat) { if (width > 32) return false; int64 number = 1; int32 index = getTrailingNumberIndex (); if (index >= 0) { if (scanInt64 (number, index)) if (!applyOnlyFormat) number++; if (separator != 0 && index > 0 && testChar (index - 1, separator) == true) index--; remove (index); } if (number < minNumber) number = minNumber; if (isWide) { char16 format[64]; char16 trail[128]; if (separator && isEmpty () == false) { sprintf16 (format, STR16 ("%%c%%0%uu"), width); sprintf16 (trail, format, separator, (uint32)number); } else { sprintf16 (format, STR16 ("%%0%uu"), width); sprintf16 (trail, format, (uint32)number); } append (trail); } else { static constexpr auto kFormatSize = 64u; static constexpr auto kTrailSize = 64u; char format[kFormatSize]; char trail[kTrailSize]; if (separator && isEmpty () == false) { snprintf (format, kFormatSize, "%%c%%0%uu", width); snprintf (trail, kTrailSize, format, separator, (uint32)number); } else { snprintf (format, kFormatSize, "%%0%uu", width); snprintf (trail, kTrailSize, format, (uint32)number); } append (trail); } return true; } //----------------------------------------------------------------------------- void String::toLower (uint32 index) { if (buffer && index < len) { if (isWide) buffer16[index] = ConstString::toLower (buffer16[index]); else buffer8[index] = ConstString::toLower (buffer8[index]); } } //----------------------------------------------------------------------------- void String::toLower () { int32 i = len; if (buffer && i > 0) { if (isWide) { #if SMTG_OS_MACOS CFMutableStringRef cfStr = CFStringCreateMutableWithExternalCharactersNoCopy ( kCFAllocator, (UniChar*)buffer16, len, len + 1, kCFAllocatorNull); CFStringLowercase (cfStr, NULL); CFRelease (cfStr); #else char16* c = buffer16; while (i--) { *c = ConstString::toLower (*c); c++; } #endif } else { char8* c = buffer8; while (i--) { *c = ConstString::toLower (*c); c++; } } } } //----------------------------------------------------------------------------- void String::toUpper (uint32 index) { if (buffer && index < len) { if (isWide) buffer16[index] = ConstString::toUpper (buffer16[index]); else buffer8[index] = ConstString::toUpper (buffer8[index]); } } //----------------------------------------------------------------------------- void String::toUpper () { int32 i = len; if (buffer && i > 0) { if (isWide) { #if SMTG_OS_MACOS CFMutableStringRef cfStr = CFStringCreateMutableWithExternalCharactersNoCopy ( kCFAllocator, (UniChar*)buffer16, len, len + 1, kCFAllocatorNull); CFStringUppercase (cfStr, NULL); CFRelease (cfStr); #else char16* c = buffer16; while (i--) { *c = ConstString::toUpper (*c); c++; } #endif } else { char8* c = buffer8; while (i--) { *c = ConstString::toUpper (*c); c++; } } } } //----------------------------------------------------------------------------- bool String::fromVariant (const FVariant& var) { switch (var.getType ()) { case FVariant::kString8: { assign (var.getString8 ()); return true; } case FVariant::kString16: { assign (var.getString16 ()); return true; } case FVariant::kFloat: { printFloat (var.getFloat ()); return true; } case FVariant::kInteger: { printInt64 (var.getInt ()); return true; } case FVariant::kObject: if (auto string = ICast (var.getObject ())) { if (string->isWideString ()) assign (string->getText16 ()); else assign (string->getText8 ()); } return true; default: remove (); } return false; } //----------------------------------------------------------------------------- void String::toVariant (FVariant& var) const { if (isWide) { var.setString16 (text16 ()); } else { var.setString8 (text8 ()); } } //----------------------------------------------------------------------------- bool String::fromAttributes (IAttributes* a, IAttrID attrID) { FVariant variant; if (a->get (attrID, variant) == kResultTrue) return fromVariant (variant); return false; } //----------------------------------------------------------------------------- bool String::toAttributes (IAttributes* a, IAttrID attrID) { FVariant variant; toVariant (variant); if (a->set (attrID, variant) == kResultTrue) return true; return false; } // "swapContent" swaps ownership of the strings pointed to //----------------------------------------------------------------------------- void String::swapContent (String& s) { void* tmp = s.buffer; uint32 tmpLen = s.len; bool tmpWide = s.isWide; s.buffer = buffer; s.len = len; s.isWide = isWide; buffer = tmp; len = tmpLen; isWide = tmpWide; } //----------------------------------------------------------------------------- void String::take (String& other) { resize (0, other.isWide); buffer = other.buffer; len = other.len; other.buffer = nullptr; other.len = 0; } //----------------------------------------------------------------------------- void String::take (void* b, bool wide) { resize (0, wide); buffer = b; isWide = wide; updateLength (); } //----------------------------------------------------------------------------- void* String::pass () { void* res = buffer; len = 0; buffer = nullptr; return res; } //----------------------------------------------------------------------------- void String::passToVariant (FVariant& var) { void* passed = pass (); if (isWide) { if (passed) { var.setString16 ((const char16*)passed); var.setOwner (true); } else var.setString16 (kEmptyString16); } else { if (passed) { var.setString8 ((const char8*)passed); var.setOwner (true); } else var.setString8 (kEmptyString8); } } //----------------------------------------------------------------------------- unsigned char* String::toPascalString (unsigned char* buf) { if (buffer) { if (isWide) { String tmp (*this); tmp.toMultiByte (); return tmp.toPascalString (buf); } int32 length = len; if (length > 255) length = 255; buf[0] = (uint8)length; while (length >= 0) { buf[length + 1] = buffer8[length]; length--; } return buf; } *buf = 0; return buf; } //----------------------------------------------------------------------------- const String& String::fromPascalString (const unsigned char* buf) { resize (0, false); isWide = 0; int32 length = buf[0]; resize (length + 1, false); buffer8[length] = 0; // cannot be removed, because we only do the 0-termination for multibyte buffer8 while (--length >= 0) buffer8[length] = buf[length + 1]; len = buf[0]; return *this; } #if SMTG_OS_MACOS //----------------------------------------------------------------------------- bool String::fromCFStringRef (const void* cfStr, uint32 encoding) { if (cfStr == 0) return false; CFStringRef strRef = (CFStringRef)cfStr; if (isWide) { CFRange range = {0, CFStringGetLength (strRef)}; CFIndex usedBytes; if (resize (static_cast (range.length + 1), true)) { if (encoding == 0xFFFF) encoding = kCFStringEncodingUnicode; if (CFStringGetBytes (strRef, range, encoding, ' ', false, (UInt8*)buffer16, range.length * 2, &usedBytes) > 0) { buffer16[usedBytes / 2] = 0; this->len = strlen16 (buffer16); return true; } } } else { if (cfStr == 0) return false; if (encoding == 0xFFFF) encoding = kCFStringEncodingASCII; int32 len = static_cast (CFStringGetLength (strRef) * 2); if (resize (++len, false)) { if (CFStringGetCString (strRef, buffer8, len, encoding)) { this->len = static_cast (strlen (buffer8)); return true; } } } return false; } //----------------------------------------------------------------------------- void* ConstString::toCFStringRef (uint32 encoding, bool mutableCFString) const { if (mutableCFString) { CFMutableStringRef str = CFStringCreateMutable (kCFAllocator, 0); if (isWide) { CFStringAppendCharacters (str, (const UniChar*)buffer16, len); return str; } else { if (encoding == 0xFFFF) encoding = kCFStringEncodingASCII; CFStringAppendCString (str, buffer8, encoding); return str; } } else { if (isWide) { if (encoding == 0xFFFF) encoding = kCFStringEncodingUnicode; return (void*)CFStringCreateWithBytes (kCFAllocator, (const unsigned char*)buffer16, len * 2, encoding, false); } else { if (encoding == 0xFFFF) encoding = kCFStringEncodingASCII; if (buffer8) return (void*)CFStringCreateWithCString (kCFAllocator, buffer8, encoding); else return (void*)CFStringCreateWithCString (kCFAllocator, "", encoding); } } return nullptr; } #endif //----------------------------------------------------------------------------- uint32 hashString8 (const char8* s, uint32 m) { uint32 h = 0; if (s) { for (h = 0; *s != '\0'; s++) h = (64 * h + *s) % m; } return h; } //----------------------------------------------------------------------------- uint32 hashString16 (const char16* s, uint32 m) { uint32 h = 0; if (s) { for (h = 0; *s != 0; s++) h = (64 * h + *s) % m; } return h; } //------------------------------------------------------------------------ template int32 tstrnatcmp (const T* s1, const T* s2, bool caseSensitive = true) { if (s1 == nullptr && s2 == nullptr) return 0; if (s1 == nullptr) return -1; if (s2 == nullptr) return 1; while (*s1 && *s2) { if (ConstString::isCharDigit (*s1) && ConstString::isCharDigit (*s2)) { int32 s1LeadingZeros = 0; while (*s1 == '0') { s1++; // skip leading zeros s1LeadingZeros++; } int32 s2LeadingZeros = 0; while (*s2 == '0') { s2++; // skip leading zeros s2LeadingZeros++; } int32 countS1Digits = 0; while (*(s1 + countS1Digits) && ConstString::isCharDigit (*(s1 + countS1Digits))) countS1Digits++; int32 countS2Digits = 0; while (*(s2 + countS2Digits) && ConstString::isCharDigit (*(s2 + countS2Digits))) countS2Digits++; if (countS1Digits != countS2Digits) return countS1Digits - countS2Digits; // one number is longer than the other for (int32 i = 0; i < countS1Digits; i++) { // countS1Digits == countS2Digits if (*s1 != *s2) return (int32) (*s1 - *s2); // the digits differ s1++; s2++; } if (s1LeadingZeros != s2LeadingZeros) return s1LeadingZeros - s2LeadingZeros; // differentiate by the number of leading zeros } else { if (caseSensitive == false) { T srcToUpper = static_cast (toupper (*s1)); T dstToUpper = static_cast (toupper (*s2)); if (srcToUpper != dstToUpper) return (int32) (srcToUpper - dstToUpper); } else if (*s1 != *s2) return (int32) (*s1 - *s2); s1++; s2++; } } if (*s1 == 0 && *s2 == 0) return 0; if (*s1 == 0) return -1; if (*s2 == 0) return 1; return (int32) (*s1 - *s2); } //------------------------------------------------------------------------ int32 strnatcmp8 (const char8* s1, const char8* s2, bool caseSensitive /*= true*/) { return tstrnatcmp (s1, s2, caseSensitive); } //------------------------------------------------------------------------ int32 strnatcmp16 (const char16* s1, const char16* s2, bool caseSensitive /*= true*/) { return tstrnatcmp (s1, s2, caseSensitive); } //----------------------------------------------------------------------------- // StringObject Implementation //----------------------------------------------------------------------------- void PLUGIN_API StringObject::setText (const char8* text) { assign (text); } //----------------------------------------------------------------------------- void PLUGIN_API StringObject::setText8 (const char8* text) { assign (text); } //----------------------------------------------------------------------------- void PLUGIN_API StringObject::setText16 (const char16* text) { assign (text); } //----------------------------------------------------------------------------- const char8* PLUGIN_API StringObject::getText8 () { return text8 (); } //----------------------------------------------------------------------------- const char16* PLUGIN_API StringObject::getText16 () { return text16 (); } //----------------------------------------------------------------------------- void PLUGIN_API StringObject::take (void* s, bool _isWide) { String::take (s, _isWide); } //----------------------------------------------------------------------------- bool PLUGIN_API StringObject::isWideString () const { return String::isWideString (); } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/classfactoryhelpers.h0000644000000000000000000000013015124701711022642 xustar0029 mtime=1767080905.18496357 30 atime=1767080905.184211274 29 ctime=1767080905.18496357 qtractor-1.5.11/src/vst3/base/source/classfactoryhelpers.h0000644000175000001440000000535715124701711022646 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/classfactoryhelpers.h // Created by : Steinberg, 03/2017 // Description : Class factory // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once //------------------------------------------------------------------------------ // Helper Macros. Not intended for direct use. // Use: // META_CLASS(className), // META_CLASS_IFACE(className,Interface), // META_CLASS_SINGLE(className,Interface) // instead. //------------------------------------------------------------------------------ #define META_CREATE_FUNC(funcName) static FUnknown* funcName () #define CLASS_CREATE_FUNC(className) \ namespace Meta { \ META_CREATE_FUNC (make##className) { return (NEW className)->unknownCast (); } \ } #define SINGLE_CREATE_FUNC(className) \ namespace Meta { \ META_CREATE_FUNC (make##className) { return className::instance ()->unknownCast (); } \ } #define _META_CLASS(className) \ namespace Meta { \ static Steinberg::MetaClass meta##className ((#className), Meta::make##className); \ } #define _META_CLASS_IFACE(className, Interface) \ namespace Meta { \ static Steinberg::MetaClass meta##Interface##className ((#className), Meta::make##className, \ Interface##_iid); \ } /** TODO */ #define META_CLASS(className) \ CLASS_CREATE_FUNC (className) \ _META_CLASS (className) /** TODO */ #define META_CLASS_IFACE(className, Interface) \ CLASS_CREATE_FUNC (className) \ _META_CLASS_IFACE (className, Interface) /** TODO */ #define META_CLASS_SINGLE(className, Interface) \ SINGLE_CREATE_FUNC (className) \ _META_CLASS_IFACE (className, Interface) qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fdynlib.h0000644000000000000000000000012715124701711020217 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fdynlib.h0000644000175000001440000000636015124701711020210 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fdynlib.h // Created by : Steinberg, 1998 // Description : Dynamic library loading // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- //------------------------------------------------------------------------ /** @file base/source/fdynlib.h Platform independent dynamic library loading. */ //------------------------------------------------------------------------ #pragma once #include "pluginterfaces/base/ftypes.h" #include "base/source/fobject.h" namespace Steinberg { //------------------------------------------------------------------------ /** Platform independent dynamic library loader. */ //------------------------------------------------------------------------ class FDynLibrary : public FObject { public: //------------------------------------------------------------------------ /** Constructor. Loads the specified dynamic library. @param[in] name the path of the library to load. @param[in] addExtension if @c true append the platform dependent default extension to @c name. @remarks - If @c name specifies a full path, the FDynLibrary searches only that path for the library. - If @c name specifies a relative path or a name without path, FDynLibrary uses a standard search strategy of the current platform to find the library; - If @c name is @c NULL the library is not loaded. - Use init() to load the library. */ FDynLibrary (const tchar* name = nullptr, bool addExtension = true); /** Destructor. The destructor unloads the library.*/ ~FDynLibrary () override; /** Loads the library if not already loaded. This function is normally called by FDynLibrary(). @remarks If the library is already loaded, this call has no effect. */ bool init (const tchar* name, bool addExtension = true); /** Returns the address of the procedure @c name */ void* getProcAddress (const char* name); /** Returns true when the library was successfully loaded. */ bool isLoaded () {return isloaded;} /** Unloads the library if it is loaded. This function is called by ~FDynLibrary (). */ bool unload (); /** Returns the platform dependent representation of the library instance. */ void* getPlatformInstance () const { return instance; } #if SMTG_OS_MACOS /** Returns @c true if the library is a bundle (Mac only). */ bool isBundleLib () const { return isBundle; } #endif //------------------------------------------------------------------------ OBJ_METHODS(FDynLibrary, FObject) protected: bool isloaded; void* instance; #if SMTG_OS_MACOS void* firstSymbol; bool isBundle; #endif }; //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fobject.cpp0000644000000000000000000000012715124701711020537 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fobject.cpp0000644000175000001440000001674015124701711020533 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fobject.cpp // Created by : Steinberg, 2008 // Description : Basic Object implementing FUnknown // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "base/source/fobject.h" #include "base/thread/include/flock.h" #include #include #define SMTG_VALIDATE_DEPENDENCY_COUNT DEVELOPMENT // validating dependencyCount #if SMTG_DEPENDENCY_COUNT #include "base/source/updatehandler.h" #define SMTG_DEPENDENCY_CHECK_LEVEL 1 // 1 => minimal assert, 2 => full assert #endif // SMTG_DEPENDENCY_COUNT namespace Steinberg { // Entry point for addRef/release tracking. See fobjecttracker.h //------------------------------------------------------------------------ #if DEVELOPMENT using FObjectTrackerFn = std::function; FObjectTrackerFn gFObjectTracker = nullptr; #endif IUpdateHandler* FObject::gUpdateHandler = nullptr; //------------------------------------------------------------------------ const FUID FObject::iid; //------------------------------------------------------------------------ struct FObjectIIDInitializer { // the object iid is always generated so that different components // only can cast to their own objects // this initializer must be after the definition of FObject::iid, otherwise // the default constructor of FUID will clear the generated iid FObjectIIDInitializer () { const_cast (FObject::iid).generate (); } } gFObjectIidInitializer; //------------------------------------------------------------------------ FObject::~FObject () { #if SMTG_DEPENDENCY_COUNT && DEVELOPMENT static bool localNeverDebugger = false; #endif #if DEVELOPMENT if (refCount > 1) FDebugPrint ("Refcount is %d when trying to delete %s\n", refCount, isA ()); #endif #if SMTG_DEPENDENCY_COUNT #if SMTG_DEPENDENCY_CHECK_LEVEL >= 1 if (gUpdateHandler) { #if DEVELOPMENT SMTG_ASSERT (dependencyCount == 0 || localNeverDebugger); #endif // DEVELOPMENT } #endif #endif // SMTG_DEPENDENCY_COUNT #if SMTG_VALIDATE_DEPENDENCY_COUNT if (!gUpdateHandler || gUpdateHandler != UpdateHandler::instance (false)) return; auto updateHandler = UpdateHandler::instance (); if (!updateHandler || updateHandler == this) return; SMTG_ASSERT ((updateHandler->checkDeferred (this) == false || localNeverDebugger) && "'this' has scheduled a deferUpdate that was not yet delivered"); if (updateHandler->hasDependencies (this)) { SMTG_ASSERT ( (false || localNeverDebugger) && "Another object is still dependent on 'this'. This leads to zombie entries in the dependency map that can later crash."); FDebugPrint ("Object still has dependencies %x %s\n", this, this->isA ()); updateHandler->printForObject (this); } #endif // SMTG_VALIDATE_DEPENDENCY_COUNT } //------------------------------------------------------------------------ uint32 PLUGIN_API FObject::addRef () { #if DEVELOPMENT if (gFObjectTracker) { gFObjectTracker (this, true); } #endif return FUnknownPrivate::atomicAdd (refCount, 1); } //------------------------------------------------------------------------ uint32 PLUGIN_API FObject::release () { #if DEVELOPMENT if (gFObjectTracker) { gFObjectTracker (this, false); } #endif auto rc = FUnknownPrivate::atomicAdd (refCount, -1); if (rc == 0) { refCount = -1000; delete this; return 0; } return rc; } //------------------------------------------------------------------------ tresult PLUGIN_API FObject::queryInterface (const TUID _iid, void** obj) { QUERY_INTERFACE (_iid, obj, FUnknown::iid, FUnknown) QUERY_INTERFACE (_iid, obj, IDependent::iid, IDependent) QUERY_INTERFACE (_iid, obj, FObject::iid, FObject) *obj = nullptr; return kNoInterface; } //------------------------------------------------------------------------ void FObject::addDependent (IDependent* dep) { if (!gUpdateHandler) return; gUpdateHandler->addDependent (unknownCast (), dep); #if SMTG_DEPENDENCY_COUNT dependencyCount++; #endif } //------------------------------------------------------------------------ void FObject::removeDependent (IDependent* dep) { #if SMTG_DEPENDENCY_COUNT && DEVELOPMENT static bool localNeverDebugger = false; #endif if (!gUpdateHandler) return; #if SMTG_DEPENDENCY_COUNT if (gUpdateHandler != UpdateHandler::instance (false)) { gUpdateHandler->removeDependent (unknownCast (), dep); dependencyCount--; return; } #if SMTG_DEPENDENCY_CHECK_LEVEL > 1 SMTG_ASSERT ((dependencyCount > 0 || localNeverDebugger) && "All dependencies have already been removed - mmichaelis 7/2021"); #endif size_t removeCount; UpdateHandler::instance ()->removeDependent (unknownCast (), dep, removeCount); if (removeCount == 0) { #if SMTG_DEPENDENCY_CHECK_LEVEL > 1 SMTG_ASSERT (localNeverDebugger && "No dependency to remove - ygrabit 8/2021"); #endif } else { SMTG_ASSERT ((removeCount == 1 || localNeverDebugger) && "Duplicated dependencies established - mmichaelis 7/2021"); } dependencyCount -= (int16)removeCount; #else gUpdateHandler->removeDependent (unknownCast (), dep); #endif // SMTG_DEPENDENCY_COUNT } //------------------------------------------------------------------------ void FObject::changed (int32 msg) { if (gUpdateHandler) gUpdateHandler->triggerUpdates (unknownCast (), msg); else updateDone (msg); } //------------------------------------------------------------------------ void FObject::deferUpdate (int32 msg) { if (gUpdateHandler) gUpdateHandler->deferUpdates (unknownCast (), msg); else updateDone (msg); } //------------------------------------------------------------------------ /** Automatic creation and destruction of singleton instances. */ //------------------------------------------------------------------------ namespace Singleton { using ObjectVector = std::vector; ObjectVector* singletonInstances = nullptr; bool singletonsTerminated = false; Steinberg::Base::Thread::FLock* singletonsLock; bool isTerminated () { return singletonsTerminated; } void lockRegister () { if (!singletonsLock) // assume first call not from multiple threads singletonsLock = NEW Steinberg::Base::Thread::FLock; singletonsLock->lock (); } void unlockRegister () { singletonsLock->unlock (); } void registerInstance (FObject** o) { SMTG_ASSERT (singletonsTerminated == false) if (singletonsTerminated == false) { if (singletonInstances == nullptr) singletonInstances = NEW std::vector; singletonInstances->push_back (o); } } struct Deleter { ~Deleter () { singletonsTerminated = true; if (singletonInstances) { for (Steinberg::FObject** obj : *singletonInstances) { (*obj)->release (); *obj = nullptr; obj = nullptr; } delete singletonInstances; singletonInstances = nullptr; } delete singletonsLock; singletonsLock = nullptr; } } deleter; } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fcleanup.h0000644000000000000000000000012715124701711020365 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fcleanup.h0000644000175000001440000002513615124701711020360 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fcleanup.h // Created by : Steinberg, 2008 // Description : Template classes for automatic resource cleanup // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include namespace Steinberg { /** Template definition for classes that help guarding against memory leaks. A stack allocated object of this type automatically deletes an at construction time passed dynamically allocated single object when it reaches the end of its scope. \n\n Intended usage: \code { int* pointerToInt = new int; Steinberg::FDeleter deleter (pointerToInt); // Do something with the variable behind pointerToInt. } // No memory leak here, destructor of deleter cleans up the integer. \endcode */ //------------------------------------------------------------------------ template struct FDeleter { /// Constructor. _toDelete is a pointer to the dynamically allocated object that is to be /// deleted when this FDeleter object's destructor is executed. FDeleter (T* _toDelete) : toDelete (_toDelete) {} /// Destructor. Calls delete on the at construction time passed pointer. ~FDeleter () { if (toDelete) delete toDelete; } T* toDelete; ///< Remembers the object that is to be deleted during destruction. }; /** Template definition for classes that help guarding against memory leaks. A stack allocated object of this type automatically deletes an at construction time passed dynamically allocated array of objects when it reaches the end of its scope. \n\n Intended usage: \code { int* pointerToIntArray = new int[10]; Steinberg::FArrayDeleter deleter (pointerToIntArray); // Do something with the array behind pointerToIntArray. } // No memory leak here, destructor of deleter cleans up the integer array. \endcode */ //------------------------------------------------------------------------ template struct FArrayDeleter { /// Constructor. _arrayToDelete is a pointer to the dynamically allocated array of objects that /// is to be deleted when this FArrayDeleter object's destructor is executed. FArrayDeleter (T* _arrayToDelete) : arrayToDelete (_arrayToDelete) {} /// Destructor. Calls delete[] on the at construction time passed pointer. ~FArrayDeleter () { if (arrayToDelete) delete[] arrayToDelete; } T* arrayToDelete; ///< Remembers the array of objects that is to be deleted during destruction. }; /** Template definition for classes that help guarding against dangling pointers. A stack allocated object of this type automatically resets an at construction time passed pointer to null when it reaches the end of its scope. \n\n Intended usage: \code int* pointerToInt = 0; { int i = 1; pointerToInt = &i; Steinberg::FPtrNuller ptrNuller (pointerToInt); // Do something with pointerToInt. } // No dangling pointer here, pointerToInt is reset to 0 by destructor of ptrNuller. \endcode */ //------------------------------------------------------------------------ template struct FPtrNuller { /// Constructor. _toNull is a reference to the pointer that is to be reset to NULL when this /// FPtrNuller object's destructor is executed. FPtrNuller (T*& _toNull) : toNull (_toNull) {} /// Destructor. Calls delete[] on the at construction time passed pointer. ~FPtrNuller () { toNull = 0; } T*& toNull; ///< Remembers the pointer that is to be set to NULL during destruction. }; /** Template definition for classes that help resetting an object's value. A stack allocated object of this type automatically resets the value of an at construction time passed object to null when it reaches the end of its scope. \n\n Intended usage: \code int theObject = 0; { Steinberg::FNuller theNuller (theObject); theObject = 1; } // Here the destructor of theNuller resets the value of theObject to 0. \endcode */ //------------------------------------------------------------------------ template struct FNuller { /// Constructor. _toNull is a reference to the object that is to be assigned 0 when this FNuller /// object's destructor is executed. FNuller (T& _toNull) : toNull (_toNull) {} /// Destructor. Assigns 0 to the at construction time passed object reference. ~FNuller () { toNull = 0; } T& toNull; ///< Remembers the object that is to be assigned 0 during destruction. }; /** Class definition for objects that help resetting boolean variables. A stack allocated object of this type automatically sets an at construction time passed boolean variable immediately to TRUE and resets the same variable to FALSE when it reaches the end of its own scope. \n\n Intended usage: \code bool theBoolean = false; { Steinberg::FBoolSetter theBoolSetter (theBoolean); // Here the constructor of theBoolSetter sets theBoolean to TRUE. // Do something. } // Here the destructor of theBoolSetter resets theBoolean to FALSE. \endcode */ //------------------------------------------------------------------------ template struct FBooleanSetter { /// Constructor. _toSet is a reference to the boolean that is set to TRUE immediately in this /// constructor call and gets reset to FALSE when this FBoolSetter object's destructor is /// executed. FBooleanSetter (T& _toSet) : toSet (_toSet) { toSet = true; } /// Destructor. Resets the at construction time passed boolean to FALSE. ~FBooleanSetter () { toSet = false; } T& toSet; ///< Remembers the boolean that is to be reset during destruction. }; using FBoolSetter = FBooleanSetter; /** Class definition for objects that help setting boolean variables. A stack allocated object of this type automatically sets an at construction time passed boolean variable to TRUE if the given condition is met. At the end of its own scope the stack object will reset the same boolean variable to FALSE, if it wasn't set so already. \n\n Intended usage: \code bool theBoolean = false; { bool creativityFirst = true; Steinberg::FConditionalBoolSetter theCBSetter (theBoolean, creativityFirst); // Here the constructor of theCBSetter sets theBoolean to the value of creativityFirst. // Do something. } // Here the destructor of theCBSetter resets theBoolean to FALSE. \endcode */ //------------------------------------------------------------------------ struct FConditionalBoolSetter { /// Constructor. _toSet is a reference to the boolean that is to be set. If the in the second /// parameter given condition is TRUE then also _toSet is set to TRUE immediately. FConditionalBoolSetter (bool& _toSet, bool condition) : toSet (_toSet) { if (condition) toSet = true; } /// Destructor. Resets the at construction time passed boolean to FALSE. ~FConditionalBoolSetter () { toSet = false; } bool& toSet; ///< Remembers the boolean that is to be reset during destruction. }; /** Template definition for classes that help closing resources. A stack allocated object of this type automatically calls the close method of an at construction time passed object when it reaches the end of its scope. It goes without saying that the given type needs to have a close method. \n\n Intended usage: \code struct CloseableObject { void close() {}; }; { CloseableObject theObject; Steinberg::FCloser theCloser (&theObject); // Do something. } // Here the destructor of theCloser calls the close method of theObject. \endcode */ template struct FCloser { /// Constructor. _obj is the pointer on which close is to be called when this FCloser object's /// destructor is executed. FCloser (T* _obj) : obj (_obj) {} /// Destructor. Calls the close function on the at construction time passed pointer. ~FCloser () { if (obj) obj->close (); } T* obj; ///< Remembers the pointer on which close is to be called during destruction. }; /** Class definition for objects that help guarding against memory leaks. A stack allocated object of this type automatically frees the "malloced" memory behind an at construction time passed pointer when it reaches the end of its scope. */ //------------------------------------------------------------------------ /*! \class FMallocReleaser */ //------------------------------------------------------------------------ class FMallocReleaser { public: /// Constructor. _data is the pointer to the memory on which free is to be called when this /// FMallocReleaser object's destructor is executed. FMallocReleaser (void* _data) : data (_data) {} /// Destructor. Calls the free function on the at construction time passed pointer. ~FMallocReleaser () { if (data) free (data); } //------------------------------------------------------------------------ protected: void* data; ///< Remembers the pointer on which free is to be called during destruction. }; //------------------------------------------------------------------------ } // namespace Steinberg #if SMTG_OS_MACOS typedef const void* CFTypeRef; extern "C" { extern void CFRelease (CFTypeRef cf); } namespace Steinberg { /** Class definition for objects that helps releasing CoreFoundation objects. A stack allocated object of this type automatically releases an at construction time passed CoreFoundation object when it reaches the end of its scope. Only available on Macintosh platform. */ //------------------------------------------------------------------------ /*! \class CFReleaser */ //------------------------------------------------------------------------ class CFReleaser { public: /// Constructor. _obj is the reference to an CoreFoundation object which is to be released when this CFReleaser object's destructor is executed. CFReleaser (CFTypeRef _obj) : obj (_obj) {} /// Destructor. Releases the at construction time passed object. ~CFReleaser () { if (obj) CFRelease (obj); } protected: CFTypeRef obj; ///< Remembers the object which is to be released during destruction. }; //------------------------------------------------------------------------ } // namespace Steinberg #endif // SMTG_OS_MACOS qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fdynlib.cpp0000644000000000000000000000012715124701711020552 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fdynlib.cpp0000644000175000001440000001334515124701711020544 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fdynlib.cpp // Created by : Steinberg, 1998 // Description : Dynamic library loading // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "base/source/fdynlib.h" #include "pluginterfaces/base/fstrdefs.h" #include "base/source/fstring.h" #if SMTG_OS_WINDOWS #include #elif SMTG_OS_MACOS #include #include #if !SMTG_OS_IOS static const Steinberg::tchar kUnixDelimiter = STR ('/'); #endif #endif namespace Steinberg { #if SMTG_OS_MACOS #include // we ignore for the moment that the NSAddImage functions are deprecated #pragma GCC diagnostic ignored "-Wdeprecated-declarations" static bool CopyProcessPath (Steinberg::String& name) { Dl_info info; if (dladdr ((const void*)CopyProcessPath, &info)) { if (info.dli_fname) { name.assign (info.dli_fname); #ifdef UNICODE name.toWideString (); #endif return true; } } return false; } #endif //------------------------------------------------------------------------ // FDynLibrary //------------------------------------------------------------------------ FDynLibrary::FDynLibrary (const tchar* n, bool addExtension) : isloaded (false) , instance (nullptr) { if (n) init (n, addExtension); } //------------------------------------------------------------------------ FDynLibrary::~FDynLibrary () { unload (); } //------------------------------------------------------------------------ bool FDynLibrary::init (const tchar* n, bool addExtension) { if (isLoaded ()) return true; Steinberg::String name (n); #if SMTG_OS_WINDOWS if (addExtension) name.append (STR (".dll")); instance = LoadLibrary (name); if (instance) isloaded = true; #elif SMTG_OS_MACOS isBundle = false; // first check if it is a bundle if (addExtension) name.append (STR (".bundle")); if (name.getChar16 (0) != STR('/')) // no absoltue path { Steinberg::String p; if (CopyProcessPath (p)) { Steinberg::int32 index = p.findLast (STR ('/')); p.remove (index+1); name = p + name; } } CFStringRef fsString = (CFStringRef)name.toCFStringRef (); CFURLRef url = CFURLCreateWithFileSystemPath (NULL, fsString, kCFURLPOSIXPathStyle, true); if (url) { CFBundleRef bundle = CFBundleCreate (NULL, url); if (bundle) { if (CFBundleLoadExecutable (bundle)) { isBundle = true; instance = (void*)bundle; } else CFRelease (bundle); } CFRelease (url); } CFRelease (fsString); name.assign (n); #if !SMTG_OS_IOS if (!isBundle) { // now we check for a dynamic library firstSymbol = NULL; if (addExtension) { name.append (STR (".dylib")); } // Only if name is a relative path we use the Process Path as root: if (name[0] != kUnixDelimiter) { Steinberg::String p; if (CopyProcessPath (p)) { Steinberg::int32 index = p.findLast (STR ("/")); p.remove (index+1); p.append (name); p.toMultiByte (Steinberg::kCP_Utf8); instance = (void*) NSAddImage (p, NSADDIMAGE_OPTION_RETURN_ON_ERROR); } } // Last but not least let the system search for it // if (instance == 0) { name.toMultiByte (Steinberg::kCP_Utf8); instance = (void*) NSAddImage (name, NSADDIMAGE_OPTION_RETURN_ON_ERROR); } } #endif // !SMTG_OS_IOS if (instance) isloaded = true; #endif return isloaded; } //------------------------------------------------------------------------ bool FDynLibrary::unload () { if (!isLoaded ()) return false; #if SMTG_OS_WINDOWS FreeLibrary ((HINSTANCE)instance); #elif SMTG_OS_MACOS if (isBundle) { if (CFGetRetainCount ((CFTypeRef)instance) == 1) CFBundleUnloadExecutable ((CFBundleRef)instance); CFRelease ((CFBundleRef)instance); } else { // we don't use this anymore as the darwin dyld can't unload dynamic libraries yet and may crash /* if (firstSymbol) { NSModule module = NSModuleForSymbol ((NSSymbol)firstSymbol); if (module) NSUnLinkModule (module, NSUNLINKMODULE_OPTION_NONE); }*/ } #endif instance = nullptr; isloaded = false; return true; } //------------------------------------------------------------------------ void* FDynLibrary::getProcAddress (const char* name) { if (!isloaded) return nullptr; #if SMTG_OS_WINDOWS return (void*)GetProcAddress ((HINSTANCE)instance, name); #elif SMTG_OS_MACOS if (isBundle) { CFStringRef functionName = CFStringCreateWithCString (NULL, name, kCFStringEncodingASCII); void* result = CFBundleGetFunctionPointerForName ((CFBundleRef)instance, functionName); CFRelease (functionName); return result; } #if !SMTG_OS_IOS else { char* symbolName = (char*) malloc (strlen (name) + 2); strcpy (symbolName, "_"); strcat (symbolName, name); NSSymbol symbol; symbol = NSLookupSymbolInImage ((const struct mach_header*)instance, symbolName, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); free (symbolName); if (symbol) { if (firstSymbol == NULL) firstSymbol = symbol; return NSAddressOfSymbol (symbol); } } #endif // !SMTG_OS_IOS return nullptr; #else return nullptr; #endif } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/timer.cpp0000644000000000000000000000013215124701711020237 xustar0030 mtime=1767080905.186211266 30 atime=1767080905.186211266 30 ctime=1767080905.186211266 qtractor-1.5.11/src/vst3/base/source/timer.cpp0000644000175000001440000002266415124701711020241 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/timer.cpp // Created by : Steinberg, 05/2006 // Description : Timer class for receiving triggers at regular intervals // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "base/source/timer.h" namespace Steinberg { static bool timersEnabled = true; //------------------------------------------------------------------------ DisableDispatchingTimers::DisableDispatchingTimers () { oldState = timersEnabled; timersEnabled = false; } //------------------------------------------------------------------------ DisableDispatchingTimers::~DisableDispatchingTimers () { timersEnabled = oldState; } //------------------------------------------------------------------------ namespace SystemTime { //------------------------------------------------------------------------ struct ZeroStartTicks { static const uint64 startTicks; static int32 getTicks32 () { return static_cast (SystemTime::getTicks64 () - startTicks); } }; const uint64 ZeroStartTicks::startTicks = SystemTime::getTicks64 (); //------------------------------------------------------------------------ int32 getTicks () { return ZeroStartTicks::getTicks32 (); } //------------------------------------------------------------------------ } // namespace SystemTime } // namespace Steinberg #if SMTG_OS_MACOS #include #include #ifdef verify #undef verify #endif namespace Steinberg { namespace SystemTime { //------------------------------------------------------------------------ struct MachTimeBase { private: struct mach_timebase_info timebaseInfo; MachTimeBase () { mach_timebase_info (&timebaseInfo); } static const MachTimeBase& instance () { static MachTimeBase gInstance; return gInstance; } public: static double getTimeNanos () { const MachTimeBase& timeBase = instance (); double absTime = static_cast (mach_absolute_time ()); // nano seconds double d = (absTime / timeBase.timebaseInfo.denom) * timeBase.timebaseInfo.numer; return d; } }; /* @return the current system time in milliseconds */ uint64 getTicks64 () { return static_cast (MachTimeBase::getTimeNanos () / 1000000.); } //------------------------------------------------------------------------ } // namespace SystemTime //------------------------------------------------------------------------ class MacPlatformTimer : public Timer { public: MacPlatformTimer (ITimerCallback* callback, uint32 milliseconds); ~MacPlatformTimer (); void stop () override; bool verify () const { return platformTimer != nullptr; } static void timerCallback (CFRunLoopTimerRef timer, void* info); protected: CFRunLoopTimerRef platformTimer; ITimerCallback* callback; }; //------------------------------------------------------------------------ MacPlatformTimer::MacPlatformTimer (ITimerCallback* callback, uint32 milliseconds) : platformTimer (nullptr), callback (callback) { if (callback) { CFRunLoopTimerContext timerContext = {}; timerContext.info = this; platformTimer = CFRunLoopTimerCreate ( kCFAllocatorDefault, CFAbsoluteTimeGetCurrent () + milliseconds * 0.001, milliseconds * 0.001f, 0, 0, timerCallback, &timerContext); if (platformTimer) CFRunLoopAddTimer (CFRunLoopGetMain (), platformTimer, kCFRunLoopCommonModes); } } //------------------------------------------------------------------------ MacPlatformTimer::~MacPlatformTimer () { stop (); } //------------------------------------------------------------------------ void MacPlatformTimer::stop () { if (platformTimer) { CFRunLoopRemoveTimer (CFRunLoopGetMain (), platformTimer, kCFRunLoopCommonModes); CFRelease (platformTimer); platformTimer = nullptr; } } //------------------------------------------------------------------------ void MacPlatformTimer::timerCallback (CFRunLoopTimerRef, void* info) { if (timersEnabled) { if (auto timer = (MacPlatformTimer*)info) timer->callback->onTimer (timer); } } //------------------------------------------------------------------------ Timer* Timer::create (ITimerCallback* callback, uint32 milliseconds) { auto timer = NEW MacPlatformTimer (callback, milliseconds); if (timer->verify ()) return timer; timer->release (); return nullptr; } //------------------------------------------------------------------------ } // namespace Steinberg #elif SMTG_OS_WINDOWS #include #include #include namespace Steinberg { namespace SystemTime { //------------------------------------------------------------------------ /* @return the current system time in milliseconds */ uint64 getTicks64 () { #if defined(__MINGW32__) return GetTickCount (); #else return GetTickCount64 (); #endif } // namespace SystemTime } // namespace Steinberg class WinPlatformTimer; using WinPlatformTimerList = std::list; //------------------------------------------------------------------------ // WinPlatformTimer //------------------------------------------------------------------------ class WinPlatformTimer : public Timer { public: //------------------------------------------------------------------------ WinPlatformTimer (ITimerCallback* callback, uint32 milliseconds); ~WinPlatformTimer () override; void stop () override; bool verify () const { return id != 0; } //------------------------------------------------------------------------ private: UINT_PTR id; ITimerCallback* callback; static void addTimer (WinPlatformTimer* t); static void removeTimer (WinPlatformTimer* t); static void CALLBACK TimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); static WinPlatformTimerList* timers; }; //------------------------------------------------------------------------ WinPlatformTimerList* WinPlatformTimer::timers = nullptr; //------------------------------------------------------------------------ WinPlatformTimer::WinPlatformTimer (ITimerCallback* callback, uint32 milliseconds) : callback (callback) { id = SetTimer (nullptr, 0, milliseconds, TimerProc); if (id) addTimer (this); } //------------------------------------------------------------------------ WinPlatformTimer::~WinPlatformTimer () { stop (); } //------------------------------------------------------------------------ void WinPlatformTimer::addTimer (WinPlatformTimer* t) { if (timers == nullptr) timers = NEW WinPlatformTimerList; timers->push_back (t); } //------------------------------------------------------------------------ void WinPlatformTimer::removeTimer (WinPlatformTimer* t) { if (!timers) return; WinPlatformTimerList::iterator it = std::find (timers->begin (), timers->end (), t); if (it != timers->end ()) timers->erase (it); if (timers->empty ()) { delete timers; timers = nullptr; } } //------------------------------------------------------------------------ void WinPlatformTimer::stop () { if (!id) return; KillTimer (nullptr, id); removeTimer (this); id = 0; } //------------------------------------------------------------------------ void CALLBACK WinPlatformTimer::TimerProc (HWND /*hwnd*/, UINT /*uMsg*/, UINT_PTR idEvent, DWORD /*dwTime*/) { if (timersEnabled && timers) { WinPlatformTimerList::const_iterator it = timers->cbegin (); while (it != timers->cend ()) { WinPlatformTimer* timer = *it; if (timer->id == idEvent) { if (timer->callback) timer->callback->onTimer (timer); return; } ++it; } } } //------------------------------------------------------------------------ Timer* Timer::create (ITimerCallback* callback, uint32 milliseconds) { auto* platformTimer = NEW WinPlatformTimer (callback, milliseconds); if (platformTimer->verify ()) return platformTimer; platformTimer->release (); return nullptr; } //------------------------------------------------------------------------ } // namespace Steinberg #elif SMTG_OS_LINUX #include #include namespace Steinberg { namespace SystemTime { //------------------------------------------------------------------------ /* @return the current system time in milliseconds */ uint64 getTicks64 () { struct timespec ts; clock_gettime (CLOCK_MONOTONIC, &ts); return static_cast (ts.tv_sec) * 1000 + static_cast (ts.tv_nsec) / 1000000; } //------------------------------------------------------------------------ } // namespace SystemTime static CreateTimerFunc createTimerFunc = nullptr; //------------------------------------------------------------------------ void InjectCreateTimerFunction (CreateTimerFunc f) { createTimerFunc = f; } //------------------------------------------------------------------------ Timer* Timer::create (ITimerCallback* callback, uint32 milliseconds) { if (createTimerFunc) return createTimerFunc (callback, milliseconds); return nullptr; } //------------------------------------------------------------------------ } // namespace Steinberg #endif qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fbuffer.cpp0000644000000000000000000000012715124701711020542 xustar0029 mtime=1767080905.18496357 29 atime=1767080905.18496357 29 ctime=1767080905.18496357 qtractor-1.5.11/src/vst3/base/source/fbuffer.cpp0000644000175000001440000003373315124701711020537 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fbuffer.cpp // Created by : Steinberg, 2008 // Description : // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "base/source/fbuffer.h" #include "base/source/fstring.h" #include namespace Steinberg { //------------------------------------------------------------------------------------- Buffer::Buffer () : buffer (nullptr) , memSize (0) , fillSize (0) , delta (defaultDelta) {} //------------------------------------------------------------------------------------- Buffer::Buffer (uint32 s, uint8 initVal) : buffer (nullptr) , memSize (s) , fillSize (0) , delta (defaultDelta) { if (memSize == 0) return; buffer = (int8*)::malloc (memSize); if (buffer) memset (buffer, initVal, memSize); else memSize = 0; } //------------------------------------------------------------------------------------- Buffer::Buffer (uint32 s) : buffer (nullptr) , memSize (s) , fillSize (0) , delta (defaultDelta) { if (memSize == 0) return; buffer = (int8*)::malloc (memSize); if (!buffer) memSize = 0; } //------------------------------------------------------------------------------------- Buffer::Buffer (const void* b , uint32 s) : buffer (nullptr) , memSize (s) , fillSize (s) , delta (defaultDelta) { if (memSize == 0) return; buffer = (int8*)::malloc (memSize); if (buffer) memcpy (buffer, b, memSize); else { memSize = 0; fillSize = 0; } } //------------------------------------------------------------------------------------- Buffer::Buffer (const Buffer& bufferR) : buffer (nullptr) , memSize (bufferR.memSize) , fillSize (bufferR.fillSize) , delta (bufferR.delta) { if (memSize == 0) return; buffer = (int8*)::malloc (memSize); if (buffer) memcpy (buffer, bufferR.buffer, memSize); else memSize = 0; } //------------------------------------------------------------------------------------- Buffer::~Buffer () { if (buffer) ::free (buffer); buffer = nullptr; } //------------------------------------------------------------------------------------- void Buffer::operator = (const Buffer& b2) { if (&b2 != this) { setSize (b2.memSize); if (b2.memSize > 0 && buffer) memcpy (buffer, b2.buffer, b2.memSize); fillSize = b2.fillSize; delta = b2.delta; } } //------------------------------------------------------------------------------------- bool Buffer::operator == (const Buffer& b2)const { if (&b2 == this) return true; if (b2.getSize () != getSize ()) return false; return memcmp (this->int8Ptr (), b2.int8Ptr (), getSize ()) == 0 ? true : false; } //------------------------------------------------------------------------------------- uint32 Buffer::get (void* b, uint32 size) { uint32 maxGet = memSize - fillSize; if (size > maxGet) size = maxGet; if (size > 0) memcpy (b, buffer + fillSize, size); fillSize += size; return size; } //------------------------------------------------------------------------------------- bool Buffer::put (char16 c) { return put ((const void*)&c, sizeof (c)); } //------------------------------------------------------------------------------------- bool Buffer::put (uint8 byte) { if (grow (fillSize + 1) == false) return false; buffer [fillSize++] = byte; return true; } //------------------------------------------------------------------------------------- bool Buffer::put (char c) { if (grow (fillSize + 1) == false) return false; buffer [fillSize++] = c; return true; } //------------------------------------------------------------------------------------- bool Buffer::put (const void* toPut, uint32 s) { if (!toPut) return false; if (grow (fillSize + s) == false) return false; memcpy (buffer + fillSize, toPut, s); fillSize += s; return true; } //------------------------------------------------------------------------------------- bool Buffer::put (const String& str) { return put ((const void*)str.text () , (str.length () + 1) * sizeof (tchar)); } //------------------------------------------------------------------------------------- bool Buffer::appendString8 (const char8* s) { if (!s) return false; uint32 len = (uint32) strlen (s); return put (s, len); } //------------------------------------------------------------------------------------- bool Buffer::appendString16 (const char16* s) { if (!s) return false; ConstString str (s); uint32 len = (uint32) str.length () * sizeof (char16); return put (s, len); } //------------------------------------------------------------------------------------- bool Buffer::prependString8 (const char8* s) { if (!s) return false; uint32 len = (uint32) strlen (s); if (len > 0) { shiftStart (len); memcpy (buffer, s, len); return true; } return false; } //------------------------------------------------------------------------------------- bool Buffer::prependString16 (const char16* s) { if (!s) return false; ConstString str (s); uint32 len = (uint32) str.length () * sizeof (char16); if (len > 0) { shiftStart (len); memcpy (buffer, s, len); return true; } return false; } //------------------------------------------------------------------------------------- bool Buffer::prependString8 (char8 c) { shiftStart (sizeof (char)); char* b = (char*)buffer; b [0] = c; return true; } //------------------------------------------------------------------------------------- bool Buffer::prependString16 (char16 c) { shiftStart (sizeof (char16)); char16* b = (char16*)buffer; b [0] = c; return true; } //------------------------------------------------------------------------------------- bool Buffer::copy (uint32 from, uint32 to, uint32 bytes) { if (from + bytes > memSize || bytes == 0) return false; if (to + bytes > memSize) setSize (to + bytes); if (from + bytes > to && from < to) { // overlap Buffer tmp (buffer + from, bytes); memcpy (buffer + to, tmp, bytes); } else memcpy (buffer + to, buffer + from, bytes); return true; } //------------------------------------------------------------------------------------- bool Buffer::makeHexString (String& result) { unsigned char* data = uint8Ptr (); uint32 bytes = getSize (); if (data == nullptr || bytes == 0) return false; char8* stringBuffer = NEWSTR8 ((bytes * 2) + 1); if (!stringBuffer) return false; int32 count = 0; while (bytes > 0) { unsigned char t1 = ((*data) >> 4) & 0x0F; unsigned char t2 = (*data) & 0x0F; if (t1 < 10) t1 += '0'; else t1 = t1 - 10 + 'A'; if (t2 < 10) t2 += '0'; else t2 = t2 - 10 + 'A'; stringBuffer [count++] = t1; stringBuffer [count++] = t2; data++; bytes--; } stringBuffer [count] = 0; result.take ((void*)stringBuffer, false); return true; } //------------------------------------------------------------------------------------- bool Buffer::fromHexString (const char8* string) { flush (); if (string == nullptr) return false; int32 len = strlen8 (string); if (len == 0 || ((len & 1) == 1)/*odd number*/ ) return false; setSize (len / 2); unsigned char* data = uint8Ptr (); bool upper = true; int32 count = 0; while (count < len) { char c = string [count]; unsigned char d = 0; if (c >= '0' && c <= '9') d += c - '0'; else if (c >= 'A' && c <= 'F') d += c - 'A' + 10; else if (c >= 'a' && c <= 'f') d += c - 'a' + 10; else return false; // no hex string if (upper) data [count >> 1] = static_cast (d << 4); else data [count >> 1] += d; upper = !upper; count++; } setFillSize (len / 2); return true; } //------------------------------------------------------------------------ void Buffer::set (uint8 value) { if (buffer) memset (buffer, value, memSize); } //------------------------------------------------------------------------------------- bool Buffer::setFillSize (uint32 c) { if (c <= memSize) { fillSize = c; return true; } return false; } //------------------------------------------------------------------------------------- bool Buffer::truncateToFillSize () { if (fillSize < memSize) setSize (fillSize); return true; } //------------------------------------------------------------------------------------- bool Buffer::grow (uint32 newSize) { if (newSize > memSize) { if (delta == 0) delta = defaultDelta; uint32 s = ((newSize + delta - 1) / delta) * delta; return setSize (s); } return true; } //------------------------------------------------------------------------ void Buffer::shiftAt (uint32 position, int32 amount) { if (amount > 0) { if (grow (fillSize + amount)) { if (position < fillSize) memmove (buffer + amount + position, buffer + position, fillSize - position); fillSize += amount; } } else if (amount < 0 && fillSize > 0) { uint32 toRemove = -amount; if (toRemove < fillSize) { if (position < fillSize) memmove (buffer + position, buffer + toRemove + position, fillSize - position - toRemove); fillSize -= toRemove; } } } //------------------------------------------------------------------------------------- void Buffer::move (int32 amount, uint8 initVal) { if (memSize == 0) return; if (amount > 0) { if ((uint32)amount < memSize) { memmove (buffer + amount, buffer, memSize - amount); memset (buffer, initVal, amount); } else memset (buffer, initVal, memSize); } else { uint32 toRemove = -amount; if (toRemove < memSize) { memmove (buffer, buffer + toRemove, memSize - toRemove); memset (buffer + memSize - toRemove, initVal, toRemove); } else memset (buffer, initVal, memSize); } } //------------------------------------------------------------------------------------- bool Buffer::setSize (uint32 newSize) { if (memSize != newSize) { if (buffer) { if (newSize > 0) { int8* newBuffer = (int8*) ::realloc (buffer, newSize); if (newBuffer == nullptr) { newBuffer = (int8*)::malloc (newSize); if (newBuffer) { uint32 tmp = newSize; if (tmp > memSize) tmp = memSize; memcpy (newBuffer, buffer, tmp); ::free (buffer); buffer = newBuffer; } else { ::free (buffer); buffer = nullptr; } } else buffer = newBuffer; } else { ::free (buffer); buffer = nullptr; } } else buffer = (int8*)::malloc (newSize); if (newSize > 0 && !buffer) memSize = 0; else memSize = newSize; if (fillSize > memSize) fillSize = memSize; } return (newSize > 0) == (buffer != nullptr); } //------------------------------------------------------------------------------------- void Buffer::fillup (uint8 value) { if (getFree () > 0) memset (buffer + fillSize, value, getFree ()); } //------------------------------------------------------------------------------------- int8* Buffer::operator + (uint32 i) { if (i < memSize) return buffer + i; static int8 eof; eof = 0; return &eof; } //------------------------------------------------------------------------------------- bool Buffer::swap (int16 swapSize) { return swap (buffer, memSize, swapSize); } //------------------------------------------------------------------------------------- bool Buffer::swap (void* buffer, uint32 bufferSize, int16 swapSize) { if (swapSize != kSwap16 && swapSize != kSwap32 && swapSize != kSwap64) return false; if (swapSize == kSwap16) { for (uint32 count = 0 ; count < bufferSize ; count += 2) { SWAP_16 ( * (((int16*)buffer) + count) ); } } else if (swapSize == kSwap32) { for (uint32 count = 0 ; count < bufferSize ; count += 4) { SWAP_32 ( * (((int32*)buffer) + count) ); } } else if (swapSize == kSwap64) { for (uint32 count = 0 ; count < bufferSize ; count += 8) { SWAP_64 ( * (((int64*)buffer) + count) ); } } return true; } //------------------------------------------------------------------------------------- void Buffer::take (Buffer& from) { setSize (0); memSize = from.memSize; fillSize = from.fillSize; buffer = from.buffer; from.buffer = nullptr; from.memSize = 0; from.fillSize = 0; } //------------------------------------------------------------------------------------- int8* Buffer::pass () { int8* res = buffer; buffer = nullptr; memSize = 0; fillSize = 0; return res; } //------------------------------------------------------------------------------------- bool Buffer::toWideString (int32 sourceCodePage) { if (getFillSize () > 0) { if (str8 () [getFillSize () - 1] != 0) // multiByteToWideString only works with 0-terminated strings endString8 (); Buffer dest (getFillSize () * sizeof (char16)); int32 result = String::multiByteToWideString (dest.str16 (), str8 (), dest.getFree () / sizeof (char16), sourceCodePage); if (result > 0) { dest.setFillSize ((result - 1) * sizeof (char16)); take (dest); return true; } return false; } return true; } //------------------------------------------------------------------------------------- bool Buffer::toMultibyteString (int32 destCodePage) { if (getFillSize () > 0) { int32 textLength = getFillSize () / sizeof (char16); // wideStringToMultiByte only works with 0-terminated strings if (str16 () [textLength - 1] != 0) endString16 (); Buffer dest (getFillSize ()); int32 result = String::wideStringToMultiByte (dest.str8 (), str16 (), dest.getFree (), destCodePage); if (result > 0) { dest.setFillSize (result - 1); take (dest); return true; } return false; } return true; } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fdebug.cpp0000644000000000000000000000012715124701711020357 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fdebug.cpp0000644000175000001440000002030115124701711020337 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fdebug.cpp // Created by : Steinberg, 1995 // Description : There are 2 levels of debugging messages: // DEVELOPMENT During development // RELEASE Program is shipping. // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "base/source/fdebug.h" #if SMTG_OS_WINDOWS #include bool AmIBeingDebugged () { return IsDebuggerPresent (); } #endif #if SMTG_OS_LINUX #include #include #include //-------------------------------------------------------------------------- bool AmIBeingDebugged () { // TODO: check if GDB or LLDB is attached return true; } #endif #if SMTG_OS_MACOS #include #include #include #include //------------------------------------------------------------------------ // from Technical Q&A QA1361 (http://developer.apple.com/qa/qa2004/qa1361.html) //------------------------------------------------------------------------ bool AmIBeingDebugged () // Returns true if the current process is being debugged (either // running under the debugger or has a debugger attached post facto). { int mib[4]; struct kinfo_proc info; size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid (); // Call sysctl. size = sizeof (info); sysctl (mib, sizeof (mib) / sizeof (*mib), &info, &size, NULL, 0); // We're being debugged if the P_TRACED flag is set. return ((info.kp_proc.p_flag & P_TRACED) != 0); } #endif // SMTG_OS_MACOS #if DEVELOPMENT #include #include #include #include #include #if SMTG_OS_WINDOWS #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #endif #if _MSC_VER #include #endif #define vsnprintf _vsnprintf #define snprintf _snprintf #elif SMTG_OS_MACOS #include #include #include #include #include #define THREAD_ALLOC_WATCH 0 // check allocations on specific threads #if THREAD_ALLOC_WATCH mach_port_t watchThreadID = 0; #endif #endif AssertionHandler gAssertionHandler = nullptr; AssertionHandler gPreAssertionHook = nullptr; DebugPrintLogger gDebugPrintLogger = nullptr; //-------------------------------------------------------------------------- static const int kDebugPrintfBufferSize = 10000; static bool neverDebugger = false; // so I can switch it off in the debugger... static std::once_flag neverDebuggerEnvCheckFlag {}; //-------------------------------------------------------------------------- static void initNeverDebugger () { std::call_once (neverDebuggerEnvCheckFlag, [] () { // add this environment variable to not stop in the debugger on ASSERT if (std::getenv ("SMTG_DEBUG_IGNORE_ASSERT")) { neverDebugger = true; } }); } //-------------------------------------------------------------------------- static void printDebugString (const char* string) { if (!string) return; if (gDebugPrintLogger) { gDebugPrintLogger (string); } else { #if SMTG_OS_MACOS || defined(__MINGW32__) fprintf (stderr, "%s", string); #elif SMTG_OS_WINDOWS OutputDebugStringA (string); #endif } } //-------------------------------------------------------------------------- // printf style debugging output //-------------------------------------------------------------------------- void FDebugPrint (const char* format, ...) { char string[kDebugPrintfBufferSize]; va_list marker; va_start (marker, format); vsnprintf (string, kDebugPrintfBufferSize, format, marker); printDebugString (string); } //-------------------------------------------------------------------------- // printf style debugging output //-------------------------------------------------------------------------- void FDebugBreak (const char* format, ...) { char string[kDebugPrintfBufferSize]; va_list marker; va_start (marker, format); vsnprintf (string, kDebugPrintfBufferSize, format, marker); printDebugString (string); // The Pre-assertion hook is always called, even if we're not running in the debugger, // so that we can log asserts without displaying them if (gPreAssertionHook) { gPreAssertionHook (string); } initNeverDebugger (); if (neverDebugger) return; if (AmIBeingDebugged ()) { // do not crash if no debugger present // If there is an assertion handler defined then let this override the UI // and tell us whether we want to break into the debugger bool breakIntoDebugger = true; if (gAssertionHandler && gAssertionHandler (string) == false) { breakIntoDebugger = false; } if (breakIntoDebugger) { #if SMTG_OS_WINDOWS && _MSC_VER __debugbreak (); // intrinsic version of DebugBreak() #elif SMTG_OS_MACOS && __arm64__ raise (SIGSTOP); #elif __ppc64__ || __ppc__ || __arm__ kill (getpid (), SIGINT); #elif __i386__ || __x86_64__ { __asm__ volatile ("int3"); } #endif } } } //-------------------------------------------------------------------------- void FPrintLastError (const char* file, int line) { #if SMTG_OS_WINDOWS LPVOID lpMessageBuffer = nullptr; FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMessageBuffer, 0, nullptr); FDebugPrint ("%s(%d) : %s\n", file, line, lpMessageBuffer); LocalFree (lpMessageBuffer); #endif #if SMTG_OS_MACOS #if !__MACH__ extern int errno; #endif FDebugPrint ("%s(%d) : Errno %d\n", file, line, errno); #endif } #if SMTG_OS_MACOS //------------------------------------------------------------------------ void* operator new (size_t size, int, const char* file, int line) { #if THREAD_ALLOC_WATCH mach_port_t threadID = mach_thread_self (); if (watchThreadID == threadID) { FDebugPrint ("Watched Thread Allocation : %s (Line:%d)\n", file ? file : "Unknown", line); } #endif try { return ::operator new (size); } catch (std::bad_alloc exception) { FDebugPrint ("bad_alloc exception : %s (Line:%d)", file ? file : "Unknown", line); } return (void*)-1; } //------------------------------------------------------------------------ void* operator new[] (size_t size, int, const char* file, int line) { #if THREAD_ALLOC_WATCH mach_port_t threadID = mach_thread_self (); if (watchThreadID == threadID) { FDebugPrint ("Watched Thread Allocation : %s (Line:%d)\n", file ? file : "Unknown", line); } #endif try { return ::operator new[] (size); } catch (std::bad_alloc exception) { FDebugPrint ("bad_alloc exception : %s (Line:%d)", file ? file : "Unknown", line); } return (void*)-1; } //------------------------------------------------------------------------ void operator delete (void* p, int, const char* file, int line) { (void)file; (void)line; ::operator delete (p); } //------------------------------------------------------------------------ void operator delete[] (void* p, int, const char* file, int line) { (void)file; (void)line; ::operator delete[] (p); } #endif // SMTG_OS_MACOS #endif // DEVELOPMENT static bool smtg_unit_testing_active = false; // ugly hack to unit testing ... //------------------------------------------------------------------------ bool isSmtgUnitTesting () { return smtg_unit_testing_active; } //------------------------------------------------------------------------ void setSmtgUnitTesting () { smtg_unit_testing_active = true; } qtractor-1.5.11/src/vst3/base/source/PaxHeaders/hexbinary.h0000644000000000000000000000013215124701711020555 xustar0030 mtime=1767080905.186211266 30 atime=1767080905.186211266 30 ctime=1767080905.186211266 qtractor-1.5.11/src/vst3/base/source/hexbinary.h0000644000175000001440000001050215124701711020543 0ustar00rncbcusers//------------------------------------------------------------------------------------- // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/hexbinary.h // Created by : Steinberg, 1/2012 // Description : HexBinary encoding and decoding // //----------------------------------------------------------------------------- // LICENSE // (c) 2024, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- // This Software Development Kit may not be distributed in parts or its entirety // without prior written agreement by Steinberg Media Technologies GmbH. // This SDK must not be used to re-engineer or manipulate any technology used // in any Steinberg or Third-party application or software module, // unless permitted by law. // Neither the name of the Steinberg Media Technologies nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SDK IS PROVIDED BY STEINBERG MEDIA TECHNOLOGIES GMBH "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. // IN NO EVENT SHALL STEINBERG MEDIA TECHNOLOGIES GMBH BE LIABLE FOR ANY DIRECT, // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- /** @file base/source/hexbinary.h HexBinary encoding and decoding. */ //---------------------------------------------------------------------------------- #pragma once #include "base/source/fbuffer.h" namespace Steinberg { //------------------------------------------------------------------------------ namespace HexBinary { //------------------------------------------------------------------------------ /** convert the HexBinary input buffer to binary. Note that it is appended to the result buffer. */ //------------------------------------------------------------------------------ inline bool decode (const void* input, int32 inputSize, Buffer& result) { if ((inputSize & 1) == 1) return false; result.grow (result.getSize () + inputSize / 2); const char8* ptr = (const char8*)input; uint8 current = 0; for (int32 i = 0; i < inputSize; i++, ptr++) { current *= 16; if (*ptr >= 48 && *ptr <= 57) // 0, 1, 2, .., 9 { current += *ptr - 48; } else if (*ptr >= 65 && *ptr <= 70) // A, B, .., F { current += *ptr - 55; } else if (*ptr >= 97 && *ptr <= 102) // a, b, .., f { current += *ptr - 87; } else { // malformed return false; } if (i % 2) { if (result.put (current) == false) return false; current = 0; } } return true; } //------------------------------------------------------------------------------ /** convert the binary input buffer to HexBinary. Note that it is appended to the result buffer. */ //------------------------------------------------------------------------------ inline bool encode (const void* input, int32 inputSize, Buffer& result) { result.grow (result.getSize () + inputSize * 2); const char8* ptr = (const char8*)input; for (int32 i = 0; i < inputSize; i++, ptr++) { char8 high = (*ptr & 0xF0) >> 4; char8 low = (*ptr & 0x0F); if (high > 9) { if (result.put ((char8)(high + 55)) == false) return false; } else { if (result.put ((char8)(high + 48)) == false) return false; } if (low > 9) { if (result.put ((char8)(low + 55)) == false) return false; } else { if (result.put ((char8)(low + 48)) == false) return false; } } return true; } //------------------------------------------------------------------------ } // namespace HexBinary } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fstdmethods.h0000644000000000000000000000012715124701711021114 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fstdmethods.h0000644000175000001440000001731115124701711021103 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fstdmethods.h // Created by : Steinberg, 2007 // Description : Convenient macros to create setter and getter methods. // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- //------------------------------------------------------------------------ /** @file base/source/fstdmethods.h Convenient macros to create setter and getter methods. */ //------------------------------------------------------------------------ #pragma once //---------------------------------------------------------------------------------- /** @name Methods for flags. Macros to create setter and getter methods for flags. Usage example with DEFINE_STATE: \code class MyClass { public: MyClass () : flags (0) {} DEFINE_FLAG (flags, isFlagged, 1<<0) DEFINE_FLAG (flags, isMine, 1<<1) private: uint32 flags; }; void someFunction () { MyClass c; if (c.isFlagged ()) // check the flag c.isFlagged (false); // set the flag } \endcode */ //---------------------------------------------------------------------------------- ///@{ /** Create Methods with @c get and @c set prefix. */ #define DEFINE_STATE(flagVar,methodName,value)\ void set##methodName (bool state) { if (state) flagVar |= (value); else flagVar &= ~(value); }\ bool get##methodName ()const { return (flagVar & (value)) != 0; } /** Create Methods with @c get prefix. There is only a 'get' method. */ #define DEFINE_GETSTATE(flagVar,methodName,value)\ bool get##methodName ()const { return (flagVar & (value)) != 0; } /** Create Methods. Same name for the getter and setter. */ #define DEFINE_FLAG(flagVar,methodName,value)\ void methodName (bool state) { if (state) flagVar |= (value); else flagVar &= ~(value); }\ bool methodName ()const { return (flagVar & (value)) != 0; } /** Create Methods. There is only a 'get' method. */ #define DEFINE_GETFLAG(flagVar,methodName,value)\ bool methodName ()const { return (flagVar & (value)) != 0; } /** Create @c static Methods. Same name for the getter and setter. */ #define DEFINE_FLAG_STATIC(flagVar,methodName,value)\ static void methodName (bool __state) { if (__state) flagVar |= (value); else flagVar &= ~(value); }\ static bool methodName () { return (flagVar & (value)) != 0; } ///@} //---------------------------------------------------------------------------------- /** @name Methods for data members. Macros to create setter and getter methods for class members. Examples: \code class MyClass { public: DATA_MEMBER (double, distance, Distance) STRING_MEMBER (Steinberg::String, name, Name) SHARED_MEMBER (FUnknown, userData, UserData) CLASS_MEMBER (Steinberg::Buffer, bufferData, BufferData) POINTER_MEMBER (Steinberg::FObject, refOnly, RefOnly) }; \endcode */ //-------------------------------------------------------------------------------------- ///@{ /** Build-in member (pass by value). */ #define DATA_MEMBER(type,varName,methodName)\ public:void set##methodName (type v) { varName = v;}\ type get##methodName ()const { return varName; }\ protected: type varName; public: //** Object member (pass by reference). */ #define CLASS_MEMBER(type,varName,methodName)\ public:void set##methodName (const type& v){ varName = v;}\ const type& get##methodName () const { return varName; }\ protected: type varName; public: //** Simple pointer. */ #define POINTER_MEMBER(type,varName,methodName)\ public:void set##methodName (type* ptr){ varName = ptr;}\ type* get##methodName ()const { return varName; }\ private: type* varName; public: //** Shared member - FUnknown / FObject / etc */ #define SHARED_MEMBER(type,varName,methodName)\ public:void set##methodName (type* v){ varName = v;}\ type* get##methodName ()const { return varName; }\ private: IPtr varName; public: //** Owned member - FUnknown / FObject / CmObject etc */ #define OWNED_MEMBER(type,varName,methodName)\ public:void set##methodName (type* v){ varName = v;}\ type* get##methodName ()const { return varName; }\ private: OPtr varName; public: //** tchar* / String class member - set by const tchar*, return by reference */ #define STRING_MEMBER(type,varName,methodName)\ public:void set##methodName (const tchar* v){ varName = v;}\ const type& get##methodName () const { return varName; }\ protected: type varName; public: //** char8* / String class member - set by const char8*, return by reference */ #define STRING8_MEMBER(type,varName,methodName)\ public:void set##methodName (const char8* v){ varName = v;}\ const type& get##methodName () const { return varName; }\ protected: type varName; public: //** Standard String Member Steinberg::String */ #define STRING_MEMBER_STD(varName,methodName) STRING_MEMBER(Steinberg::String,varName,methodName) #define STRING8_MEMBER_STD(varName,methodName) STRING8_MEMBER(Steinberg::String,varName,methodName) ///@} // obsolete names #define DEFINE_VARIABLE(type,varName,methodName) DATA_MEMBER(type,varName,methodName) #define DEFINE_POINTER(type,varName,methodName) POINTER_MEMBER(type,varName,methodName) #define DEFINE_MEMBER(type,varName,methodName) CLASS_MEMBER(type,varName,methodName) //------------------------------------------------------------------------ // for implementing comparison operators using a class member or a compare method: //------------------------------------------------------------------------ #define COMPARE_BY_MEMBER_METHODS(className,memberName) \ bool operator == (const className& other) const {return (memberName == other.memberName);} \ bool operator != (const className& other) const {return (memberName != other.memberName);} \ bool operator < (const className& other) const {return (memberName < other.memberName);} \ bool operator > (const className& other) const {return (memberName > other.memberName);} \ bool operator <= (const className& other) const {return (memberName <= other.memberName);} \ bool operator >= (const className& other) const {return (memberName >= other.memberName);} #define COMPARE_BY_MEMORY_METHODS(className) \ bool operator == (const className& other) const {return memcmp (this, &other, sizeof (className)) == 0;} \ bool operator != (const className& other) const {return memcmp (this, &other, sizeof (className)) != 0;} \ bool operator < (const className& other) const {return memcmp (this, &other, sizeof (className)) < 0;} \ bool operator > (const className& other) const {return memcmp (this, &other, sizeof (className)) > 0;} \ bool operator <= (const className& other) const {return memcmp (this, &other, sizeof (className)) <= 0;} \ bool operator >= (const className& other) const {return memcmp (this, &other, sizeof (className)) >= 0;} #define COMPARE_BY_COMPARE_METHOD(className,methodName) \ bool operator == (const className& other) const {return methodName (other) == 0;} \ bool operator != (const className& other) const {return methodName (other) != 0;} \ bool operator < (const className& other) const {return methodName (other) < 0;} \ bool operator > (const className& other) const {return methodName (other) > 0;} \ bool operator <= (const className& other) const {return methodName (other) <= 0; } \ bool operator >= (const className& other) const {return methodName (other) >= 0; } qtractor-1.5.11/src/vst3/base/source/PaxHeaders/timer.h0000644000000000000000000000013215124701711017704 xustar0030 mtime=1767080905.186211266 30 atime=1767080905.186211266 30 ctime=1767080905.186211266 qtractor-1.5.11/src/vst3/base/source/timer.h0000644000175000001440000001053615124701711017701 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/timer.h // Created by : Steinberg, 05/2006 // Description : Timer class for receiving tiggers at regular intervals // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "fobject.h" #include #if SMTG_CPP17 #include #endif namespace Steinberg { class Timer; //------------------------------------------------------------------------ namespace SystemTime { uint64 getTicks64 (); inline uint64 getTicksDuration (uint64 old, uint64 now) { if (old > now) return (std::numeric_limits::max) () - old + now; return now - old; } int32 getTicks (); ///< deprecated, use getTicks64 () //------------------------------------------------------------------------ } // namespace SystemTime //------------------------------------------------------------------------ /** @class ITimerCallback Implement this callback interface to receive triggers from a timer. Note: This interface is intended as a mix-in class and therefore does not provide ref-counting. @see Timer */ class ITimerCallback { public: virtual ~ITimerCallback () {} /** This method is called at the end of each interval. \param timer The timer which calls. */ virtual void onTimer (Timer* timer) = 0; }; template ITimerCallback* newTimerCallback (const Call& call) { struct Callback : public ITimerCallback { Callback (const Call& call) : call (call) {} void onTimer (Timer* timer) SMTG_OVERRIDE { call (timer); } Call call; }; return NEW Callback (call); } #if SMTG_CPP17 //------------------------------------------------------------------------ /** @class TimerCallback * * a timer callback object using a funtion for the timer call */ struct TimerCallback final : ITimerCallback { using CallbackFunc = std::function; TimerCallback (CallbackFunc&& f) : f (std::move (f)) {} TimerCallback (const CallbackFunc& f) : f (f) {} void onTimer (Timer* timer) override { f (timer); } private: CallbackFunc f; }; #endif // SMTG_CPP17 // ----------------------------------------------------------------- /** @class Timer Timer is a class that allows you to receive triggers at regular intervals. Note: The timer class is an abstract base class with (hidden) platform specific subclasses. Usage: @code class TimerReceiver : public FObject, public ITimerCallback { ... virtual void onTimer (Timer* timer) { // do stuff } ... }; TimerReceiver* receiver = new TimerReceiver (); Timer* myTimer = Timer::create (receiver, 100); // interval: every 100ms ... ... if (myTimer) myTimer->release (); if (receiver) receiver->release (); @endcode @see ITimerCallback */ class Timer : public FObject { public: /** Create a timer with a given interval \param callback The receiver of the timer calls. \param intervalMilliseconds The timer interval in milliseconds. \return The created timer if any, callers owns the timer. The timer starts immediately. */ static Timer* create (ITimerCallback* callback, uint32 intervalMilliseconds); virtual void stop () = 0; ///< Stop the timer. }; // ----------------------------------------------------------------- /** @class DisableDispatchingTimers Disables dispatching of timers for the live time of this object */ class DisableDispatchingTimers { public: DisableDispatchingTimers (); ~DisableDispatchingTimers (); private: bool oldState; }; #if SMTG_OS_LINUX using CreateTimerFunc = Timer* (*)(ITimerCallback* callback, uint32 intervalMilliseconds); void InjectCreateTimerFunction (CreateTimerFunc f); #endif //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fdebug.h0000644000000000000000000000012715124701711020024 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fdebug.h0000644000175000001440000001710315124701711020012 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fdebug.h // Created by : Steinberg, 1995 // Description : There are 2 levels of debugging messages: // DEVELOPMENT During development // RELEASE Program is shipping. // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /** @file base/source/fdebug.h Debugging tools. There are 2 levels of debugging messages: - DEVELOPMENT - During development - RELEASE - Program is shipping. */ //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ftypes.h" #include #if SMTG_OS_MACOS #include #endif /** Returns true if a debugger is attached. */ bool AmIBeingDebugged (); //----------------------------------------------------------------------------- // development / release //----------------------------------------------------------------------------- #if !defined (DEVELOPMENT) && !defined (RELEASE) #ifdef _DEBUG #define DEVELOPMENT 1 #elif defined (NDEBUG) #define RELEASE 1 #else #error DEVELOPMENT, RELEASE, _DEBUG, or NDEBUG must be defined! #endif #endif //----------------------------------------------------------------------------- #if SMTG_OS_WINDOWS /** Disable compiler warning: * C4291: "No matching operator delete found; memory will not be freed if initialization throws an * exception. A placement new is used for which there is no placement delete." */ #if DEVELOPMENT && defined(_MSC_VER) #pragma warning(disable : 4291) #pragma warning(disable : 4985) #endif #endif // SMTG_OS_WINDOWS #if DEVELOPMENT //----------------------------------------------------------------------------- /** If "f" is not true and a debugger is present, send an error string to the debugger for display and cause a breakpoint exception to occur in the current process. SMTG_ASSERT is removed completely in RELEASE configuration. So do not pass methods calls to this macro that are expected to exist in the RELEASE build (for method calls that need to be present in a RELEASE build, use the VERIFY macros instead)*/ #define SMTG_ASSERT(f) \ if (!(f)) \ FDebugBreak ("%s(%d) : Assert failed: %s\n", __FILE__, __LINE__, #f); #define SMTG_ASSERT_MSG(f, msg) \ if (!(f)) \ FDebugBreak ("%s(%d) : Assert failed: [%s] [%s]\n", __FILE__, __LINE__, #f, msg); /** Send "comment" string to the debugger for display. */ #define SMTG_WARNING(comment) FDebugPrint ("%s(%d) : %s\n", __FILE__, __LINE__, comment); /** Send the last error string to the debugger for display. */ #define SMTG_PRINTSYSERROR FPrintLastError (__FILE__, __LINE__); /** If a debugger is present, send string "s" to the debugger for display and cause a breakpoint exception to occur in the current process. */ #define SMTG_DEBUGSTR(s) FDebugBreak (s); /** Use VERIFY for calling methods "f" having a bool result (expecting them to return 'true') The call of "f" is not removed in RELEASE builds, only the result verification. eg: SMTG_VERIFY (isValid ()) */ #define SMTG_VERIFY(f) SMTG_ASSERT (f) /** Use VERIFY_IS for calling methods "f" and expect a certain result "r". The call of "f" is not removed in RELEASE builds, only the result verification. eg: SMTG_VERIFY_IS (callMethod (), kResultOK) */ #define SMTG_VERIFY_IS(f, r) \ if ((f) != (r)) \ FDebugBreak ("%s(%d) : Assert failed: %s\n", __FILE__, __LINE__, #f); /** Use VERIFY_NOT for calling methods "f" and expect the result to be anything else but "r". The call of "f" is not removed in RELEASE builds, only the result verification. eg: SMTG_VERIFY_NOT (callMethod (), kResultError) */ #define SMTG_VERIFY_NOT(f, r) \ if ((f) == (r)) \ FDebugBreak ("%s(%d) : Assert failed: %s\n", __FILE__, __LINE__, #f); /** @name Shortcut macros for sending strings to the debugger for display. First parameter is always the format string (printf like). */ ///@{ #define SMTG_DBPRT0(a) FDebugPrint (a); #define SMTG_DBPRT1(a, b) FDebugPrint (a, b); #define SMTG_DBPRT2(a, b, c) FDebugPrint (a, b, c); #define SMTG_DBPRT3(a, b, c, d) FDebugPrint (a, b, c, d); #define SMTG_DBPRT4(a, b, c, d, e) FDebugPrint (a, b, c, d, e); #define SMTG_DBPRT5(a, b, c, d, e, f) FDebugPrint (a, b, c, d, e, f); ///@} /** @name Helper functions for the above defined macros. You shouldn't use them directly (if you do so, don't forget "#if DEVELOPMENT")! It is recommended to use the macros instead. */ ///@{ void FDebugPrint (const char* format, ...); void FDebugBreak (const char* format, ...); void FPrintLastError (const char* file, int line); ///@} /** @name Provide a custom assertion handler and debug print handler, eg so that we can provide an assert with a custom dialog, or redirect the debug output to a file or stream. */ ///@{ using AssertionHandler = bool (*) (const char* message); extern AssertionHandler gAssertionHandler; extern AssertionHandler gPreAssertionHook; using DebugPrintLogger = void (*) (const char* message); extern DebugPrintLogger gDebugPrintLogger; ///@} /** Definition of memory allocation macros: Use "NEW" to allocate storage for individual objects. Use "NEWVEC" to allocate storage for an array of objects. */ #if SMTG_OS_MACOS void* operator new (size_t, int, const char*, int); void* operator new[] (size_t, int, const char*, int); void operator delete (void* p, int, const char* file, int line); void operator delete[] (void* p, int, const char* file, int line); #ifndef NEW #define NEW new (1, __FILE__, __LINE__) #define NEWVEC new (1, __FILE__, __LINE__) #endif #define DEBUG_NEW DEBUG_NEW_LEAKS #elif SMTG_OS_WINDOWS && defined(_MSC_VER) #ifndef NEW void* operator new (size_t, int, const char*, int); #define NEW new (1, __FILE__, __LINE__) #define NEWVEC new (1, __FILE__, __LINE__) #endif #else #ifndef NEW #define NEW new #define NEWVEC new #endif #endif #else /** if DEVELOPMENT is not set, these macros will do nothing. */ #define SMTG_ASSERT(f) #define SMTG_ASSERT_MSG(f, msg) #define SMTG_WARNING(s) #define SMTG_PRINTSYSERROR #define SMTG_DEBUGSTR(s) #define SMTG_VERIFY(f) f; #define SMTG_VERIFY_IS(f, r) f; #define SMTG_VERIFY_NOT(f, r) f; #define SMTG_DBPRT0(a) #define SMTG_DBPRT1(a, b) #define SMTG_DBPRT2(a, b, c) #define SMTG_DBPRT3(a, b, c, d) #define SMTG_DBPRT4(a, b, c, d, e) #define SMTG_DBPRT5(a, b, c, d, e, f) #ifndef NEW #define NEW new #define NEWVEC new #endif #endif // replace #if SMTG_CPPUNIT_TESTING bool isSmtgUnitTesting (); void setSmtgUnitTesting (); #if !SMTG_RENAME_ASSERT #if SMTG_OS_WINDOWS #undef ASSERT #endif #define ASSERT SMTG_ASSERT #define WARNING SMTG_WARNING #define DEBUGSTR SMTG_DEBUGSTR #define VERIFY SMTG_VERIFY #define VERIFY_IS SMTG_VERIFY_IS #define VERIFY_NOT SMTG_VERIFY_NOT #define PRINTSYSERROR SMTG_PRINTSYSERROR #define DBPRT0 SMTG_DBPRT0 #define DBPRT1 SMTG_DBPRT1 #define DBPRT2 SMTG_DBPRT2 #define DBPRT3 SMTG_DBPRT3 #define DBPRT4 SMTG_DBPRT4 #define DBPRT5 SMTG_DBPRT5 #endif qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fcommandline.h0000644000000000000000000000012715124701711021224 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fcommandline.h0000644000175000001440000003147615124701711021223 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fcommandline.h // Created by : Steinberg, 2007 // Description : Very simple command-line parser. @see Steinberg::CommandLine // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include #include #include #include #include #include #include namespace Steinberg { //------------------------------------------------------------------------ /** Very simple command-line parser. Parses the command-line into a CommandLine::VariablesMap.\n The command-line parser uses CommandLine::Descriptions to define the available options. @b Example: \code #include "base/source/fcommandline.h" #include int main (int argc, char* argv[]) { using namespace std; CommandLine::Descriptions desc; CommandLine::VariablesMap valueMap; desc.addOptions ("myTool") ("help", "produce help message") ("opt1", string(), "option 1") ("opt2", string(), "option 2") ; CommandLine::parse (argc, argv, desc, valueMap); if (valueMap.hasError () || valueMap.count ("help")) { cout << desc << "\n"; return 1; } if (valueMap.count ("opt1")) { cout << "Value of option 1 " << valueMap["opt1"] << "\n"; } if (valueMap.count ("opt2")) { cout << "Value of option 2 " << valueMap["opt2"] << "\n"; } return 0; } \endcode @note This is a "header only" implementation.\n If you need the declarations in more than one cpp file, you have to define @c SMTG_NO_IMPLEMENTATION in all but one file. */ //------------------------------------------------------------------------ namespace CommandLine { //------------------------------------------------------------------------ /** Command-line parsing result. This is the result of the parser.\n - Use hasError() to check for errors.\n - To test if a option was specified on the command-line use: count()\n - To retrieve the value of an options, use operator [](const VariablesMapContainer::key_type k)\n */ //------------------------------------------------------------------------ class VariablesMap { bool mParaError; using VariablesMapContainer = std::map; VariablesMapContainer mVariablesMapContainer; public: VariablesMap () : mParaError (false) {} ///< Constructor. Creates a empty VariablesMap. bool hasError () const { return mParaError; } ///< Returns @c true when an error has occurred. void setError () { mParaError = true; } ///< Sets the error state to @c true. /** Retrieve the value of option @c k.*/ std::string& operator[] (const VariablesMapContainer::key_type k); /** Retrieve the value of option @c k. */ const std::string& operator[] (const VariablesMapContainer::key_type k) const; /** Returns @c != @c 0 if command-line contains option @c k. */ VariablesMapContainer::size_type count (const VariablesMapContainer::key_type k) const; }; //! type of the list of elements on the command line that are not handled by options parsing using FilesVector = std::vector; //------------------------------------------------------------------------ /** The description of one single command-line option. Normally you rarely use a Description directly.\n In most cases you will use the Descriptions::addOptions (const std::string&) method to create and add descriptions. */ //------------------------------------------------------------------------ class Description : public std::string { public: /** Construct a Description */ Description (const std::string& name, const std::string& help, const std::string& valueType); std::string mHelp; ///< The help string for this option. std::string mType; ///< The type of this option (kBool, kString). static const std::string kBool; static const std::string kString; }; //------------------------------------------------------------------------ /** List of command-line option descriptions. Use addOptions (const std::string&) to add Descriptions. */ //------------------------------------------------------------------------ class Descriptions { using DescriptionsList = std::deque; DescriptionsList mDescriptions; std::string mCaption; public: /** Sets the command-line tool caption and starts adding Descriptions. */ Descriptions& addOptions (const std::string& caption = "", std::initializer_list&& options = {}); /** Parse the command-line. */ bool parse (int ac, char* av[], VariablesMap& result, FilesVector* files = nullptr) const; /** Print a brief description for the command-line tool into the stream @c os. */ void print (std::ostream& os) const; /** Add a new switch. Only */ Descriptions& operator () (const std::string& name, const std::string& help); /** Add a new option of type @c inType. Currently only std::string is supported. */ template Descriptions& operator () (const std::string& name, const Type& inType, std::string help); }; //------------------------------------------------------------------------ // If you need the declarations in more than one cpp file you have to define // SMTG_NO_IMPLEMENTATION in all but one file. //------------------------------------------------------------------------ #ifndef SMTG_NO_IMPLEMENTATION //------------------------------------------------------------------------ /*! If command-line contains option @c k more than once, only the last value will survive. */ //------------------------------------------------------------------------ std::string& VariablesMap::operator[] (const VariablesMapContainer::key_type k) { return mVariablesMapContainer[k]; } //------------------------------------------------------------------------ /*! If command-line contains option @c k more than once, only the last value will survive. */ //------------------------------------------------------------------------ const std::string& VariablesMap::operator[] (const VariablesMapContainer::key_type k) const { return (*const_cast (this))[k]; } //------------------------------------------------------------------------ VariablesMap::VariablesMapContainer::size_type VariablesMap::count ( const VariablesMapContainer::key_type k) const { return mVariablesMapContainer.count (k); } //------------------------------------------------------------------------ /** Add a new option with a string as parameter. */ //------------------------------------------------------------------------ template <> Descriptions& Descriptions::operator () (const std::string& name, const std::string& inType, std::string help) { mDescriptions.emplace_back (name, help, inType); return *this; } /** Parse the command - line. */ bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result, FilesVector* files = nullptr); /** Make Descriptions stream able. */ std::ostream& operator<< (std::ostream& os, const Descriptions& desc); const std::string Description::kBool = "bool"; const std::string Description::kString = "string"; //------------------------------------------------------------------------ /*! In most cases you will use the Descriptions::addOptions (const std::string&) method to create and add descriptions. @param[in] name of the option. @param[in] help a help description for this option. @param[out] valueType Description::kBool or Description::kString. */ Description::Description (const std::string& name, const std::string& help, const std::string& valueType) : std::string (name), mHelp (help), mType (valueType) { } //------------------------------------------------------------------------ /*! Returning a reference to *this, enables chaining of calls to operator()(const std::string&, const std::string&). @param[in] name of the added option. @param[in] help a help description for this option. @return a reference to *this. */ Descriptions& Descriptions::operator () (const std::string& name, const std::string& help) { mDescriptions.emplace_back (name, help, Description::kBool); return *this; } //------------------------------------------------------------------------ /*! Usage example: @code CommandLine::Descriptions desc; desc.addOptions ("myTool") // Set caption to "myTool" ("help", "produce help message") // add switch -help ("opt1", string(), "option 1") // add string option -opt1 ("opt2", string(), "option 2") // add string option -opt2 ; @endcode @note The operator() is used for every additional option. Or with initializer list : @code CommandLine::Descriptions desc; desc.addOptions ("myTool", // Set caption to "myTool" {{"help", "produce help message", Description::kBool}, // add switch -help {"opt1", "option 1", Description::kString}, // add string option -opt1 {"opt2", "option 2", Description::kString}} // add string option -opt2 ); @endcode @param[in] caption the caption of the command-line tool. @param[in] options initializer list with options @return a reverense to *this. */ Descriptions& Descriptions::addOptions (const std::string& caption, std::initializer_list&& options) { mCaption = caption; std::move (options.begin (), options.end (), std::back_inserter (mDescriptions)); return *this; } //------------------------------------------------------------------------ /*! @param[in] ac count of command-line parameters @param[in] av command-line as array of strings @param[out] result the parsing result @param[out] files optional list of elements on the command line that are not handled by options parsing */ bool Descriptions::parse (int ac, char* av[], VariablesMap& result, FilesVector* files) const { using namespace std; for (int i = 1; i < ac; i++) { string current = av[i]; if (current[0] == '-') { int pos = current[1] == '-' ? 2 : 1; current = current.substr (pos, string::npos); DescriptionsList::const_iterator found = find (mDescriptions.begin (), mDescriptions.end (), current); if (found != mDescriptions.end ()) { result[*found] = "true"; if (found->mType != Description::kBool) { if (((i + 1) < ac) && *av[i + 1] != '-') { result[*found] = av[++i]; } else { result[*found] = "error!"; result.setError (); return false; } } } else { result.setError (); return false; } } else if (files) files->push_back (av[i]); } return true; } //------------------------------------------------------------------------ /*! The description includes the help strings for all options. */ //------------------------------------------------------------------------ void Descriptions::print (std::ostream& os) const { if (!mCaption.empty ()) os << mCaption << ":\n"; size_t maxLength = 0u; std::for_each (mDescriptions.begin (), mDescriptions.end (), [&] (const Description& d) { maxLength = std::max (maxLength, d.size ()); }); for (const Description& opt : mDescriptions) { os << "-" << opt; for (auto s = opt.size (); s < maxLength; ++s) os << " "; os << " | " << opt.mHelp << "\n"; } } //------------------------------------------------------------------------ std::ostream& operator<< (std::ostream& os, const Descriptions& desc) { desc.print (os); return os; } //------------------------------------------------------------------------ /*! @param[in] ac count of command-line parameters @param[in] av command-line as array of strings @param[in] desc Descriptions including all allowed options @param[out] result the parsing result @param[out] files optional list of elements on the command line that are not handled by options parsing */ bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result, FilesVector* files) { return desc.parse (ac, av, result, files); } #endif //------------------------------------------------------------------------ } // namespace CommandLine } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/baseiids.cpp0000644000000000000000000000013215124701711020702 xustar0030 mtime=1767080905.184211274 30 atime=1767080905.184211274 30 ctime=1767080905.184211274 qtractor-1.5.11/src/vst3/base/source/baseiids.cpp0000644000175000001440000000223315124701711020672 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/baseidds.cpp // Created by : Steinberg, 01/2008 // Description : Basic Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/base/istringresult.h" #include "pluginterfaces/base/ipersistent.h" namespace Steinberg { DEF_CLASS_IID (IString) DEF_CLASS_IID (IStringResult) DEF_CLASS_IID (IPersistent) DEF_CLASS_IID (IAttributes) DEF_CLASS_IID (IAttributes2) //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fstreamer.h0000644000000000000000000000012715124701711020560 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fstreamer.h0000644000175000001440000001674115124701711020555 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fstreamer.h // Created by : Steinberg, 12/2005 // Description : Extract of typed stream i/o methods from FStream // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" namespace Steinberg { //------------------------------------------------------------------------ enum FSeekMode { kSeekSet, kSeekCurrent, kSeekEnd }; //------------------------------------------------------------------------ // FStreamer //------------------------------------------------------------------------ /** Byteorder-aware base class for typed stream i/o. */ //------------------------------------------------------------------------ class FStreamer { public: //------------------------------------------------------------------------ FStreamer (int16 byteOrder = BYTEORDER); virtual ~FStreamer () {} /** @name Implementing class must override. */ ///@{ virtual TSize readRaw (void*, TSize) = 0; ///< Read one buffer of size. virtual TSize writeRaw (const void*, TSize) = 0; ///< Write one buffer of size. virtual int64 seek (int64, FSeekMode) = 0; ///< Set file position for stream. virtual int64 tell () = 0; ///< Return current file position. ///@} /** @name Streams are byteOrder aware. */ ///@{ inline void setByteOrder (int32 e) { byteOrder = (int16)e; } inline int32 getByteOrder () const { return byteOrder; } ///@} /** @name read and write int8 and char. */ ///@{ bool writeChar8 (char8); bool readChar8 (char8&); bool writeUChar8 (unsigned char); bool readUChar8 (unsigned char&); bool writeChar16 (char16 c); bool readChar16 (char16& c); bool writeInt8 (int8 c); bool readInt8 (int8& c); bool writeInt8u (uint8 c); bool readInt8u (uint8& c); ///@} /** @name read and write int16. */ ///@{ bool writeInt16 (int16); bool readInt16 (int16&); bool writeInt16Array (const int16* array, int32 count); bool readInt16Array (int16* array, int32 count); bool writeInt16u (uint16); bool readInt16u (uint16&); bool writeInt16uArray (const uint16* array, int32 count); bool readInt16uArray (uint16* array, int32 count); ///@} /** @name read and write int32. */ ///@{ bool writeInt32 (int32); bool readInt32 (int32&); bool writeInt32Array (const int32* array, int32 count); bool readInt32Array (int32* array, int32 count); bool writeInt32u (uint32); bool readInt32u (uint32&); bool writeInt32uArray (const uint32* array, int32 count); bool readInt32uArray (uint32* array, int32 count); ///@} /** @name read and write int64. */ ///@{ bool writeInt64 (int64); bool readInt64 (int64&); bool writeInt64Array (const int64* array, int32 count); bool readInt64Array (int64* array, int32 count); bool writeInt64u (uint64); bool readInt64u (uint64&); bool writeInt64uArray (const uint64* array, int32 count); bool readInt64uArray (uint64* array, int32 count); ///@} /** @name read and write float and float array. */ ///@{ bool writeFloat (float); bool readFloat (float&); bool writeFloatArray (const float* array, int32 count); bool readFloatArray (float* array, int32 count); ///@} /** @name read and write double and double array. */ ///@{ bool writeDouble (double); bool readDouble (double&); bool writeDoubleArray (const double* array, int32 count); bool readDoubleArray (double* array, int32 count); ///@} /** @name read and write Boolean. */ ///@{ bool writeBool (bool); ///< Write one boolean bool readBool (bool&); ///< Read one bool. ///@} /** @name read and write Strings. */ ///@{ TSize writeString8 (const char8* ptr, bool terminate = false); ///< a direct output function writing only one string (ascii 8bit) TSize readString8 (char8* ptr, TSize size); ///< a direct input function reading only one string (ascii) (ended by a \n or \0 or eof) bool writeStr8 (const char8* ptr); ///< write a string length (strlen) and string itself char8* readStr8 (); ///< read a string length and string text (The return string must be deleted when use is finished) static int32 getStr8Size (const char8* ptr); ///< returns the size of a saved string bool writeStringUtf8 (const tchar* ptr); ///< always terminated, converts to utf8 if non ascii characters are in string int32 readStringUtf8 (tchar* ptr, int32 maxSize); ///< read a UTF8 string ///@} bool skip (uint32 bytes); bool pad (uint32 bytes); //------------------------------------------------------------------------ protected: int16 byteOrder; }; //------------------------------------------------------------------------ /** FStreamSizeHolder Declaration remembers size of stream chunk for backward compatibility. Example: @code externalize (a) { FStreamSizeHolder sizeHolder; sizeHolder.beginWrite (); // sets start mark, writes dummy size a << .... sizeHolder.endWrite (); // jumps to start mark, updates size, jumps back here } internalize (a) { FStreamSizeHolder sizeHolder; sizeHolder.beginRead (); // reads size, mark a >> .... sizeHolder.endRead (); // jumps forward if new version has larger size } @endcode */ //------------------------------------------------------------------------ class FStreamSizeHolder { public: FStreamSizeHolder (FStreamer &s); void beginWrite (); ///< remembers position and writes 0 int32 endWrite (); ///< writes and returns size (since the start marker) int32 beginRead (); ///< returns size void endRead (); ///< jump to end of chunk protected: FStreamer &stream; int64 sizePos; }; class IBStream; //------------------------------------------------------------------------ // IBStreamer //------------------------------------------------------------------------ /** Wrapper class for typed reading/writing from or to IBStream. Can be used framework-independent in plug-ins. */ //------------------------------------------------------------------------ class IBStreamer: public FStreamer { public: //------------------------------------------------------------------------ /** Constructor for a given IBSTream and a byteOrder. */ IBStreamer (IBStream* stream, int16 byteOrder = BYTEORDER); IBStream* getStream () { return stream; } ///< Returns the associated IBStream. // FStreamer overrides: TSize readRaw (void*, TSize) SMTG_OVERRIDE; ///< Read one buffer of size. TSize writeRaw (const void*, TSize) SMTG_OVERRIDE; ///< Write one buffer of size. int64 seek (int64, FSeekMode) SMTG_OVERRIDE; ///< Set file position for stream. int64 tell () SMTG_OVERRIDE; ///< Return current file position. //------------------------------------------------------------------------ protected: IBStream* stream; }; //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/updatehandler.cpp0000644000000000000000000000013215124701711021737 xustar0030 mtime=1767080905.186211266 30 atime=1767080905.186211266 30 ctime=1767080905.186211266 qtractor-1.5.11/src/vst3/base/source/updatehandler.cpp0000644000175000001440000004437015124701711021737 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/updatehandler.cpp // Created by : Steinberg, 2008 // Description : // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "base/source/updatehandler.h" #include "base/source/classfactoryhelpers.h" #include "base/source/fstring.h" #if SMTG_CPP11_STDLIBSUPPORT #include #else #include #endif #include #include #include #define NON_EXISTING_DEPENDENCY_CHECK 0 // not yet #define CLASS_NAME_TRACKED DEVELOPMENT using Steinberg::Base::Thread::FGuard; namespace Steinberg { DEF_CLASS_IID (IUpdateManager) namespace Update { const uint32 kHashSize = (1 << 8); // must be power of 2 (16 bytes * 256 == 4096) const uint32 kMapSize = 1024 * 10; //------------------------------------------------------------------------ inline uint32 hashPointer (void* p) { return (uint32)((uint64 (p) >> 12) & (kHashSize - 1)); } //------------------------------------------------------------------------ inline IPtr getUnknownBase (FUnknown* unknown) { FUnknown* result = nullptr; if (unknown) { if (unknown->queryInterface (FObject::iid, (void**)&result) != kResultTrue) unknown->queryInterface (FUnknown::iid, (void**)&result); } return owned (result); } #if CLASS_NAME_TRACKED //------------------------------------------------------------------------ struct Dependency { Dependency (FUnknown* o, IDependent* d) : obj (o), dep (d), objClass (nullptr), depClass (nullptr) { } inline bool operator== (const Dependency& d) const { return obj == d.obj; } inline bool operator!= (const Dependency& d) const { return obj != d.obj; } inline bool operator< (const Dependency& d) const { return obj < d.obj; } inline bool operator> (const Dependency& d) const { return obj > d.obj; } FUnknown* obj; IDependent* dep; FClassID objClass; FClassID depClass; }; #endif //------------------------------------------------------------------------ struct DeferedChange { DeferedChange (FUnknown* o, int32 m = 0) : obj (o), msg (m) {} ~DeferedChange () {} DeferedChange (const DeferedChange& r) : obj (r.obj), msg (r.msg) {} inline bool operator== (const DeferedChange& d) const { return obj == d.obj; } inline bool operator!= (const DeferedChange& d) const { return obj != d.obj; } FUnknown* obj; int32 msg; }; //------------------------------------------------------------------------ struct UpdateData { UpdateData (FUnknown* o, IDependent** d, uint32 c) : obj (o), dependents (d), count (c) { } FUnknown* obj; IDependent** dependents; uint32 count; bool operator== (const UpdateData& d) const { return d.obj == obj && d.dependents == dependents; } }; //------------------------------------------------------------------------ using DeferedChangeList = std::deque; using DeferedChangeListIterConst = DeferedChangeList::const_iterator; using DeferedChangeListIter = DeferedChangeList::iterator; using UpdateDataList = std::deque; using UpdateDataListIterConst = UpdateDataList::const_iterator; #if CLASS_NAME_TRACKED using DependentList = std::vector; #else typedef std::vector DependentList; #endif using DependentListIter = DependentList::iterator; using DependentListIterConst = DependentList::const_iterator; #if SMTG_CPP11_STDLIBSUPPORT using DependentMap = std::unordered_map; #else typedef std::map DependentMap; #endif using DependentMapIter = DependentMap::iterator; using DependentMapIterConst = DependentMap::const_iterator; struct Table { DependentMap depMap[kHashSize]; DeferedChangeList defered; UpdateDataList updateData; }; //------------------------------------------------------------------------ void updateDone (FUnknown* unknown, int32 message) { if (message != IDependent::kDestroyed) { FObject* obj = FObject::unknownToObject (unknown); if (obj) obj->updateDone (message); } } } // namespace Update //------------------------------------------------------------------------ static int32 countEntries (Update::DependentMap& map) { int32 total = 0; Update::DependentMapIterConst iterMap = map.begin (); while (iterMap != map.end ()) { const Update::DependentList& list = iterMap->second; Update::DependentListIterConst iterList = list.begin (); while (iterList != list.end ()) { total++; ++iterList; } ++iterMap; } return total; } //------------------------------------------------------------------------ UpdateHandler::UpdateHandler () { table = NEW Update::Table; if (FObject::getUpdateHandler () == nullptr) FObject::setUpdateHandler (this); } //------------------------------------------------------------------------ UpdateHandler::~UpdateHandler () { if (FObject::getUpdateHandler () == this) FObject::setUpdateHandler (nullptr); delete table; table = nullptr; } //------------------------------------------------------------------------ tresult PLUGIN_API UpdateHandler::addDependent (FUnknown* u, IDependent* _dependent) { IPtr unknown = Update::getUnknownBase (u); if (!unknown || !_dependent) return kResultFalse; FGuard guard (lock); #if CLASS_NAME_TRACKED Update::Dependency dependent (unknown, _dependent); FObject* obj = FObject::unknownToObject (unknown); if (obj) dependent.objClass = obj->isA (); obj = FObject::unknownToObject (_dependent); if (obj) dependent.depClass = obj->isA (); #else IDependent* dependent = _dependent; #endif Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; Update::DependentMapIter it = map.find (unknown); if (it == map.end ()) { Update::DependentList list; list.push_back (dependent); map[unknown] = list; } else { (*it).second.push_back (dependent); } return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent) { size_t eraseCount; return removeDependent (u, dependent, eraseCount); } //------------------------------------------------------------------------ tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent, size_t& eraseCount) { eraseCount = 0; IPtr unknown = Update::getUnknownBase (u); if (unknown == nullptr && dependent == nullptr) return kResultFalse; FGuard guard (lock); Update::UpdateDataListIterConst iter = table->updateData.begin (); while (iter != table->updateData.end ()) { if ((*iter).obj == unknown || unknown == nullptr) { for (uint32 count = 0; count < (*iter).count; count++) { if ((*iter).dependents[count] == dependent) (*iter).dependents[count] = nullptr; } } ++iter; } // Remove all objects for the given dependent if (unknown == nullptr) { for (uint32 j = 0; j < Update::kHashSize; j++) { Update::DependentMap& map = table->depMap[j]; Update::DependentMapIter iterMap = map.begin (); while (iterMap != map.end ()) { Update::DependentList& list = (*iterMap).second; Update::DependentListIter iterList = list.begin (); bool listIsEmpty = false; while (iterList != list.end ()) { #if CLASS_NAME_TRACKED if ((*iterList).dep == dependent) #else if ((*iterList) == dependent) #endif { eraseCount = list.size (); if (list.size () == 1u) { listIsEmpty = true; break; } iterList = list.erase (iterList); } else { ++iterList; } } if (listIsEmpty) iterMap = map.erase (iterMap); else ++iterMap; } } } else { bool mustFlush = true; Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; Update::DependentMapIter iterList = map.find (unknown); #if NON_EXISTING_DEPENDENCY_CHECK SMTG_ASSERT (iterList != map.end () && "ERROR: Trying to remove a non existing dependency!") #endif if (iterList != map.end ()) { if (dependent == nullptr) // Remove all dependents of object { eraseCount = iterList->second.size (); map.erase (iterList); } else // Remove one dependent { Update::DependentList& dependentlist = (*iterList).second; Update::DependentListIter iterDependentlist = dependentlist.begin (); while (iterDependentlist != dependentlist.end ()) { #if CLASS_NAME_TRACKED if ((*iterDependentlist).dep == dependent) #else if ((*iterDependentlist) == dependent) #endif { iterDependentlist = dependentlist.erase (iterDependentlist); eraseCount++; if (dependentlist.empty ()) { map.erase (iterList); break; } } else { ++iterDependentlist; mustFlush = false; } } } } if (mustFlush) cancelUpdates (unknown); } return kResultTrue; } //------------------------------------------------------------------------ tresult UpdateHandler::doTriggerUpdates (FUnknown* u, int32 message, bool suppressUpdateDone) { IPtr unknown = Update::getUnknownBase (u); if (!unknown) return kResultFalse; // to avoid crashes due to stack overflow, we reduce the amount of memory reserved for the // dependents IDependent* smallDependents[Update::kMapSize / 10]; // 8kB for x64 IDependent** dependents = smallDependents; int32 maxDependents = Update::kMapSize / 10; int32 count = 0; { FGuard guard (lock); // scope for lock Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; Update::DependentMapIterConst iterList = map.find (unknown); if (iterList != map.end ()) { const Update::DependentList& dependentlist = (*iterList).second; Update::DependentListIterConst iterDependentlist = dependentlist.begin (); while (iterDependentlist != dependentlist.end ()) { #if CLASS_NAME_TRACKED dependents[count] = (*iterDependentlist).dep; #else dependents[count] = *iterDependentlist; #endif count++; if (count >= maxDependents) { if (dependents == smallDependents) { dependents = NEW IDependent*[Update::kMapSize]; memcpy (dependents, smallDependents, count * sizeof (dependents[0])); maxDependents = Update::kMapSize; } else { SMTG_WARNING ("Dependency overflow") break; } } ++iterDependentlist; } } // push update data on the stack if (count > 0) { Update::UpdateData data (unknown, dependents, count); table->updateData.push_back (data); } } // end scope int32 i = 0; while (i < count) { if (dependents[i]) dependents[i]->update (unknown, message); i++; } if (dependents != smallDependents) delete[] dependents; // remove update data from the stack if (count > 0) { FGuard guard (lock); table->updateData.pop_back (); } // send update done message if (suppressUpdateDone == false) Update::updateDone (unknown, message); return count > 0 ? kResultTrue : kResultFalse; // Object was found and has been updated } //------------------------------------------------------------------------ tresult PLUGIN_API UpdateHandler::triggerUpdates (FUnknown* u, int32 message) { return doTriggerUpdates (u, message, false); } //------------------------------------------------------------------------ tresult PLUGIN_API UpdateHandler::deferUpdates (FUnknown* u, int32 message) { IPtr unknown = Update::getUnknownBase (u); if (!unknown) return kResultFalse; FGuard guard (lock); Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; Update::DependentMapIterConst iterList = map.find (unknown); bool hasDependency = (iterList != map.end ()); if (hasDependency == false) { Update::updateDone (unknown, message); return kResultTrue; } bool found = false; Update::DeferedChangeListIterConst iter = table->defered.begin (); while (iter != table->defered.end ()) { if ((*iter).obj == unknown && (*iter).msg == message) { found = true; break; } ++iter; } if (!found) { Update::DeferedChange change (unknown, message); table->defered.push_back (change); } return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API UpdateHandler::triggerDeferedUpdates (FUnknown* unknown) { Update::DeferedChangeList deferedAgain; if (!unknown) { while (table->defered.empty () == false) { // Remove first from queue lock.lock (); FUnknown* obj = table->defered.front ().obj; int32 msg = table->defered.front ().msg; table->defered.pop_front (); // check if this object is currently being updated. in this case, defer update again... bool canSignal = true; Update::UpdateDataListIterConst iter = table->updateData.begin (); while (iter != table->updateData.end ()) { if ((*iter).obj == obj) { canSignal = false; break; } ++iter; } lock.unlock (); if (canSignal) { triggerUpdates (obj, msg); } else { Update::DeferedChange change (obj, msg); deferedAgain.push_back (change); } } } else { IPtr object = Update::getUnknownBase (unknown); Update::DeferedChange tmp (object); while (true) { lock.lock (); Update::DeferedChangeListIter it = std::find (table->defered.begin (), table->defered.end (), tmp); if (it == table->defered.end ()) { lock.unlock (); return kResultTrue; } if ((*it).obj != nullptr) { int32 msg = (*it).msg; table->defered.erase (it); // check if this object is currently being updated. in this case, defer update // again... bool canSignal = true; Update::UpdateDataListIterConst iter = table->updateData.begin (); while (iter != table->updateData.end ()) { if ((*iter).obj == object) { canSignal = false; break; } ++iter; } lock.unlock (); if (canSignal) { triggerUpdates (object, msg); } else { Update::DeferedChange change (object, msg); deferedAgain.push_back (change); } } } } if (deferedAgain.empty () == false) { FGuard guard (lock); Update::DeferedChangeListIterConst iter = deferedAgain.begin (); while (iter != deferedAgain.end ()) { table->defered.push_back (*iter); ++iter; } } return kResultTrue; } //------------------------------------------------------------------------ tresult PLUGIN_API UpdateHandler::cancelUpdates (FUnknown* u) { IPtr unknown = Update::getUnknownBase (u); if (!unknown) return kResultFalse; FGuard guard (lock); Update::DeferedChange change (unknown, 0); while (true) { auto iter = std::find (table->defered.begin (), table->defered.end (), change); if (iter != table->defered.end ()) table->defered.erase (iter); else break; } return kResultTrue; } //------------------------------------------------------------------------ size_t UpdateHandler::countDependencies (FUnknown* object) { FGuard guard (lock); uint32 res = 0; IPtr unknown = Update::getUnknownBase (object); if (unknown) { Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; Update::DependentMapIter iterList = map.find (unknown); if (iterList != map.end ()) return iterList->second.size (); } else { for (uint32 j = 0; j < Update::kHashSize; j++) { Update::DependentMap& map = table->depMap[j]; res += countEntries (map); } } return res; } #if DEVELOPMENT //------------------------------------------------------------------------ bool UpdateHandler::checkDeferred (FUnknown* object) { IPtr unknown = Update::getUnknownBase (object); FGuard guard (lock); Update::DeferedChange tmp (unknown); Update::DeferedChangeListIterConst it = std::find (table->defered.begin (), table->defered.end (), tmp); if (it != table->defered.end ()) return true; return false; } //------------------------------------------------------------------------ bool UpdateHandler::hasDependencies (FUnknown* u) { IPtr unknown = Update::getUnknownBase (u); if (!unknown) return false; FGuard guard (lock); Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; Update::DependentMapIterConst iterList = map.find (unknown); bool hasDependency = (iterList != map.end ()); return hasDependency; } //------------------------------------------------------------------------ void UpdateHandler::printForObject (FObject* obj) const { IPtr unknown = Update::getUnknownBase (obj); if (!unknown) return; FUnknownPtr dep (obj); bool header = false; Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)]; Update::DependentMapIterConst iterList = map.begin (); while (iterList != map.end ()) { const Update::DependentList& dependentlist = (*iterList).second; Update::DependentListIterConst iterDependentlist = dependentlist.begin (); while (iterDependentlist != dependentlist.end ()) { #if CLASS_NAME_TRACKED if ((*iterList).first == unknown || (*iterDependentlist).dep == dep.getInterface ()) { if (!header) { FDebugPrint ("Dependencies for object %8" FORMAT_INT64A " %s\n", (uint64)obj, obj->isA ()); header = true; } FDebugPrint ("%s %8" FORMAT_INT64A "\n <- %s %8" FORMAT_INT64A "\n", (*iterDependentlist).depClass, (uint64) (*iterDependentlist).dep, (*iterDependentlist).objClass, (uint64) ((*iterList).first)); } #else if ((*iterList).first == unknown || (*iterDependentlist) == dep.getInterface ()) { if (!header) { FDebugPrint ("Dependencies for object %8" FORMAT_INT64A " %s\n", (uint64)obj, obj->isA ()); header = true; } FDebugPrint ("%8" FORMAT_INT64A "\n <- %8" FORMAT_INT64A "\n", (uint64) (*iterDependentlist), (uint64) ((*iterList).first)); } #endif ++iterDependentlist; } ++iterList; } } #endif //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fbuffer.h0000644000000000000000000000012715124701711020207 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18496357 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fbuffer.h0000644000175000001440000003073115124701711020177 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fbuffer.h // Created by : Steinberg, 2008 // Description : // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ftypes.h" #include namespace Steinberg { class String; //------------------------------------------------------------------------ /** Buffer. @ingroup adt A Buffer is an object-oriented wrapper for a piece of memory. It adds several utility functions, e.g. for managing the size of the Buffer, appending or prepending values or strings to it. Internally it uses the standard memory functions malloc(), free(), etc. */ //------------------------------------------------------------------------ class Buffer { public: //--------------------------------------------------------------------- /** Default constructor, allocates no memory at all. */ Buffer (); /** Constructor - creates a new Buffer with a given size and copies contents from optional memory pointer. \param[in] b : optional memory pointer with the size of at least the given size \param[in] size : the size of the new Buffer to be allocated, in bytes. */ Buffer (const void* b, uint32 size); /** Constructor - creates a new Buffer with a given size and fills it all with a given value. \param[in] size : the size of the new Buffer to be allocated, in bytes. \param[in] initVal : the initial value the Buffer will be completely filled with */ Buffer (uint32 size, uint8 initVal); /** Constructor - creates a new Buffer with a given size. \param[in] size : the size of the new Buffer to be allocated, in bytes. */ Buffer (uint32 size); /** Copy constructor - creates a new Buffer from a given Buffer. \param[in] buff : the Buffer from which all memory will be copied to the new one */ Buffer (const Buffer& buff); /** Destructor - deallocates the internal memory. */ virtual ~Buffer (); /** Assignment operator - copies contents from a given Buffer and increases the size if necessary. \param[in] buff : the Buffer from which all memory will be copied */ void operator = (const Buffer& buff); /** Comparison operator - copies contents from a given Buffer and increases the size if necessary. \param[in] buff : the Buffer to be compared to \return true, if the given Buffer's content is equal to this one, else false */ bool operator == (const Buffer& buff)const; uint32 getSize () const {return memSize;} ///< \return the actual size of the Buffer's memory, in bytes. /** Sets a new size for this Buffer, keeping as much content as possible. \param[in] newSize : the new size for the Buffer, in bytes, newSize maybe zero \return true, if the new size could be adapted, else false */ bool setSize (uint32 newSize); /** Increases the Buffer to the next block, block size given by delta. \param[in] memSize : the new minimum size of the Buffer, newSize maybe zero \return true, if the Buffer could be grown successfully, else false */ bool grow (uint32 memSize); bool setMaxSize (uint32 size) {return grow (size);} ///< see \ref grow() void fillup (uint8 initVal = 0); ///< set from fillSize to end uint32 getFillSize ()const {return fillSize;} ///< \return the actual fill size bool setFillSize (uint32 c); ///< sets a new fill size, does not change any memory inline void flush () {setFillSize (0);} ///< sets fill size to zero bool truncateToFillSize (); ///< \return always true, truncates the size of the Buffer to the actual fill size bool isFull () const { return (fillSize == memSize); } ///< \return true, if all memory is filled up, else false uint32 getFree () const { return (memSize - fillSize); }///< \return remaining memory inline void shiftStart (int32 amount) {return shiftAt (0, amount);} ///< moves all memory by given amount, grows the Buffer if necessary void shiftAt (uint32 position, int32 amount); ///< moves memory starting at the given position void move (int32 amount, uint8 initVal = 0); ///< shifts memory at start without growing the buffer, so data is lost and initialized with init val bool copy (uint32 from, uint32 to, uint32 bytes); ///< copies a number of bytes from one position to another, the size may be adapted uint32 get (void* b, uint32 size); ///< copy to buffer from fillSize, and shift fillSize void setDelta (uint32 d) {delta = d;} ///< define the block size by which the Buffer grows, see \ref grow() bool put (uint8); ///< append value at end, grows Buffer if necessary bool put (char16 c); ///< append value at end, grows Buffer if necessary bool put (char c); ///< append value at end, grows Buffer if necessary bool put (const void* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary bool put (void* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary bool put (uint8* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary bool put (char8* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary bool put (const uint8* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary bool put (const char8* , uint32 size); ///< append bytes from a given buffer, grows Buffer if necessary bool put (const String&); ///< append String at end, grows Buffer if necessary void set (uint8 value); ///< fills complete Buffer with given value // strings ---------------- bool appendString (const tchar* s); bool appendString (tchar* s); bool appendString (tchar c) { return put (c); } bool appendString8 (const char8* s); bool appendString16 (const char16* s); bool appendString8 (char8* s) { return appendString8 ((const char8*)s); } bool appendString8 (unsigned char* s) { return appendString8 ((const char8*)s); } bool appendString8 (const unsigned char* s) { return appendString8 ((const char8*)s); } bool appendString8 (char8 c) { return put ((uint8)c); } bool appendString8 (unsigned char c) { return put (c); } bool appendString16 (char16 c) { return put (c); } bool appendString16 (char16* s) { return appendString16 ((const char16*)s); } bool prependString (const tchar* s); bool prependString (tchar* s); bool prependString (tchar c); bool prependString8 (const char8* s); bool prependString16 (const char16* s); bool prependString8 (char8 c); bool prependString8 (unsigned char c) { return prependString8 ((char8)c); } bool prependString8 (char8* s) { return prependString8 ((const char8*)s); } bool prependString8 (unsigned char* s) { return prependString8((const char8*)s); } bool prependString8 (const unsigned char* s) { return prependString8 ((const char8*)s); } bool prependString16 (char16 c); bool prependString16 (char16* s) { return prependString16 ((const char16*)s); } bool operator+= (const char* s) { return appendString8 (s); } bool operator+= (char c) { return appendString8 (c); } bool operator+= (const char16* s) { return appendString16 (s); } bool operator+= (char16 c) { return appendString16 (c); } bool operator= (const char* s) { flush (); return appendString8 (s); } bool operator= (const char16* s) { flush (); return appendString16 (s); } bool operator= (char8 c) { flush (); return appendString8 (c); } bool operator= (char16 c) { flush (); return appendString16 (c); } void endString () {put (tchar (0));} void endString8 () {put (char8 (0));} void endString16 () {put (char16 (0));} bool makeHexString (String& result); bool fromHexString (const char8* string); // conversion operator void* () const { return (void*)buffer; } ///< conversion inline tchar* str () const {return (tchar*)buffer;} ///< conversion inline char8* str8 () const {return (char8*)buffer;} ///< conversion inline char16* str16 () const {return (char16*)buffer;} ///< conversion inline int8* int8Ptr () const {return (int8*)buffer;} ///< conversion inline uint8* uint8Ptr () const {return (uint8*)buffer; } ///< conversion inline int16* int16Ptr () const {return (int16*)buffer; } ///< conversion inline uint16* uint16Ptr () const {return (uint16*)buffer; } ///< conversion inline int32* int32Ptr () const {return (int32*)buffer; } ///< conversion inline uint32* uint32Ptr () const {return (uint32*)buffer; } ///< conversion inline float* floatPtr () const {return (float*)buffer; } ///< conversion inline double* doublePtr () const {return (double*)buffer; } ///< conversion inline char16* wcharPtr () const {return (char16*)buffer;} ///< conversion int8* operator + (uint32 i); ///< \return the internal Buffer's address plus the given offset i, zero if offset is out of range int32 operator ! () { return buffer == nullptr; } enum swapSize { kSwap16 = 2, kSwap32 = 4, kSwap64 = 8 }; bool swap (int16 swapSize); ///< swap all bytes of this Buffer by the given swapSize static bool swap (void* buffer, uint32 bufferSize, int16 swapSize); ///< utility, swap given number of bytes in given buffer by the given swapSize void take (Buffer& from); ///< takes another Buffer's memory, frees the current Buffer's memory int8* pass (); ///< pass the current Buffer's memory /** Converts a Buffer's content to UTF-16 from a given multi-byte code page, Buffer must contain char8 of given encoding. \param[in] sourceCodePage : the actual code page of the Buffer's content \return true, if the conversion was successful, else false */ virtual bool toWideString (int32 sourceCodePage); // Buffer contains char8 of given encoding -> utf16 /** Converts a Buffer's content from UTF-16 to a given multi-byte code page, Buffer must contain UTF-16 encoded characters. \param[in] destCodePage : the desired code page to convert the Buffer's content to \return true, if the conversion was successful, else false */ virtual bool toMultibyteString (int32 destCodePage); // Buffer contains utf16 -> char8 of given encoding //------------------------------------------------------------------------ protected: static const uint32 defaultDelta = 0x1000; // 0x1000 int8* buffer; uint32 memSize; uint32 fillSize; uint32 delta; }; inline bool Buffer::put (void* p, uint32 count) { return put ((const void*)p , count ); } inline bool Buffer::put (uint8 * p, uint32 count) { return put ((const void*)p , count ); } inline bool Buffer::put (char8* p, uint32 count) { return put ((const void*)p , count ); } inline bool Buffer::put (const uint8* p, uint32 count) { return put ((const void*)p , count ); } inline bool Buffer::put (const char8* p, uint32 count) { return put ((const void*)p , count ); } //------------------------------------------------------------------------ inline bool Buffer::appendString (const tchar* s) { #ifdef UNICODE return appendString16 (s); #else return appendString8 (s); #endif } //------------------------------------------------------------------------ inline bool Buffer::appendString (tchar* s) { #ifdef UNICODE return appendString16 (s); #else return appendString8 (s); #endif } //------------------------------------------------------------------------ inline bool Buffer::prependString (const tchar* s) { #ifdef UNICODE return prependString16 (s); #else return prependString8 (s); #endif } //------------------------------------------------------------------------ inline bool Buffer::prependString (tchar* s) { #ifdef UNICODE return prependString16 (s); #else return prependString8 (s); #endif } //------------------------------------------------------------------------ inline bool Buffer::prependString (tchar c) { #ifdef UNICODE return prependString16 (c); #else return prependString8 (c); #endif } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fobject.h0000644000000000000000000000012715124701711020204 xustar0029 mtime=1767080905.18521127 29 atime=1767080905.18521127 29 ctime=1767080905.18521127 qtractor-1.5.11/src/vst3/base/source/fobject.h0000644000175000001440000004576415124701711020210 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fobject.h // Created by : Steinberg, 2008 // Description : Basic Object implementing FUnknown // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- //------------------------------------------------------------------------ /** @file base/source/fobject.h Basic Object implementing FUnknown. */ //------------------------------------------------------------------------ #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/base/iupdatehandler.h" #include "base/source/fdebug.h" // use of NEW #define SMTG_DEPENDENCY_COUNT DEVELOPMENT namespace Steinberg { //---------------------------------- using FClassID = FIDString; //------------------------------------------------------------------------ // Basic FObject - implements FUnknown + IDependent //------------------------------------------------------------------------ /** Implements FUnknown and IDependent. FObject is a polymorphic class that implements IDependent (of SKI module) and therefore derived from FUnknown, which is the most abstract base class of all. All COM-like virtual methods of FUnknown such as queryInterface(), addRef(), release() are implemented here. On top of that, dependency-related methods are implemented too. Pointer casting is done via the template methods FCast, either FObject to FObject or FUnknown to FObject. FObject supports a new singleton concept, therefore these objects are deleted automatically upon program termination. - Runtime type information: An object can be queried at runtime, of what class it is. To do this correctly, every class must override some methods. This is simplified by using the OBJ_METHODS macros @see - FUnknown - IDependent - IUpdateHandler */ //------------------------------------------------------------------------ class FObject : public IDependent { public: //------------------------------------------------------------------------ FObject () = default; ///< default constructor... FObject (const FObject&) ///< overloaded constructor... : refCount (1) #if SMTG_DEPENDENCY_COUNT , dependencyCount (0) #endif {} FObject& operator= (const FObject&) { return *this; } ///< overloads operator "=" as the reference assignment virtual ~FObject (); ///< destructor... // OBJECT_METHODS static inline FClassID getFClassID () {return "FObject";} ///< return Class ID as an ASCII string (statically) virtual FClassID isA () const {return FObject::getFClassID ();} ///< a local alternative to getFClassID () virtual bool isA (FClassID s) const {return isTypeOf (s, false);} ///< evaluates if the passed ID is of the FObject type virtual bool isTypeOf (FClassID s, bool /*askBaseClass*/ = true) const {return classIDsEqual (s, FObject::getFClassID ());} ///< evaluates if the passed ID is of the FObject type int32 getRefCount () {return refCount;} ///< returns the current interface reference count FUnknown* unknownCast () {return this;} ///< get FUnknown interface from object // FUnknown tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) SMTG_OVERRIDE; ///< please refer to FUnknown::queryInterface () uint32 PLUGIN_API addRef () SMTG_OVERRIDE; ///< please refer to FUnknown::addref () uint32 PLUGIN_API release () SMTG_OVERRIDE; ///< please refer to FUnknown::release () // IDependent void PLUGIN_API update (FUnknown* /*changedUnknown*/, int32 /*message*/) SMTG_OVERRIDE {} ///< empty virtual method that should be overridden by derived classes for data updates upon changes // IDependency virtual void addDependent (IDependent* dep); ///< adds dependency to the object virtual void removeDependent (IDependent* dep); ///< removes dependency from the object virtual void changed (int32 msg = kChanged); ///< Inform all dependents, that the object has changed. virtual void deferUpdate (int32 msg = kChanged); ///< Similar to triggerUpdates, except only delivered in idle (usefull in collecting updates). virtual void updateDone (int32 /* msg */) {} ///< empty virtual method that should be overridden by derived classes virtual bool isEqualInstance (FUnknown* d) {return this == d;} static void setUpdateHandler (IUpdateHandler* handler) {gUpdateHandler = handler;} ///< set method for the local attribute static IUpdateHandler* getUpdateHandler () {return gUpdateHandler;} ///< get method for the local attribute // static helper functions static inline bool classIDsEqual (FClassID ci1, FClassID ci2); ///< compares (evaluates) 2 class IDs static inline FObject* unknownToObject (FUnknown* unknown); ///< pointer conversion from FUnknown to FObject /** convert from FUnknown to FObject */ template static inline IPtr fromUnknown (FUnknown* unknown); /** Special UID that is used to cast an FUnknown pointer to a FObject */ static const FUID iid; //------------------------------------------------------------------------ protected: int32 refCount = 1; ///< COM-model local reference count #if SMTG_DEPENDENCY_COUNT int16 dependencyCount = 0; #endif static IUpdateHandler* gUpdateHandler; }; //------------------------------------------------------------------------ // conversion from FUnknown to FObject subclass //------------------------------------------------------------------------ template inline IPtr FObject::fromUnknown (FUnknown* unknown) { if (unknown) { FObject* object = nullptr; if (unknown->queryInterface (FObject::iid, (void**)&object) == kResultTrue && object) { if (object->isTypeOf (C::getFClassID (), true)) return IPtr (static_cast (object), false); object->release (); } } return {}; } //------------------------------------------------------------------------ inline FObject* FObject::unknownToObject (FUnknown* unknown) { FObject* object = nullptr; if (unknown) { unknown->queryInterface (FObject::iid, (void**)&object); if (object) { if (object->release () == 0) object = nullptr; } } return object; } //------------------------------------------------------------------------ inline bool FObject::classIDsEqual (FClassID ci1, FClassID ci2) { return (ci1 && ci2) ? (strcmp (ci1, ci2) == 0) : false; } //----------------------------------------------------------------------- /** FCast overload 1 - FObject to FObject */ //----------------------------------------------------------------------- template inline C* FCast (const FObject* object) { if (object && object->isTypeOf (C::getFClassID (), true)) return (C*) object; return nullptr; } //----------------------------------------------------------------------- /** FCast overload 2 - FUnknown to FObject */ //----------------------------------------------------------------------- template inline C* FCast (FUnknown* unknown) { FObject* object = FObject::unknownToObject (unknown); return FCast (object); } //----------------------------------------------------------------------- /** ICast - casting from FObject to FUnknown Interface */ //----------------------------------------------------------------------- template inline IPtr ICast (FObject* object) { return FUnknownPtr (object ? object->unknownCast () : nullptr); } //----------------------------------------------------------------------- /** ICast - casting from FUnknown to another FUnknown Interface */ //----------------------------------------------------------------------- template inline IPtr ICast (FUnknown* object) { return FUnknownPtr (object); } //------------------------------------------------------------------------ template inline C* FCastIsA (const FObject* object) { if (object && object->isA (C::getFClassID ())) return (C*)object; return nullptr; } #ifndef SMTG_HIDE_DEPRECATED_INLINE_FUNCTIONS //----------------------------------------------------------------------- /** \deprecated FUCast - casting from FUnknown to Interface */ //----------------------------------------------------------------------- template SMTG_DEPRECATED_MSG("use ICast<>") inline C* FUCast (FObject* object) { return FUnknownPtr (object ? object->unknownCast () : nullptr); } template SMTG_DEPRECATED_MSG("use ICast<>") inline C* FUCast (FUnknown* object) { return FUnknownPtr (object); } #endif // SMTG_HIDE_DEPRECATED_FUNCTIONS //------------------------------------------------------------------------ /** @name Convenience methods that call release or delete respectively on a pointer if it is non-zero, and then set the pointer to zero. Note: you should prefer using IPtr or OPtr instead of these methods whenever possible. Examples: @code ~Foo () { // instead of ... if (somePointer) { somePointer->release (); somePointer = 0; } // ... just being lazy I write SafeRelease (somePointer) } @endcode */ ///@{ //----------------------------------------------------------------------- template inline void SafeRelease (I *& ptr) { if (ptr) { ptr->release (); ptr = 0; } } //----------------------------------------------------------------------- template inline void SafeRelease (IPtr & ptr) { ptr = 0; } //----------------------------------------------------------------------- template inline void SafeDelete (T *& ptr) { if (ptr) { delete ptr; ptr = 0; } } ///@} //----------------------------------------------------------------------- template inline void AssignShared (T*& dest, T* newPtr) { if (dest == newPtr) return; if (dest) dest->release (); dest = newPtr; if (dest) dest->addRef (); } //----------------------------------------------------------------------- template inline void AssignSharedDependent (IDependent* _this, T*& dest, T* newPtr) { if (dest == newPtr) return; if (dest) dest->removeDependent (_this); AssignShared (dest, newPtr); if (dest) dest->addDependent (_this); } //----------------------------------------------------------------------- template inline void AssignSharedDependent (IDependent* _this, IPtr& dest, T* newPtr) { if (dest == newPtr) return; if (dest) dest->removeDependent (_this); dest = newPtr; if (dest) dest->addDependent (_this); } //----------------------------------------------------------------------- template inline void SafeReleaseDependent (IDependent* _this, T*& dest) { if (dest) dest->removeDependent (_this); SafeRelease (dest); } //----------------------------------------------------------------------- template inline void SafeReleaseDependent (IDependent* _this, IPtr& dest) { if (dest) dest->removeDependent (_this); SafeRelease (dest); } //------------------------------------------------------------------------ /** Automatic creation and destruction of singleton instances. */ namespace Singleton { /** registers an instance (type FObject) */ void registerInstance (FObject** o); /** Returns true when singleton instances were already released. */ bool isTerminated (); /** lock and unlock the singleton registration for multi-threading safety */ void lockRegister (); void unlockRegister (); } //------------------------------------------------------------------------ } // namespace Steinberg //----------------------------------------------------------------------- #define SINGLETON(ClassName) \ static ClassName* instance (bool create = true) \ { \ static Steinberg::FObject* inst = nullptr; \ if (inst == nullptr && create && Steinberg::Singleton::isTerminated () == false) \ { \ Steinberg::Singleton::lockRegister (); \ if (inst == nullptr) \ { \ inst = NEW ClassName; \ Steinberg::Singleton::registerInstance (&inst); \ } \ Steinberg::Singleton::unlockRegister (); \ } \ return (ClassName*)inst; \ } //----------------------------------------------------------------------- #define OBJ_METHODS(className, baseClass) \ static inline Steinberg::FClassID getFClassID () {return (#className);} \ virtual Steinberg::FClassID isA () const SMTG_OVERRIDE {return className::getFClassID ();} \ virtual bool isA (Steinberg::FClassID s) const SMTG_OVERRIDE {return isTypeOf (s, false);} \ virtual bool isTypeOf (Steinberg::FClassID s, bool askBaseClass = true) const SMTG_OVERRIDE \ { return (FObject::classIDsEqual (s, #className) ? true : (askBaseClass ? baseClass::isTypeOf (s, true) : false)); } //------------------------------------------------------------------------ /** Delegate refcount functions to BaseClass. BaseClase must implement ref counting. */ //------------------------------------------------------------------------ #define REFCOUNT_METHODS(BaseClass) \ virtual Steinberg::uint32 PLUGIN_API addRef ()SMTG_OVERRIDE{ return BaseClass::addRef (); } \ virtual Steinberg::uint32 PLUGIN_API release ()SMTG_OVERRIDE{ return BaseClass::release (); } //------------------------------------------------------------------------ /** @name Macros to implement FUnknown::queryInterface (). Examples: @code class Foo : public FObject, public IFoo2, public IFoo3 { ... DEFINE_INTERFACES DEF_INTERFACE (IFoo2) DEF_INTERFACE (IFoo3) END_DEFINE_INTERFACES (FObject) REFCOUNT_METHODS(FObject) // Implement IFoo2 interface ... // Implement IFoo3 interface ... ... }; @endcode */ ///@{ //------------------------------------------------------------------------ /** Start defining interfaces. */ //------------------------------------------------------------------------ #define DEFINE_INTERFACES \ Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID iid, void** obj) SMTG_OVERRIDE \ { //------------------------------------------------------------------------ /** Add a interfaces. */ //------------------------------------------------------------------------ #define DEF_INTERFACE(InterfaceName) \ QUERY_INTERFACE (iid, obj, Steinberg::getTUID (), InterfaceName) //------------------------------------------------------------------------ /** End defining interfaces. */ //------------------------------------------------------------------------ #define END_DEFINE_INTERFACES(BaseClass) \ return BaseClass::queryInterface (iid, obj); \ } ///@} //------------------------------------------------------------------------ /** @name Convenient macros to implement Steinberg::FUnknown::queryInterface (). Examples: @code class Foo : public FObject, public IFoo2, public IFoo3 { ... DEF_INTERFACES_2(IFoo2,IFoo3,FObject) REFCOUNT_METHODS(FObject) ... }; @endcode */ ///@{ //------------------------------------------------------------------------ #define DEF_INTERFACES_1(InterfaceName,BaseClass) \ DEFINE_INTERFACES \ DEF_INTERFACE (InterfaceName) \ END_DEFINE_INTERFACES (BaseClass) //------------------------------------------------------------------------ #define DEF_INTERFACES_2(InterfaceName1,InterfaceName2,BaseClass) \ DEFINE_INTERFACES \ DEF_INTERFACE (InterfaceName1) \ DEF_INTERFACE (InterfaceName2) \ END_DEFINE_INTERFACES (BaseClass) //------------------------------------------------------------------------ #define DEF_INTERFACES_3(InterfaceName1,InterfaceName2,InterfaceName3,BaseClass) \ DEFINE_INTERFACES \ DEF_INTERFACE (InterfaceName1) \ DEF_INTERFACE (InterfaceName2) \ DEF_INTERFACE (InterfaceName3) \ END_DEFINE_INTERFACES (BaseClass) //------------------------------------------------------------------------ #define DEF_INTERFACES_4(InterfaceName1,InterfaceName2,InterfaceName3,InterfaceName4,BaseClass) \ DEFINE_INTERFACES \ DEF_INTERFACE (InterfaceName1) \ DEF_INTERFACE (InterfaceName2) \ DEF_INTERFACE (InterfaceName3) \ DEF_INTERFACE (InterfaceName4) \ END_DEFINE_INTERFACES (BaseClass) ///@} //------------------------------------------------------------------------ /** @name Convenient macros to implement Steinberg::FUnknown methods. Examples: @code class Foo : public FObject, public IFoo2, public IFoo3 { ... FUNKNOWN_METHODS2(IFoo2,IFoo3,FObject) ... }; @endcode */ ///@{ #define FUNKNOWN_METHODS(InterfaceName,BaseClass) \ DEF_INTERFACES_1(InterfaceName,BaseClass) \ REFCOUNT_METHODS(BaseClass) #define FUNKNOWN_METHODS2(InterfaceName1,InterfaceName2,BaseClass) \ DEF_INTERFACES_2(InterfaceName1,InterfaceName2,BaseClass) \ REFCOUNT_METHODS(BaseClass) #define FUNKNOWN_METHODS3(InterfaceName1,InterfaceName2,InterfaceName3,BaseClass) \ DEF_INTERFACES_3(InterfaceName1,InterfaceName2,InterfaceName3,BaseClass) \ REFCOUNT_METHODS(BaseClass) #define FUNKNOWN_METHODS4(InterfaceName1,InterfaceName2,InterfaceName3,InterfaceName4,BaseClass) \ DEF_INTERFACES_4(InterfaceName1,InterfaceName2,InterfaceName3,InterfaceName4,BaseClass) \ REFCOUNT_METHODS(BaseClass) ///@} //------------------------------------------------------------------------ //------------------------------------------------------------------------ #if COM_COMPATIBLE //------------------------------------------------------------------------ /** @name Macros to implement IUnknown interfaces with FObject. Examples: @code class MyEnumFormat : public FObject, IEnumFORMATETC { ... COM_UNKNOWN_METHODS (IEnumFORMATETC, IUnknown) ... }; @endcode */ ///@{ //------------------------------------------------------------------------ #define IUNKNOWN_REFCOUNT_METHODS(BaseClass) \ STDMETHOD_ (ULONG, AddRef) (void) {return BaseClass::addRef ();} \ STDMETHOD_ (ULONG, Release) (void) {return BaseClass::release ();} //------------------------------------------------------------------------ #define COM_QUERY_INTERFACE(iid, obj, InterfaceName) \ if (riid == __uuidof(InterfaceName)) \ { \ addRef (); \ *obj = (InterfaceName*)this; \ return kResultOk; \ } //------------------------------------------------------------------------ #define COM_OBJECT_QUERY_INTERFACE(InterfaceName,BaseClass) \ STDMETHOD (QueryInterface) (REFIID riid, void** object) \ { \ COM_QUERY_INTERFACE (riid, object, InterfaceName) \ return BaseClass::queryInterface ((FIDString)&riid, object); \ } //------------------------------------------------------------------------ #define COM_UNKNOWN_METHODS(InterfaceName,BaseClass) \ COM_OBJECT_QUERY_INTERFACE(InterfaceName,BaseClass) \ IUNKNOWN_REFCOUNT_METHODS(BaseClass) ///@} #endif // COM_COMPATIBLE qtractor-1.5.11/src/vst3/base/source/PaxHeaders/updatehandler.h0000644000000000000000000000013215124701711021404 xustar0030 mtime=1767080905.186211266 30 atime=1767080905.186211266 30 ctime=1767080905.186211266 qtractor-1.5.11/src/vst3/base/source/updatehandler.h0000644000175000001440000001162515124701711021401 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/updatehandler.h // Created by : Steinberg, 2008 // Description : // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "base/source/fobject.h" #include "base/thread/include/flock.h" #include "pluginterfaces/base/iupdatehandler.h" namespace Steinberg { /// @cond ignore namespace Update { struct Table; } /// @endcond //------------------------------------------------------------------------ /** Handle Send and Cancel pending message for a given object*/ //------------------------------------------------------------------------ class IUpdateManager : public FUnknown { public: //------------------------------------------------------------------------ /** cancel pending messages send by \param object or by any if object is 0 */ virtual tresult PLUGIN_API cancelUpdates (FUnknown* object) = 0; /** send pending messages send by \param object or by any if object is 0 */ virtual tresult PLUGIN_API triggerDeferedUpdates (FUnknown* object = nullptr) = 0; static const FUID iid; }; DECLARE_CLASS_IID (IUpdateManager, 0x030B780C, 0xD6E6418D, 0x8CE00BC2, 0x09C834D4) //------------------------------------------------------------------------------ /** UpdateHandler implements IUpdateManager and IUpdateHandler to handle dependencies between objects to store and forward messages to dependent objects. This implementation is thread save, so objects can send message, add or remove dependents from different threads. Do do so it uses mutex, so be aware of locking. */ //------------------------------------------------------------------------------ class UpdateHandler : public FObject, public IUpdateHandler, public IUpdateManager { public: //------------------------------------------------------------------------------ UpdateHandler (); ~UpdateHandler () SMTG_OVERRIDE; using FObject::addDependent; using FObject::removeDependent; using FObject::deferUpdate; // IUpdateHandler //private: friend class FObject; /** register \param dependent to get messages from \param object */ tresult PLUGIN_API addDependent (FUnknown* object, IDependent* dependent) SMTG_OVERRIDE; /** unregister \param dependent to get no messages from \param object */ tresult PLUGIN_API removeDependent (FUnknown* object, IDependent* dependent, size_t& earseCount); tresult PLUGIN_API removeDependent (FUnknown* object, IDependent* dependent) SMTG_OVERRIDE; public: /** send \param message to all dependents of \param object immediately */ tresult PLUGIN_API triggerUpdates (FUnknown* object, int32 message) SMTG_OVERRIDE; /** send \param message to all dependents of \param object when idle */ tresult PLUGIN_API deferUpdates (FUnknown* object, int32 message) SMTG_OVERRIDE; // IUpdateManager /** cancel pending messages send by \param object or by any if object is 0 */ tresult PLUGIN_API cancelUpdates (FUnknown* object) SMTG_OVERRIDE; /** send pending messages send by \param object or by any if object is 0 */ tresult PLUGIN_API triggerDeferedUpdates (FUnknown* object = nullptr) SMTG_OVERRIDE; /// @cond ignore // obsolete functions kept for compatibility void checkUpdates (FObject* object = nullptr) { triggerDeferedUpdates (object ? object->unknownCast () : nullptr); } void flushUpdates (FObject* object) { if (object) cancelUpdates (object->unknownCast ()); } void deferUpdate (FObject* object, int32 message) { if (object) deferUpdates (object->unknownCast (), message); } void signalChange (FObject* object, int32 message, bool suppressUpdateDone = false) { if (object) doTriggerUpdates (object->unknownCast (), message, suppressUpdateDone); } #if DEVELOPMENT bool checkDeferred (FUnknown* object); bool hasDependencies (FUnknown* object); void printForObject (FObject* object) const; #endif /// @endcond size_t countDependencies (FUnknown* object = nullptr); OBJ_METHODS (UpdateHandler, FObject) FUNKNOWN_METHODS2 (IUpdateHandler, IUpdateManager, FObject) SINGLETON (UpdateHandler) //------------------------------------------------------------------------------ private: tresult doTriggerUpdates (FUnknown* object, int32 message, bool suppressUpdateDone); Steinberg::Base::Thread::FLock lock; Update::Table* table = nullptr; }; //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/base/source/PaxHeaders/fstring.h0000644000000000000000000000013115124701711020237 xustar0030 mtime=1767080905.186211266 29 atime=1767080905.18521127 30 ctime=1767080905.186211266 qtractor-1.5.11/src/vst3/base/source/fstring.h0000644000175000001440000011663115124701711020240 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Base // Version : 1.0 // // Category : Helpers // Filename : base/source/fstring.h // Created by : Steinberg, 2008 // Description : String class // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ftypes.h" #include "pluginterfaces/base/fstrdefs.h" #include "pluginterfaces/base/istringresult.h" #include "pluginterfaces/base/ipersistent.h" #include "base/source/fobject.h" #include #include namespace Steinberg { class FVariant; class String; #ifdef UNICODE static const bool kWideStringDefault = true; #else static const bool kWideStringDefault = false; #endif static const uint16 kBomUtf16 = 0xFEFF; ///< UTF16 Byte Order Mark static const char8* const kBomUtf8 = "\xEF\xBB\xBF"; ///< UTF8 Byte Order Mark static const int32 kBomUtf8Length = 3; enum MBCodePage { kCP_ANSI = 0, ///< Default ANSI codepage. kCP_MAC_ROMAN = 2, ///< Default Mac codepage. kCP_ANSI_WEL = 1252, ///< West European Latin Encoding. kCP_MAC_CEE = 10029, ///< Mac Central European Encoding. kCP_Utf8 = 65001, ///< UTF8 Encoding. kCP_ShiftJIS = 932, ///< Shifted Japan Industrial Standard Encoding. kCP_US_ASCII = 20127, ///< US-ASCII (7-bit). kCP_Default = kCP_ANSI ///< Default ANSI codepage. }; enum UnicodeNormalization { kUnicodeNormC, ///< Unicode normalization Form C, canonical composition. kUnicodeNormD, ///< Unicode normalization Form D, canonical decomposition. kUnicodeNormKC, ///< Unicode normalization form KC, compatibility composition. kUnicodeNormKD ///< Unicode normalization form KD, compatibility decomposition. }; //------------------------------------------------------------------------ // Helper functions to create hash codes from string data. //------------------------------------------------------------------------ extern uint32 hashString8 (const char8* s, uint32 m); extern uint32 hashString16 (const char16* s, uint32 m); inline uint32 hashString (const tchar* s, uint32 m) { #ifdef UNICODE return hashString16 (s, m); #else return hashString8 (s, m); #endif } //----------------------------------------------------------------------------- /** Invariant String. @ingroup adt A base class which provides methods to work with its member string. Neither of the operations allows modifying the member string and that is why all operation are declared as const. There are operations for access, comparison, find, numbers and conversion. Almost all operations exist in three versions for char8, char16 and the polymorphic type tchar. The type tchar can either be char8 or char16 depending on whether UNICODE is activated or not.*/ //----------------------------------------------------------------------------- class ConstString { public: //----------------------------------------------------------------------------- ConstString (const char8* str, int32 length = -1); ///< Assign from string of type char8 (length=-1: all) ConstString (const char16* str, int32 length = -1); ///< Assign from string of type char16 (length=-1: all) ConstString (const ConstString& str, int32 offset = 0, int32 length = -1); ///< Copy constructor (length=-1: all). ConstString (const FVariant& var); ///< Assign a string from FVariant ConstString (); virtual ~ConstString () {} ///< Destructor. // access ----------------------------------------------------------------- virtual int32 length () const {return static_cast (len);} ///< Return length of string inline bool isEmpty () const {return buffer == nullptr || len == 0;} ///< Return true if string is empty operator const char8* () const {return text8 ();} ///< Returns pointer to string of type char8 (no modification allowed) operator const char16* () const {return text16 ();} ///< Returns pointer to string of type char16(no modification allowed) inline tchar operator[] (short idx) const {return getChar (static_cast (idx));} ///< Returns character at 'idx' inline tchar operator[] (long idx) const {return getChar (static_cast (idx));} inline tchar operator[] (int idx) const {return getChar (static_cast (idx));} inline tchar operator[] (unsigned short idx) const {return getChar (idx);} inline tchar operator[] (unsigned long idx) const {return getChar (static_cast (idx));} inline tchar operator[] (unsigned int idx) const {return getChar (idx);} inline virtual const char8* text8 () const; ///< Returns pointer to string of type char8 inline virtual const char16* text16 () const; ///< Returns pointer to string of type char16 inline virtual const tchar* text () const; ///< Returns pointer to string of type tchar inline virtual const void* ptr () const {return buffer;} ///< Returns pointer to string of type void inline virtual char8 getChar8 (uint32 index) const; ///< Returns character of type char16 at 'index' inline virtual char16 getChar16 (uint32 index) const; ///< Returns character of type char8 at 'index' inline tchar getChar (uint32 index) const; ///< Returns character of type tchar at 'index' inline tchar getCharAt (uint32 index) const; ///< Returns character of type tchar at 'index', no conversion! bool testChar8 (uint32 index, char8 c) const; ///< Returns true if character is equal at position 'index' bool testChar16 (uint32 index, char16 c) const; inline bool testChar (uint32 index, char8 c) const {return testChar8 (index, c);} inline bool testChar (uint32 index, char16 c) const {return testChar16 (index, c);} bool extract (String& result, uint32 idx, int32 n = -1) const; ///< Get n characters long substring starting at index (n=-1: until end) int32 copyTo8 (char8* str, uint32 idx = 0, int32 n = -1) const; int32 copyTo16 (char16* str, uint32 idx = 0, int32 n = -1) const; int32 copyTo (tchar* str, uint32 idx = 0, int32 n = -1) const; void copyTo (IStringResult* result) const; ///< Copies whole member string void copyTo (IString& string) const; ///< Copies whole member string inline uint32 hash (uint32 tsize) const { return isWide ? hashString16 (buffer16, tsize) : hashString8 (buffer8, tsize) ; } //------------------------------------------------------------------------- // compare ---------------------------------------------------------------- enum CompareMode { kCaseSensitive, ///< Comparison is done with regard to character's case kCaseInsensitive ///< Comparison is done without regard to character's case }; int32 compareAt (uint32 index, const ConstString& str, int32 n = -1, CompareMode m = kCaseSensitive) const; ///< Compare n characters of str with n characters of this starting at index (return: see above) int32 compare (const ConstString& str, int32 n, CompareMode m = kCaseSensitive) const; ///< Compare n characters of str with n characters of this (return: see above) int32 compare (const ConstString& str, CompareMode m = kCaseSensitive) const; ///< Compare all characters of str with this (return: see above) int32 naturalCompare (const ConstString& str, CompareMode mode = kCaseSensitive) const; bool startsWith (const ConstString& str, CompareMode m = kCaseSensitive) const; ///< Check if this starts with str bool endsWith (const ConstString& str, CompareMode m = kCaseSensitive) const; ///< Check if this ends with str bool contains (const ConstString& str, CompareMode m = kCaseSensitive) const; ///< Check if this contains str // static methods static bool isCharSpace (char8 character); ///< Returns true if character is a space static bool isCharSpace (char16 character); ///< @copydoc isCharSpace(const char8) static bool isCharAlpha (char8 character); ///< Returns true if character is an alphabetic character static bool isCharAlpha (char16 character); ///< @copydoc isCharAlpha(const char8) static bool isCharAlphaNum (char8 character); ///< Returns true if character is an alphanumeric character static bool isCharAlphaNum (char16 character); ///< @copydoc isCharAlphaNum(const char8) static bool isCharDigit (char8 character); ///< Returns true if character is a number static bool isCharDigit (char16 character); ///< @copydoc isCharDigit(const char8) static bool isCharAscii (char8 character); ///< Returns true if character is in ASCII range static bool isCharAscii (char16 character); ///< Returns true if character is in ASCII range static bool isCharUpper (char8 character); static bool isCharUpper (char16 character); static bool isCharLower (char8 character); static bool isCharLower (char16 character); //------------------------------------------------------------------------- /** @name Find first occurrence of n characters of str in this (n=-1: all) ending at endIndex (endIndex = -1: all)*/ ///@{ inline int32 findFirst (const ConstString& str, int32 n = -1, CompareMode m = kCaseSensitive, int32 endIndex = -1) const {return findNext (0, str, n, m, endIndex);} inline int32 findFirst (char8 c, CompareMode m = kCaseSensitive, int32 endIndex = -1) const {return findNext (0, c, m, endIndex);} inline int32 findFirst (char16 c, CompareMode m = kCaseSensitive, int32 endIndex = -1) const {return findNext (0, c, m, endIndex);} ///@} /** @name Find next occurrence of n characters of str starting at startIndex in this (n=-1: all) ending at endIndex (endIndex = -1: all)*/ ///@{ int32 findNext (int32 startIndex, const ConstString& str, int32 n = -1, CompareMode = kCaseSensitive, int32 endIndex = -1) const; int32 findNext (int32 startIndex, char8 c, CompareMode = kCaseSensitive, int32 endIndex = -1) const; int32 findNext (int32 startIndex, char16 c, CompareMode = kCaseSensitive, int32 endIndex = -1) const; ///@} /** @name Find previous occurrence of n characters of str starting at startIndex in this (n=-1: all) */ ///@{ int32 findPrev (int32 startIndex, const ConstString& str, int32 n = -1, CompareMode = kCaseSensitive) const; int32 findPrev (int32 startIndex, char8 c, CompareMode = kCaseSensitive) const; int32 findPrev (int32 startIndex, char16 c, CompareMode = kCaseSensitive) const; ///@} inline int32 findLast (const ConstString& str, int32 n = -1, CompareMode m = kCaseSensitive) const {return findPrev (-1, str, n, m);} ///< Find last occurrence of n characters of str in this (n=-1: all) inline int32 findLast (char8 c, CompareMode m = kCaseSensitive) const {return findPrev (-1, c, m);} inline int32 findLast (char16 c, CompareMode m = kCaseSensitive) const {return findPrev (-1, c, m);} int32 countOccurences (char8 c, uint32 startIndex, CompareMode = kCaseSensitive) const; ///< Counts occurences of c within this starting at index int32 countOccurences (char16 c, uint32 startIndex, CompareMode = kCaseSensitive) const; int32 getFirstDifferent (const ConstString& str, CompareMode = kCaseSensitive) const; ///< Returns position of first different character //------------------------------------------------------------------------- // numbers ---------------------------------------------------------------- bool isDigit (uint32 index) const; ///< Returns true if character at position is a digit bool scanFloat (double& value, uint32 offset = 0, bool scanToEnd = true) const; ///< Converts string to double value starting at offset bool scanInt64 (int64& value, uint32 offset = 0, bool scanToEnd = true) const; ///< Converts string to int64 value starting at offset bool scanUInt64 (uint64& value, uint32 offset = 0, bool scanToEnd = true) const; ///< Converts string to uint64 value starting at offset bool scanInt32 (int32& value, uint32 offset = 0, bool scanToEnd = true) const; ///< Converts string to int32 value starting at offset bool scanUInt32 (uint32& value, uint32 offset = 0, bool scanToEnd = true) const; ///< Converts string to uint32 value starting at offset bool scanHex (uint8& value, uint32 offset = 0, bool scanToEnd = true) const; ///< Converts string to hex/uint8 value starting at offset int32 getTrailingNumberIndex (uint32 width = 0) const; ///< Returns start index of trailing number int64 getTrailingNumber (int64 fallback = 0) const; ///< Returns result of scanInt64 or the fallback int64 getNumber () const; ///< Returns result of scanInt64 // static methods static bool scanInt64_8 (const char8* text, int64& value, bool scanToEnd = true); ///< Converts string of type char8 to int64 value static bool scanInt64_16 (const char16* text, int64& value, bool scanToEnd = true); ///< Converts string of type char16 to int64 value static bool scanInt64 (const tchar* text, int64& value, bool scanToEnd = true); ///< Converts string of type tchar to int64 value static bool scanUInt64_8 (const char8* text, uint64& value, bool scanToEnd = true); ///< Converts string of type char8 to uint64 value static bool scanUInt64_16 (const char16* text, uint64& value, bool scanToEnd = true); ///< Converts string of type char16 to uint64 value static bool scanUInt64 (const tchar* text, uint64& value, bool scanToEnd = true); ///< Converts string of type tchar to uint64 value static bool scanInt32_8 (const char8* text, int32& value, bool scanToEnd = true); ///< Converts string of type char8 to int32 value static bool scanInt32_16 (const char16* text, int32& value, bool scanToEnd = true); ///< Converts string of type char16 to int32 value static bool scanInt32 (const tchar* text, int32& value, bool scanToEnd = true); ///< Converts string of type tchar to int32 value static bool scanUInt32_8 (const char8* text, uint32& value, bool scanToEnd = true); ///< Converts string of type char8 to int32 value static bool scanUInt32_16 (const char16* text, uint32& value, bool scanToEnd = true); ///< Converts string of type char16 to int32 value static bool scanUInt32 (const tchar* text, uint32& value, bool scanToEnd = true); ///< Converts string of type tchar to int32 value static bool scanHex_8 (const char8* text, uint8& value, bool scanToEnd = true); ///< Converts string of type char8 to hex/unit8 value static bool scanHex_16 (const char16* text, uint8& value, bool scanToEnd = true); ///< Converts string of type char16 to hex/unit8 value static bool scanHex (const tchar* text, uint8& value, bool scanToEnd = true); ///< Converts string of type tchar to hex/unit8 value //------------------------------------------------------------------------- // conversion ------------------------------------------------------------- void toVariant (FVariant& var) const; static char8 toLower (char8 c); ///< Converts to lower case static char8 toUpper (char8 c); ///< Converts to upper case static char16 toLower (char16 c); static char16 toUpper (char16 c); static int32 multiByteToWideString (char16* dest, const char8* source, int32 wcharCount, uint32 sourceCodePage = kCP_Default); ///< If dest is zero, this returns the maximum number of bytes needed to convert source static int32 wideStringToMultiByte (char8* dest, const char16* source, int32 char8Count, uint32 destCodePage = kCP_Default); ///< If dest is zero, this returns the maximum number of bytes needed to convert source bool isWideString () const {return isWide != 0;} ///< Returns true if string is wide bool isAsciiString () const; ///< Checks if all characters in string are in ascii range bool isNormalized (UnicodeNormalization = kUnicodeNormC); ///< On PC only kUnicodeNormC is working #if SMTG_OS_WINDOWS ConstString (const wchar_t* str, int32 length = -1) : ConstString (wscast (str), length) {} operator const wchar_t* () const { return wscast (text16 ());} #endif #if SMTG_OS_MACOS virtual void* toCFStringRef (uint32 encoding = 0xFFFF, bool mutableCFString = false) const; ///< CFString conversion #endif //------------------------------------------------------------------------- //----------------------------------------------------------------------------- protected: union { void* buffer; char8* buffer8; char16* buffer16; }; uint32 len : 30; uint32 isWide : 1; }; //----------------------------------------------------------------------------- /** String. @ingroup adt Extends class ConstString by operations which allow modifications. If allocated externally and passed in via take() or extracted via pass(), memory is expected to be allocated with C's malloc() (rather than new) and deallocated with free(). Use the NEWSTR8/16 and DELETESTR8/16 macros below. \see ConstString */ //----------------------------------------------------------------------------- class String : public ConstString { public: //----------------------------------------------------------------------------- String (); String (const char8* str, MBCodePage codepage, int32 n = -1, bool isTerminated = true); ///< assign n characters of str and convert to wide string by using the specified codepage String (const char8* str, int32 n = -1, bool isTerminated = true); ///< assign n characters of str (-1: all) String (const char16* str, int32 n = -1, bool isTerminated = true); ///< assign n characters of str (-1: all) String (const String& str, int32 n = -1); ///< assign n characters of str (-1: all) String (const ConstString& str, int32 n = -1); ///< assign n characters of str (-1: all) String (const FVariant& var); ///< assign from FVariant String (IString* str); ///< assign from IString ~String () SMTG_OVERRIDE; #if SMTG_CPP11_STDLIBSUPPORT String (String&& str); String& operator= (String&& str); #endif // access------------------------------------------------------------------ void updateLength (); ///< Call this when the string is truncated outside (not recommended though) const char8* text8 () const SMTG_OVERRIDE; const char16* text16 () const SMTG_OVERRIDE; char8 getChar8 (uint32 index) const SMTG_OVERRIDE; char16 getChar16 (uint32 index) const SMTG_OVERRIDE; bool setChar8 (uint32 index, char8 c); bool setChar16 (uint32 index, char16 c); inline bool setChar (uint32 index, char8 c) {return setChar8 (index, c);} inline bool setChar (uint32 index, char16 c) {return setChar16 (index, c);} //------------------------------------------------------------------------- // assignment-------------------------------------------------------------- String& operator= (const char8* str) {return assign (str);} ///< Assign from a string of type char8 String& operator= (const char16* str) {return assign (str);} String& operator= (const ConstString& str) {return assign (str);} String& operator= (const String& str) {return assign (str);} String& operator= (char8 c) {return assign (c);} String& operator= (char16 c) {return assign (c);} String& assign (const ConstString& str, int32 n = -1); ///< Assign n characters of str (-1: all) String& assign (const char8* str, int32 n = -1, bool isTerminated = true); ///< Assign n characters of str (-1: all) String& assign (const char16* str, int32 n = -1, bool isTerminated = true); ///< Assign n characters of str (-1: all) String& assign (char8 c, int32 n = 1); String& assign (char16 c, int32 n = 1); //------------------------------------------------------------------------- // concat------------------------------------------------------------------ String& append (const ConstString& str, int32 n = -1); ///< Append n characters of str to this (n=-1: all) String& append (const char8* str, int32 n = -1); ///< Append n characters of str to this (n=-1: all) String& append (const char16* str, int32 n = -1); ///< Append n characters of str to this (n=-1: all) String& append (const char8 c, int32 n = 1); ///< Append char c n times String& append (const char16 c, int32 n = 1); ///< Append char c n times String& insertAt (uint32 idx, const ConstString& str, int32 n = -1); ///< Insert n characters of str at position idx (n=-1: all) String& insertAt (uint32 idx, const char8* str, int32 n = -1); ///< Insert n characters of str at position idx (n=-1: all) String& insertAt (uint32 idx, const char16* str, int32 n = -1); ///< Insert n characters of str at position idx (n=-1: all) String& insertAt (uint32 idx, char8 c) {char8 str[] = {c, 0}; return insertAt (idx, str, 1);} String& insertAt (uint32 idx, char16 c) {char16 str[] = {c, 0}; return insertAt (idx, str, 1);} String& operator+= (const String& str) {return append (str);} String& operator+= (const ConstString& str) {return append (str);} String& operator+= (const char8* str) {return append (str);} String& operator+= (const char16* str) {return append (str);} String& operator+= (const char8 c) {return append (c);} String& operator+= (const char16 c) {return append (c);} //------------------------------------------------------------------------- // replace----------------------------------------------------------------- String& replace (uint32 idx, int32 n1, const ConstString& str, int32 n2 = -1); ///< Replace n1 characters of this (starting at idx) with n2 characters of str (n1,n2=-1: until end) String& replace (uint32 idx, int32 n1, const char8* str, int32 n2 = -1); ///< Replace n1 characters of this (starting at idx) with n2 characters of str (n1,n2=-1: until end) String& replace (uint32 idx, int32 n1, const char16* str, int32 n2 = -1); ///< Replace n1 characters of this (starting at idx) with n2 characters of str (n1,n2=-1: until end) int32 replace (const char8* toReplace, const char8* toReplaceWith, bool all = false, CompareMode m = kCaseSensitive); ///< Replace find string with replace string - returns number of replacements int32 replace (const char16* toReplace, const char16* toReplaceWith, bool all = false, CompareMode m = kCaseSensitive); ///< Replace find string with replace string - returns number of replacements bool replaceChars8 (const char8* toReplace, char8 toReplaceBy); ///< Returns true when any replacement was done bool replaceChars16 (const char16* toReplace, char16 toReplaceBy); inline bool replaceChars8 (char8 toReplace, char8 toReplaceBy) {char8 str[] = {toReplace, 0}; return replaceChars8 (str, toReplaceBy);} inline bool replaceChars16 (char16 toReplace, char16 toReplaceBy) {char16 str[] = {toReplace, 0}; return replaceChars16 (str, toReplaceBy);} inline bool replaceChars (char8 toReplace, char8 toReplaceBy) {return replaceChars8 (toReplace, toReplaceBy);} inline bool replaceChars (char16 toReplace, char16 toReplaceBy) {return replaceChars16 (toReplace, toReplaceBy);} inline bool replaceChars (const char8* toReplace, char8 toReplaceBy) {return replaceChars8 (toReplace, toReplaceBy);} inline bool replaceChars (const char16* toReplace, char16 toReplaceBy) {return replaceChars16 (toReplace, toReplaceBy);} //------------------------------------------------------------------------- // remove------------------------------------------------------------------ String& remove (uint32 index = 0, int32 n = -1); ///< Remove n characters from string starting at index (n=-1: until end) enum CharGroup {kSpace, kNotAlphaNum, kNotAlpha}; bool trim (CharGroup mode = kSpace); ///< Trim lead/trail. void removeChars (CharGroup mode = kSpace); ///< Removes all of group. bool removeChars8 (const char8* which); ///< Remove all occurrences of each char in 'which' bool removeChars16 (const char16* which); ///< Remove all occurrences of each char in 'which' inline bool removeChars8 (const char8 which) {char8 str[] = {which, 0}; return removeChars8 (str); } inline bool removeChars16 (const char16 which) {char16 str[] = {which, 0}; return removeChars16 (str); } inline bool removeChars (const char8* which) {return removeChars8 (which);} inline bool removeChars (const char16* which) {return removeChars16 (which);} inline bool removeChars (const char8 which) {return removeChars8 (which);} inline bool removeChars (const char16 which) {return removeChars16 (which);} bool removeSubString (const ConstString& subString, bool allOccurences = true); //------------------------------------------------------------------------- // print------------------------------------------------------------------- String& printf (const char8* format, ...); ///< Print formatted data into string String& printf (const char16* format, ...); ///< Print formatted data into string String& vprintf (const char8* format, va_list args); String& vprintf (const char16* format, va_list args); //------------------------------------------------------------------------- // numbers----------------------------------------------------------------- String& printInt64 (int64 value); /** * @brief print a float into a string, trailing zeros will be trimmed * @param value the floating value to be printed * @param maxPrecision (optional) the max precision allowed for this, num of significant digits after the comma * For instance printFloat (1.234, 2) => 1.23 * @return the resulting string. */ String& printFloat (double value, uint32 maxPrecision = 6); /** Increment the trailing number if present else start with minNumber, width specifies the string width format (width 2 for number 3 is 03), applyOnlyFormat set to true will only format the string to the given width without incrementing the founded trailing number */ bool incrementTrailingNumber (uint32 width = 2, tchar separator = STR (' '), uint32 minNumber = 1, bool applyOnlyFormat = false); //------------------------------------------------------------------------- // conversion-------------------------------------------------------------- bool fromVariant (const FVariant& var); ///< Assigns string from FVariant void toVariant (FVariant& var) const; bool fromAttributes (IAttributes* a, IAttrID attrID); ///< Assigns string from FAttributes bool toAttributes (IAttributes* a, IAttrID attrID); void swapContent (String& s); ///< Swaps ownership of the strings pointed to void take (String& str); ///< Take ownership of the string of 'str' void take (void* _buffer, bool wide); ///< Take ownership of buffer void* pass (); void passToVariant (FVariant& var); ///< Pass ownership of buffer to Variant - sets Variant ownership void toLower (uint32 index); ///< Lower case the character. void toLower (); ///< Lower case the string. void toUpper (uint32 index); ///< Upper case the character. void toUpper (); ///< Upper case the string. unsigned char* toPascalString (unsigned char* buf); ///< Pascal string conversion const String& fromPascalString (const unsigned char* buf); ///< Pascal string conversion bool toWideString (uint32 sourceCodePage = kCP_Default); ///< Converts to wide string according to sourceCodePage bool toMultiByte (uint32 destCodePage = kCP_Default); void fromUTF8 (const char8* utf8String); ///< Assigns from UTF8 string bool normalize (UnicodeNormalization = kUnicodeNormC); ///< On PC only kUnicodeNormC is working #if SMTG_OS_WINDOWS String (const wchar_t* str, int32 length = -1, bool isTerminated = true) : String (wscast (str), length, isTerminated) {} String& operator= (const wchar_t* str) {return String::operator= (wscast (str)); } #endif #if SMTG_OS_MACOS virtual bool fromCFStringRef (const void*, uint32 encoding = 0xFFFF); ///< CFString conversion #endif //------------------------------------------------------------------------- //----------------------------------------------------------------------------- protected: bool resize (uint32 newSize, bool wide, bool fill = false); private: bool _toWideString (const char8* src, int32 length, uint32 sourceCodePage = kCP_Default); void tryFreeBuffer (); bool checkToMultiByte (uint32 destCodePage = kCP_Default) const; // to remove debug code from inline - const_cast inside!!! }; // String memory allocation macros #define NEWSTR8(len) ((char8*)::malloc(len)) // len includes trailing zero #define NEWSTR16(len) ((char16*)::malloc(2*(len))) #define DELETESTR8(p) (::free((char*)(p))) // cast to strip const #define DELETESTR16(p) (::free((char16*)(p))) // String concatenation functions. inline String operator+ (const ConstString& s1, const ConstString& s2) {return String (s1).append (s2);} inline String operator+ (const ConstString& s1, const char8* s2) {return String (s1).append (s2);} inline String operator+ (const ConstString& s1, const char16* s2) {return String (s1).append (s2);} inline String operator+ (const char8* s1, const ConstString& s2) {return String (s1).append (s2);} inline String operator+ (const char16* s1, const ConstString& s2) {return String (s1).append (s2);} inline String operator+ (const ConstString& s1, const String& s2) {return String (s1).append (s2);} inline String operator+ (const String& s1, const ConstString& s2) {return String (s1).append (s2);} inline String operator+ (const String& s1, const String& s2) {return String (s1).append (s2);} inline String operator+ (const String& s1, const char8* s2) {return String (s1).append (s2);} inline String operator+ (const String& s1, const char16* s2) {return String (s1).append (s2);} inline String operator+ (const char8* s1, const String& s2) {return String (s1).append (s2);} inline String operator+ (const char16* s1, const String& s2) {return String (s1).append (s2);} //----------------------------------------------------------------------------- // ConstString //----------------------------------------------------------------------------- inline const tchar* ConstString::text () const { #ifdef UNICODE return text16 (); #else return text8 (); #endif } //----------------------------------------------------------------------------- inline const char8* ConstString::text8 () const { return (!isWide && buffer8) ? buffer8: kEmptyString8; } //----------------------------------------------------------------------------- inline const char16* ConstString::text16 () const { return (isWide && buffer16) ? buffer16 : kEmptyString16; } //----------------------------------------------------------------------------- inline char8 ConstString::getChar8 (uint32 index) const { if (index < len && buffer8 && !isWide) return buffer8[index]; return 0; } //----------------------------------------------------------------------------- inline char16 ConstString::getChar16 (uint32 index) const { if (index < len && buffer16 && isWide) return buffer16[index]; return 0; } //----------------------------------------------------------------------------- inline tchar ConstString::getChar (uint32 index) const { #ifdef UNICODE return getChar16 (index); #else return getChar8 (index); #endif } //----------------------------------------------------------------------------- inline tchar ConstString::getCharAt (uint32 index) const { #ifdef UNICODE if (isWide) return getChar16 (index); #endif return static_cast (getChar8 (index)); } //----------------------------------------------------------------------------- inline int64 ConstString::getNumber () const { int64 tmp = 0; scanInt64 (tmp); return tmp; } //----------------------------------------------------------------------------- inline bool ConstString::scanInt32_8 (const char8* text, int32& value, bool scanToEnd) { int64 tmp; if (scanInt64_8 (text, tmp, scanToEnd)) { value = (int32)tmp; return true; } return false; } //----------------------------------------------------------------------------- inline bool ConstString::scanInt32_16 (const char16* text, int32& value, bool scanToEnd) { int64 tmp; if (scanInt64_16 (text, tmp, scanToEnd)) { value = (int32)tmp; return true; } return false; } //----------------------------------------------------------------------------- inline bool ConstString::scanInt32 (const tchar* text, int32& value, bool scanToEnd) { int64 tmp; if (scanInt64 (text, tmp, scanToEnd)) { value = (int32)tmp; return true; } return false; } //----------------------------------------------------------------------------- inline bool ConstString::scanUInt32_8 (const char8* text, uint32& value, bool scanToEnd) { uint64 tmp; if (scanUInt64_8 (text, tmp, scanToEnd)) { value = (uint32)tmp; return true; } return false; } //----------------------------------------------------------------------------- inline bool ConstString::scanUInt32_16 (const char16* text, uint32& value, bool scanToEnd) { uint64 tmp; if (scanUInt64_16 (text, tmp, scanToEnd)) { value = (uint32)tmp; return true; } return false; } //----------------------------------------------------------------------------- inline bool ConstString::scanUInt32 (const tchar* text, uint32& value, bool scanToEnd) { uint64 tmp; if (scanUInt64 (text, tmp, scanToEnd)) { value = (uint32)tmp; return true; } return false; } //----------------------------------------------------------------------------- inline const char8* String::text8 () const { if (isWide && !isEmpty ()) checkToMultiByte (); // this should be avoided, since it can lead to information loss return ConstString::text8 (); } //----------------------------------------------------------------------------- inline const char16* String::text16 () const { if (!isWide && !isEmpty ()) { const_cast (*this).toWideString (); } return ConstString::text16 (); } //----------------------------------------------------------------------------- inline char8 String::getChar8 (uint32 index) const { if (isWide && !isEmpty ()) checkToMultiByte (); // this should be avoided, since it can lead to information loss return ConstString::getChar8 (index); } //----------------------------------------------------------------------------- inline char16 String::getChar16 (uint32 index) const { if (!isWide && !isEmpty ()) { const_cast (*this).toWideString (); } return ConstString::getChar16 (index); } //----------------------------------------------------------------------------- inline bool operator< (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) < 0) ? true : false;} inline bool operator<= (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) <= 0) ? true : false;} inline bool operator> (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) > 0) ? true : false;} inline bool operator>= (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) >= 0) ? true : false;} inline bool operator== (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) == 0) ? true : false;} inline bool operator!= (const ConstString& s1, const ConstString& s2) {return (s1.compare (s2) != 0) ? true : false;} inline bool operator< (const ConstString& s1, const char8* s2) {return (s1.compare (s2) < 0) ? true : false;} inline bool operator<= (const ConstString& s1, const char8* s2) {return (s1.compare (s2) <= 0) ? true : false;} inline bool operator> (const ConstString& s1, const char8* s2) {return (s1.compare (s2) > 0) ? true : false;} inline bool operator>= (const ConstString& s1, const char8* s2) {return (s1.compare (s2) >= 0) ? true : false;} inline bool operator== (const ConstString& s1, const char8* s2) {return (s1.compare (s2) == 0) ? true : false;} inline bool operator!= (const ConstString& s1, const char8* s2) {return (s1.compare (s2) != 0) ? true : false;} inline bool operator< (const char8* s1, const ConstString& s2) {return (s2.compare (s1) > 0) ? true : false;} inline bool operator<= (const char8* s1, const ConstString& s2) {return (s2.compare (s1) >= 0) ? true : false;} inline bool operator> (const char8* s1, const ConstString& s2) {return (s2.compare (s1) < 0) ? true : false;} inline bool operator>= (const char8* s1, const ConstString& s2) {return (s2.compare (s1) <= 0) ? true : false;} inline bool operator== (const char8* s1, const ConstString& s2) {return (s2.compare (s1) == 0) ? true : false;} inline bool operator!= (const char8* s1, const ConstString& s2) {return (s2.compare (s1) != 0) ? true : false;} inline bool operator< (const ConstString& s1, const char16* s2) {return (s1.compare (s2) < 0) ? true : false;} inline bool operator<= (const ConstString& s1, const char16* s2) {return (s1.compare (s2) <= 0) ? true : false;} inline bool operator> (const ConstString& s1, const char16* s2) {return (s1.compare (s2) > 0) ? true : false;} inline bool operator>= (const ConstString& s1, const char16* s2) {return (s1.compare (s2) >= 0) ? true : false;} inline bool operator== (const ConstString& s1, const char16* s2) {return (s1.compare (s2) == 0) ? true : false;} inline bool operator!= (const ConstString& s1, const char16* s2) {return (s1.compare (s2) != 0) ? true : false;} inline bool operator< (const char16* s1, const ConstString& s2) {return (s2.compare (s1) > 0) ? true : false;} inline bool operator<= (const char16* s1, const ConstString& s2) {return (s2.compare (s1) >= 0) ? true : false;} inline bool operator> (const char16* s1, const ConstString& s2) {return (s2.compare (s1) < 0) ? true : false;} inline bool operator>= (const char16* s1, const ConstString& s2) {return (s2.compare (s1) <= 0) ? true : false;} inline bool operator== (const char16* s1, const ConstString& s2) {return (s2.compare (s1) == 0) ? true : false;} inline bool operator!= (const char16* s1, const ConstString& s2) {return (s2.compare (s1) != 0) ? true : false;} // The following functions will only work with European Numbers! // (e.g. Arabic, Tibetan, and Khmer digits are not supported) extern int32 strnatcmp8 (const char8* s1, const char8* s2, bool caseSensitive = true); extern int32 strnatcmp16 (const char16* s1, const char16* s2, bool caseSensitive = true); inline int32 strnatcmp (const tchar* s1, const tchar* s2, bool caseSensitive = true) { #ifdef UNICODE return strnatcmp16 (s1, s2, caseSensitive); #else return strnatcmp8 (s1, s2, caseSensitive); #endif } //----------------------------------------------------------------------------- /** StringObject implements IStringResult and IString methods. It can therefore be exchanged with other Steinberg objects using one or both of these interfaces. \see String, ConstString */ //----------------------------------------------------------------------------- class StringObject : public FObject, public String, public IStringResult, public IString { public: //----------------------------------------------------------------------------- StringObject () {} StringObject (const char16* str, int32 n = -1, bool isTerminated = true) : String (str, n, isTerminated) {} StringObject (const char8* str, int32 n = -1, bool isTerminated = true) : String (str, n, isTerminated) {} StringObject (const StringObject& str, int32 n = -1) : String (str, n) {} StringObject (const String& str, int32 n = -1) : String (str, n) {} StringObject (const FVariant& var) : String (var) {} using String::operator=; // IStringResult ---------------------------------------------------------- void PLUGIN_API setText (const char8* text) SMTG_OVERRIDE; //------------------------------------------------------------------------- // IString----------------------------------------------------------------- void PLUGIN_API setText8 (const char8* text) SMTG_OVERRIDE; void PLUGIN_API setText16 (const char16* text) SMTG_OVERRIDE; const char8* PLUGIN_API getText8 () SMTG_OVERRIDE; const char16* PLUGIN_API getText16 () SMTG_OVERRIDE; void PLUGIN_API take (void* s, bool _isWide) SMTG_OVERRIDE; bool PLUGIN_API isWideString () const SMTG_OVERRIDE; //------------------------------------------------------------------------- OBJ_METHODS (StringObject, FObject) FUNKNOWN_METHODS2 (IStringResult, IString, FObject) }; //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/PaxHeaders/pluginterfaces0000644000000000000000000000013215124701742017143 xustar0030 mtime=1767080930.090106392 30 atime=1767080892.816263357 30 ctime=1767080930.090106392 qtractor-1.5.11/src/vst3/pluginterfaces/0000755000175000001440000000000015124701742017210 5ustar00rncbcusersqtractor-1.5.11/src/vst3/pluginterfaces/PaxHeaders/test0000644000000000000000000000012715124701711020122 xustar0029 mtime=1767080905.19378968 29 atime=1767080905.19378968 29 ctime=1767080905.19378968 qtractor-1.5.11/src/vst3/pluginterfaces/test/0000755000175000001440000000000015124701711020163 5ustar00rncbcusersqtractor-1.5.11/src/vst3/pluginterfaces/test/PaxHeaders/itest.h0000644000000000000000000000012715124701711021500 xustar0029 mtime=1767080905.19378968 29 atime=1767080905.19378968 29 ctime=1767080905.19378968 qtractor-1.5.11/src/vst3/pluginterfaces/test/itest.h0000644000175000001440000001332015124701711021463 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // Version : 1.0 // // Category : SDK Core Interfaces // Filename : pluginterfaces/test/itest.h // Created by : Steinberg, 01/2005 // Description : Test Interface - Availability Depends on HOST // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #ifndef kTestClass #define kTestClass "Test Class" ///< A class for automated tests #endif namespace Steinberg { class ITestResult; /** ------------------------------------------------------------------------ ITest interface declaration \defgroup TestClass Test Class: Interface for unit testing The SDK provides already some tests which can be executed by the validator. A plug-in has the possibility to add its own tests: (check public.sdk/source/vst/utility/testing.h) */ class ITest : public FUnknown { public: //--- --------------------------------------------------------------------- /** called immediately before the test is actually run. * Usually this will be used to setup the test environment. * \return true upon success */ virtual bool PLUGIN_API setup () = 0; /** execute the test. * \param testResult : points to a test result where the test can * (optionally) add an error message. * \return true upon success * \sa ITestResult */ virtual bool PLUGIN_API run (ITestResult* testResult) = 0; /** called after the test has run. This method shall be used to * deconstruct a test environment that has been setup with ITest::setup (). * \return true upon success */ virtual bool PLUGIN_API teardown () = 0; /** This function is used to provide information about the performed * testcase. What is done, what is validated and what has to be prepared * before executing the test (in case of half-automated tests). * \return null terminated string upon success, zero otherwise */ virtual const tchar* PLUGIN_API getDescription () { return nullptr; } //--- --------------------------------------------------------------------- static const FUID iid; }; #ifdef UNICODE DECLARE_CLASS_IID (ITest, 0xFE64FC19, 0x95684F53, 0xAAA78DC8, 0x7228338E) #else DECLARE_CLASS_IID (ITest, 0x9E2E608B, 0x64C64CF8, 0x839059BD, 0xA194032D) #endif //------------------------------------------------------------------------ // ITestResult interface declaration //------------------------------------------------------------------------ /** Test Result message logger [host imp] When a test is called, a pointer to an ITestResult is passed in, so the test class can output error messages */ class ITestResult : public FUnknown { public: //--- --------------------------------------------------------------------- /** add an error message */ virtual void PLUGIN_API addErrorMessage (const tchar* msg) = 0; /** add a message */ virtual void PLUGIN_API addMessage (const tchar* msg) = 0; //--- --------------------------------------------------------------------- static const FUID iid; }; #ifdef UNICODE DECLARE_CLASS_IID (ITestResult, 0x69796279, 0xF651418B, 0xB24D79B7, 0xD7C527F4) #else DECLARE_CLASS_IID (ITestResult, 0xCE13B461, 0x5334451D, 0xB3943E99, 0x7446885B) #endif //------------------------------------------------------------------------ // ITestSuite interface declaration //------------------------------------------------------------------------ /** A collection of tests supporting a hierarchical ordering [host imp] [create via hostclasses] */ class ITestSuite : public FUnknown { public: //--- --------------------------------------------------------------------- /** append a new test */ virtual tresult PLUGIN_API addTest (FIDString name, ITest* test) = 0; /** append an entire test suite as a child suite */ virtual tresult PLUGIN_API addTestSuite (FIDString name, ITestSuite* testSuite) = 0; virtual tresult PLUGIN_API setEnvironment (ITest* environment) = 0; //--- --------------------------------------------------------------------- static const FUID iid; }; #ifdef UNICODE DECLARE_CLASS_IID (ITestSuite, 0x5CA7106F, 0x98784AA5, 0xB4D30D71, 0x2F5F1498) #else DECLARE_CLASS_IID (ITestSuite, 0x81724C94, 0xE9F64F65, 0xACB104E9, 0xCC702253) #endif //------------------------------------------------------------------------ // ITestFactory interface declaration //------------------------------------------------------------------------ /** Class factory that any testable module defines for creating tests that will be executed from the host - [plug imp] */ class ITestFactory : public FUnknown { public: //--- --------------------------------------------------------------------- /** create the tests that this module provides. * \param context : * \param parentSuite : the test suite that the newly created tests * shall register with. */ virtual tresult PLUGIN_API createTests (FUnknown* context, ITestSuite* parentSuite) = 0; //--- --------------------------------------------------------------------- static const FUID iid; }; #ifdef UNICODE DECLARE_CLASS_IID (ITestFactory, 0xAB483D3A, 0x15264650, 0xBF86EEF6, 0x9A327A93) #else DECLARE_CLASS_IID (ITestFactory, 0xE77EA913, 0x58AA4838, 0x986A4620, 0x53579080) #endif //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/PaxHeaders/LICENSE.txt0000644000000000000000000000013215124701711021037 xustar0030 mtime=1767080905.191211245 30 atime=1767080905.191211245 30 ctime=1767080905.191211245 qtractor-1.5.11/src/vst3/pluginterfaces/LICENSE.txt0000644000175000001440000000235115124701711021030 0ustar00rncbcusers//----------------------------------------------------------------------------- MIT License Copyright (c) 2025, Steinberg Media Technologies GmbH Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //---------------------------------------------------------------------------------qtractor-1.5.11/src/vst3/pluginterfaces/PaxHeaders/vst0000644000000000000000000000013115124701711017752 xustar0030 mtime=1767080905.195211228 29 atime=1767080905.19378968 30 ctime=1767080905.195211228 qtractor-1.5.11/src/vst3/pluginterfaces/vst/0000755000175000001440000000000015124701711020020 5ustar00rncbcusersqtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstprocesscontext.h0000644000000000000000000000013215124701711024172 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstprocesscontext.h0000644000175000001440000001517215124701711024170 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstprocesscontext.h // Created by : Steinberg, 10/2005 // Description : VST Processing Context Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Frame Rate A frame rate describes the number of image (frame) displayed per second. Some examples: - 23.976 fps is framesPerSecond: 24 and flags: kPullDownRate - 24 fps is framesPerSecond: 24 and flags: 0 - 25 fps is framesPerSecond: 25 and flags: 0 - 29.97 drop fps is framesPerSecond: 30 and flags: kDropRate|kPullDownRate - 29.97 fps is framesPerSecond: 30 and flags: kPullDownRate - 30 fps is framesPerSecond: 30 and flags: 0 - 30 drop fps is framesPerSecond: 30 and flags: kDropRate - 50 fps is framesPerSecond: 50 and flags: 0 - 59.94 fps is framesPerSecond: 60 and flags: kPullDownRate - 60 fps is framesPerSecond: 60 and flags: 0 */ struct FrameRate { //------------------------------------------------------------------------ enum FrameRateFlags { kPullDownRate = 1 << 0, kDropRate = 1 << 1 }; uint32 framesPerSecond; ///< frame rate uint32 flags; ///< flags #FrameRateFlags //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ /** Description of a chord. A chord is described with a key note, a root note and the \copydoc chordMask \see ProcessContext */ struct Chord { //------------------------------------------------------------------------ uint8 keyNote; ///< key note in chord uint8 rootNote; ///< lowest note in chord /** Bitmask of a chord. \n 1st bit set: minor second; 2nd bit set: major second, and so on. \n There is \b no bit for the keynote (root of the chord) because it is inherently always present. \n Examples: - XXXX 0000 0100 1000 (= 0x0048) -> major chord - XXXX 0000 0100 0100 (= 0x0044) -> minor chord - XXXX 0010 0100 0100 (= 0x0244) -> minor chord with minor seventh */ int16 chordMask; enum Masks { kChordMask = 0x0FFF, ///< mask for chordMask kReservedMask = 0xF000 ///< reserved for future use }; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ /** Audio processing context. For each processing block the host provides timing information and musical parameters that can change over time. For a host that supports jumps (like cycle) it is possible to split up a processing block into multiple parts in order to provide a correct project time inside of every block, but this behavior is not mandatory. Since the timing will be correct at the beginning of the next block again, a host that is dependent on a fixed processing block size can choose to neglect this problem. \see IAudioProcessor, ProcessData */ struct ProcessContext { //------------------------------------------------------------------------ /** Transport state & other flags */ enum StatesAndFlags { kPlaying = 1 << 1, ///< currently playing kCycleActive = 1 << 2, ///< cycle is active kRecording = 1 << 3, ///< currently recording kSystemTimeValid = 1 << 8, ///< systemTime contains valid information kContTimeValid = 1 << 17, ///< continousTimeSamples contains valid information kProjectTimeMusicValid = 1 << 9,///< projectTimeMusic contains valid information kBarPositionValid = 1 << 11, ///< barPositionMusic contains valid information kCycleValid = 1 << 12, ///< cycleStartMusic and barPositionMusic contain valid information kTempoValid = 1 << 10, ///< tempo contains valid information kTimeSigValid = 1 << 13, ///< timeSigNumerator and timeSigDenominator contain valid information kChordValid = 1 << 18, ///< chord contains valid information kSmpteValid = 1 << 14, ///< smpteOffset and frameRate contain valid information kClockValid = 1 << 15 ///< samplesToNextClock valid }; uint32 state; ///< a combination of the values from \ref StatesAndFlags double sampleRate; ///< current sample rate (always valid) TSamples projectTimeSamples; ///< project time in samples (always valid) int64 systemTime; ///< system time in nanoseconds (optional) TSamples continousTimeSamples; ///< project time, without loop (optional) TQuarterNotes projectTimeMusic; ///< musical position in quarter notes (1.0 equals 1 quarter note) (optional) TQuarterNotes barPositionMusic; ///< last bar start position, in quarter notes (optional) TQuarterNotes cycleStartMusic; ///< cycle start in quarter notes (optional) TQuarterNotes cycleEndMusic; ///< cycle end in quarter notes (optional) double tempo; ///< tempo in BPM (Beats Per Minute) (optional) int32 timeSigNumerator; ///< time signature numerator (e.g. 3 for 3/4) (optional) int32 timeSigDenominator; ///< time signature denominator (e.g. 4 for 3/4) (optional) Chord chord; ///< musical info (optional) int32 smpteOffsetSubframes; ///< SMPTE (sync) offset in subframes (1/80 of frame) (optional) FrameRate frameRate; ///< frame rate (optional) int32 samplesToNextClock; ///< MIDI Clock Resolution (24 Per Quarter Note), can be negative (nearest) (optional) //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstmidimapping2.h0000644000000000000000000000013215124701711023467 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstmidimapping2.h0000644000175000001440000001467215124701711023471 0ustar00rncbcusers//------------------------------------------------------------------------ // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstmidimapping2.h // Created by : Steinberg, 10/2025 // Description : MIDI controller mapping (includes MIDI 2.0) // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstcomponent.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { using MidiGroup = uint8; using MidiChannel = uint8; using BusIndex = int32; //------------------------------------------------------------------------ /** Midi2Controller * * describes a MIDI 2.0 Registered or Assignable Controller * */ struct Midi2Controller { uint8 bank : 7; // msb TBool registered : 1; // true: registered, false: assignable uint8 index : 7; // lsb TBool reserved : 1; }; //------------------------------------------------------------------------ struct Midi2ControllerParamIDAssignment { ParamID pId; BusIndex busIndex; MidiChannel channel; Midi2Controller controller; }; //------------------------------------------------------------------------ struct Midi2ControllerParamIDAssignmentList { uint32 count; Midi2ControllerParamIDAssignment* map; }; //------------------------------------------------------------------------ struct Midi1ControllerParamIDAssignment { ParamID pId; BusIndex busIndex; MidiChannel channel; CtrlNumber controller; }; //------------------------------------------------------------------------ struct Midi1ControllerParamIDAssignmentList { uint32 count; Midi1ControllerParamIDAssignment* map; }; //------------------------------------------------------------------------ /** MIDI Mapping interface: Vst::IMidiMapping2 \ingroup vstIPlug vst380 - [plug imp] - [extends IEditController] - [released: 3.8.0] - [optional] - [replaces Vst::IMidiMapping] This interface replaces Vst::IMidiMapping to support the extended MIDI controllers in MIDI 2.0. A MIDI 2.0 capable host first queries for the Vst::IMidiMapping2 interface and uses the old Vst::IMidiMapping interface as a fallback. A plug-in can use the Vst::IPlugInterfaceSupport to check if the host supports Vst::IMidiMapping2. */ class IMidiMapping2 : public FUnknown { public: /** Gets the number of MIDI 2.0 controller to parameter assignments * * @param direction input/output direction * @return number of MIDI 2.0 controller to parameter assignments * \note [UI-thread & Connected] */ virtual uint32 PLUGIN_API getNumMidi2ControllerAssignments (BusDirections direction) = 0; /** Gets MIDI 2.0 controller parameter assignments * * the list is preallocated by the host and must be filled by the plug-in * * @param direction input/output direction * @param list list of assignments * @return kResultTrue on success * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getMidi2ControllerAssignments ( BusDirections direction, const Midi2ControllerParamIDAssignmentList& list) = 0; /** Gets the number of MIDI 1.0 controller to parameter assignments * * @param direction input/output direction * @return number of MIDI 1.0 controller to parameter assignments * \note [UI-thread & Connected] */ virtual uint32 PLUGIN_API getNumMidi1ControllerAssignments (BusDirections direction) = 0; /** Gets MIDI 1.0 controller parameter assignments * * the list is preallocated by the host and must be filled by the plug-in * * @param direction input/output direction * @param list list of assignments * @return kResultTrue on success * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getMidi1ControllerAssignments ( BusDirections direction, const Midi1ControllerParamIDAssignmentList& list) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IMidiMapping2, 0x6DE14B88, 0x03F94F09, 0xA2552F0F, 0x9326593E) //------------------------------------------------------------------------ /** MIDI Learn interface: Vst::IMidiLearn2 \ingroup vstIPlug vst380 - [plug imp] - [extends IEditController] - [released: 3.8.0] - [optional] - [replaces Vst::IMidiLearn] If this interface is implemented by the edit controller, the host will call this method whenever there is live MIDI-CC input for the plug-in. This way, the plug-in can change its MIDI-CC parameter mapping and notify the host using IComponentHandler::restartComponent with the kMidiCCAssignmentChanged flag. Use this if you want to implement custom MIDI-Learn functionality in your plug-in. */ class IMidiLearn2 : public FUnknown { public: /** Called on live input MIDI 2.0-CC change associated to a given bus index and MIDI channel * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API onLiveMidi2ControllerInput (BusIndex index, MidiChannel channel, Midi2Controller midiCC) = 0; /** Called on live input MIDI 1.0-CC change associated to a given bus index and MIDI channel * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API onLiveMidi1ControllerInput (BusIndex index, MidiChannel channel, CtrlNumber midiCC) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IMidiLearn2, 0xF07E498A, 0x78864327, 0x8B431CED, 0xA3C553FC) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstaudioprocessor.h0000644000000000000000000000013115124701711024147 xustar0030 mtime=1767080905.194000456 29 atime=1767080905.19378968 30 ctime=1767080905.194000456 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstaudioprocessor.h0000644000175000001440000005710715124701711024152 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstaudioprocessor.h // Created by : Steinberg, 10/2005 // Description : VST Audio Processing Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "ivstcomponent.h" #include "vstspeaker.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ /** Class Category Name for Audio Processor Component */ //------------------------------------------------------------------------ #ifndef kVstAudioEffectClass #define kVstAudioEffectClass "Audio Module Class" #endif //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { class IEventList; class IParameterChanges; struct ProcessContext; //------------------------------------------------------------------------ /** Component Types used as subCategories in PClassInfo2 */ namespace PlugType { /** * \defgroup plugType Plug-in Type used for subCategories * @{ */ SMTG_CONSTEXPR const CString kFx = "Fx"; ///< others type (not categorized) SMTG_CONSTEXPR const CString kFxAnalyzer = "Fx|Analyzer"; ///< Scope, FFT-Display, Loudness Processing... SMTG_CONSTEXPR const CString kFxBass = "Fx|Bass"; ///< Tools dedicated to Bass Guitar SMTG_CONSTEXPR const CString kFxChannelStrip = "Fx|Channel Strip"; ///< Tools dedicated to Channel Strip SMTG_CONSTEXPR const CString kFxDelay = "Fx|Delay"; ///< Delay, Multi-tap Delay, Ping-Pong Delay... SMTG_CONSTEXPR const CString kFxDistortion = "Fx|Distortion"; ///< Amp Simulator, Sub-Harmonic, SoftClipper... SMTG_CONSTEXPR const CString kFxDrums = "Fx|Drums"; ///< Tools dedicated to Drums... SMTG_CONSTEXPR const CString kFxDynamics = "Fx|Dynamics"; ///< Compressor, Expander, Gate, Limiter, Maximizer, Tape Simulator, EnvelopeShaper... SMTG_CONSTEXPR const CString kFxEQ = "Fx|EQ"; ///< Equalization, Graphical EQ... SMTG_CONSTEXPR const CString kFxFilter = "Fx|Filter"; ///< WahWah, ToneBooster, Specific Filter,... SMTG_CONSTEXPR const CString kFxGenerator = "Fx|Generator"; ///< Tone Generator, Noise Generator... SMTG_CONSTEXPR const CString kFxGuitar = "Fx|Guitar"; ///< Tools dedicated to Guitar SMTG_CONSTEXPR const CString kFxInstrument = "Fx|Instrument"; ///< Fx which could be loaded as Instrument too SMTG_CONSTEXPR const CString kFxInstrumentExternal = "Fx|Instrument|External"; ///< Fx which could be loaded as Instrument too and is external (wrapped Hardware) SMTG_CONSTEXPR const CString kFxMastering = "Fx|Mastering"; ///< Dither, Noise Shaping,... SMTG_CONSTEXPR const CString kFxMicrophone = "Fx|Microphone"; ///< Tools dedicated to Microphone SMTG_CONSTEXPR const CString kFxModulation = "Fx|Modulation"; ///< Phaser, Flanger, Chorus, Tremolo, Vibrato, AutoPan, Rotary, Cloner... SMTG_CONSTEXPR const CString kFxNetwork = "Fx|Network"; ///< using Network SMTG_CONSTEXPR const CString kFxPitchShift = "Fx|Pitch Shift"; ///< Pitch Processing, Pitch Correction, Vocal Tuning... SMTG_CONSTEXPR const CString kFxRestoration = "Fx|Restoration"; ///< Denoiser, Declicker,... SMTG_CONSTEXPR const CString kFxReverb = "Fx|Reverb"; ///< Reverberation, Room Simulation, Convolution Reverb... SMTG_CONSTEXPR const CString kFxSpatial = "Fx|Spatial"; ///< MonoToStereo, StereoEnhancer,... SMTG_CONSTEXPR const CString kFxSurround = "Fx|Surround"; ///< dedicated to surround processing: LFE Splitter, Bass Manager... SMTG_CONSTEXPR const CString kFxTools = "Fx|Tools"; ///< Volume, Mixer, Tuner... SMTG_CONSTEXPR const CString kFxVocals = "Fx|Vocals"; ///< Tools dedicated to Vocals SMTG_CONSTEXPR const CString kInstrument = "Instrument"; ///< Effect used as instrument (sound generator), not as insert SMTG_CONSTEXPR const CString kInstrumentDrum = "Instrument|Drum"; ///< Instrument for Drum sounds SMTG_CONSTEXPR const CString kInstrumentExternal = "Instrument|External";///< External Instrument (wrapped Hardware) SMTG_CONSTEXPR const CString kInstrumentPiano = "Instrument|Piano"; ///< Instrument for Piano sounds SMTG_CONSTEXPR const CString kInstrumentSampler = "Instrument|Sampler"; ///< Instrument based on Samples SMTG_CONSTEXPR const CString kInstrumentSynth = "Instrument|Synth"; ///< Instrument based on Synthesis SMTG_CONSTEXPR const CString kInstrumentSynthSampler = "Instrument|Synth|Sampler"; ///< Instrument based on Synthesis and Samples SMTG_CONSTEXPR const CString kAmbisonics = "Ambisonics"; ///< used for Ambisonics channel (FX or Panner/Mixconverter/Up-Mixer/Down-Mixer when combined with other category) SMTG_CONSTEXPR const CString kAnalyzer = "Analyzer"; ///< Meter, Scope, FFT-Display, not selectable as insert plug-in SMTG_CONSTEXPR const CString kNoOfflineProcess = "NoOfflineProcess"; ///< will be NOT used for plug-in offline processing (will work as normal insert plug-in) SMTG_CONSTEXPR const CString kOnlyARA = "OnlyARA"; ///< used for plug-ins that require ARA to operate (will not work as normal insert plug-in) SMTG_CONSTEXPR const CString kOnlyOfflineProcess = "OnlyOfflineProcess"; ///< used for plug-in offline processing (will not work as normal insert plug-in) SMTG_CONSTEXPR const CString kOnlyRealTime = "OnlyRT"; ///< indicates that it supports only realtime process call, no processing faster than realtime SMTG_CONSTEXPR const CString kSpatial = "Spatial"; ///< used for SurroundPanner SMTG_CONSTEXPR const CString kSpatialFx = "Spatial|Fx"; ///< used for SurroundPanner and as insert effect SMTG_CONSTEXPR const CString kUpDownMix = "Up-Downmix"; ///< used for Mixconverter/Up-Mixer/Down-Mixer SMTG_CONSTEXPR const CString kMono = "Mono"; ///< used for Mono only plug-in [optional] SMTG_CONSTEXPR const CString kStereo = "Stereo"; ///< used for Stereo only plug-in [optional] SMTG_CONSTEXPR const CString kSurround = "Surround"; ///< used for Surround only plug-in [optional] /**@}*/ } //------------------------------------------------------------------------ /** Component Flags used as classFlags in PClassInfo2 */ enum ComponentFlags { //------------------------------------------------------------------------ kDistributable = 1 << 0, ///< Component can be run on remote computer kSimpleModeSupported = 1 << 1 ///< Component supports simple IO mode (or works in simple mode anyway) see \ref vst3IoMode //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ /** Symbolic sample size. * \see ProcessSetup, ProcessData */ enum SymbolicSampleSizes { kSample32, ///< 32-bit precision kSample64 ///< 64-bit precision }; //------------------------------------------------------------------------ /** Processing mode informs the plug-in about the context and at which frequency the process call is * called. * . * VST3 defines 3 modes: * * - kRealtime: each process call is called at a realtime frequency (defined by [numSamples of * ProcessData] / samplerate). The plug-in should always try to process as fast as possible in order * to let enough time slice to other plug-ins. * * - kPrefetch: each process call could be called at a variable frequency (jitter, slower / * faster than realtime), the plug-in should process at the same quality level than realtime, * plug-in must not slow down to realtime (e.g. disk streaming)! The host should avoid to process in * kPrefetch mode such sampler based plug-in. * * - kOffline: each process call could be faster than realtime or slower, higher quality than * realtime could be used. plug-ins using disk streaming should be sure that they have enough time * in the process call for streaming, if needed by slowing down to realtime or slower. * . * Note about Process Modes switching: * - Switching between kRealtime and kPrefetch process modes are done in realtime thread without * need of calling IAudioProcessor::setupProcessing, the plug-in should check in process call the * member processMode of ProcessData in order to know in which mode it is processed. * - Switching between kRealtime (or kPrefetch) and kOffline requires that the host calls * IAudioProcessor::setupProcessing in order to inform the plug-in about this mode change. * . * \see ProcessSetup, ProcessData */ enum ProcessModes { kRealtime, ///< realtime processing kPrefetch, ///< prefetch processing kOffline ///< offline processing }; //------------------------------------------------------------------------ /** kNoTail * * to be returned by getTailSamples when no tail is wanted * \see IAudioProcessor::getTailSamples */ static const uint32 kNoTail = 0; //------------------------------------------------------------------------ /** kInfiniteTail * * to be returned by getTailSamples when infinite tail is wanted * \see IAudioProcessor::getTailSamples */ static const uint32 kInfiniteTail = kMaxInt32u; //------------------------------------------------------------------------ /** Audio processing setup. * \see IAudioProcessor::setupProcessing */ struct ProcessSetup { //------------------------------------------------------------------------ int32 processMode; ///< \ref ProcessModes int32 symbolicSampleSize; ///< \ref SymbolicSampleSizes int32 maxSamplesPerBlock; ///< maximum number of samples per audio block SampleRate sampleRate; ///< sample rate //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ /** Processing buffers of an audio bus. This structure contains the processing buffer for each channel of an audio bus. - The number of channels (numChannels) must always match the current bus arrangement. It could be set to value '0' when the host wants to flush the parameters (when the plug-in is not processed). - The size of the channel buffer array must always match the number of channels. So the host must always supply an array for the channel buffers, regardless if the bus is active or not. However, if an audio bus is currently inactive, the actual sample buffer addresses are safe to be null. - The silence flag is set when every sample of the according buffer has the value '0'. It is intended to be used as help for optimizations allowing a plug-in to reduce processing activities. But even if this flag is set for a channel, the channel buffers must still point to valid memory! This flag is optional. A host is free to support it or not. . \see ProcessData */ struct AudioBusBuffers { AudioBusBuffers () : numChannels (0), silenceFlags (0), channelBuffers64 (nullptr) {} //------------------------------------------------------------------------ int32 numChannels; ///< number of audio channels in bus uint64 silenceFlags; ///< Bitset of silence state per channel union { Sample32** channelBuffers32; ///< sample buffers to process with 32-bit precision Sample64** channelBuffers64; ///< sample buffers to process with 64-bit precision }; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ /** Any data needed in audio processing. * The host prepares AudioBusBuffers for each input/output bus, * regardless of the bus activation state. Bus buffer indices always match * with bus indices used in IComponent::getBusInfo of media type kAudio. * \see AudioBusBuffers, IParameterChanges, IEventList, ProcessContext, IProcessContextRequirements */ struct ProcessData { ProcessData () : processMode (0) , symbolicSampleSize (kSample32) , numSamples (0) , numInputs (0) , numOutputs (0) , inputs (nullptr) , outputs (nullptr) , inputParameterChanges (nullptr) , outputParameterChanges (nullptr) , inputEvents (nullptr) , outputEvents (nullptr) , processContext (nullptr) { } //------------------------------------------------------------------------ int32 processMode; ///< processing mode - value of \ref ProcessModes int32 symbolicSampleSize; ///< sample size - value of \ref SymbolicSampleSizes int32 numSamples; ///< number of samples to process int32 numInputs; ///< number of audio input busses int32 numOutputs; ///< number of audio output busses AudioBusBuffers* inputs; ///< buffers of input busses AudioBusBuffers* outputs; ///< buffers of output busses IParameterChanges* inputParameterChanges; ///< incoming parameter changes for this block IParameterChanges* outputParameterChanges; ///< outgoing parameter changes for this block (optional) IEventList* inputEvents; ///< incoming events for this block (optional) IEventList* outputEvents; ///< outgoing events for this block (optional) ProcessContext* processContext; ///< processing context (optional, but most welcome) //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ /** Audio processing interface: Vst::IAudioProcessor \ingroup vstIPlug vst300 - [plug imp] - [extends IComponent] - [released: 3.0.0] - [mandatory] This interface must always be supported by audio processing plug-ins. */ class IAudioProcessor : public FUnknown { public: //------------------------------------------------------------------------ /** Try to set (host => plug-in) a wanted arrangement for inputs and outputs. * The host should always deliver the same number of input and output busses than the plug-in * needs (see \ref IComponent::getBusCount). The plug-in has 3 possibilities to react on this * setBusArrangements call:\n * * 1. The plug-in accepts these arrangements, then it should modify, if needed, its busses to * match these new arrangements (later on asked by the host with IComponent::getBusInfo () or * IAudioProcessor::getBusArrangement ()) and then should return kResultTrue.\n * * 2. The plug-in does not accept or support these requested arrangements for all * inputs/outputs or just for some or only one bus, but the plug-in can try to adapt its * current arrangements according to the requested ones (requested arrangements for kMain busses * should be handled with more priority than the ones for kAux busses), then it should modify * its busses arrangements and should return kResultFalse.\n * * 3. Same than the point 2 above the plug-in does not support these requested arrangements but * the plug-in cannot find corresponding arrangements, the plug-in could keep its current * arrangement or fall back to a default arrangement by modifying its busses arrangements and * should return kResultFalse.\n * * \param inputs pointer to an array of \ref SpeakerArrangement * \param numIns number of \ref SpeakerArrangement in inputs array * \param outputs pointer to an array of \ref SpeakerArrangement * \param numOuts number of \ref SpeakerArrangement in outputs array * Returns kResultTrue when Arrangements is supported and is the current one, else returns * kResultFalse. * * \note [UI-thread & (Initialized | Connected | Setup Done)] */ virtual tresult PLUGIN_API setBusArrangements (SpeakerArrangement* inputs /*in*/, int32 numIns /*in*/, SpeakerArrangement* outputs /*in*/, int32 numOuts /*in*/) = 0; /** Gets the bus arrangement for a given direction (input/output) and index. * Note: IComponent::getBusInfo () and IAudioProcessor::getBusArrangement () should be always * return the same information about the busses arrangements. * \note [UI-thread & (Initialized | Connected | Setup Done | Activated | Processing] */ virtual tresult PLUGIN_API getBusArrangement (BusDirection dir /*in*/, int32 index /*in*/, SpeakerArrangement& arr /*inout*/) = 0; /** Asks if a given sample size is supported see \ref SymbolicSampleSizes. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API canProcessSampleSize (int32 symbolicSampleSize /*in*/) = 0; /** Gets the current Latency in samples. * The returned value defines the group delay or the latency of the plug-in. For example, if * the plug-in internally needs to look in advance (like compressors) 512 samples then this * plug-in should report 512 as latency. If during the use of the plug-in this latency change, * the plug-in has to inform the host by using IComponentHandler::restartComponent * (kLatencyChanged), this could lead to audio playback interruption because the host has to * recompute its internal mixer delay compensation. Note that for player live recording this * latency should be zero or small. * \note [UI-thread & Setup Done] */ virtual uint32 PLUGIN_API getLatencySamples () = 0; /** Called in disable state (setActive not called with true) before setProcessing is called and * processing will begin. * \note [UI-thread & (Initialized | Connected)]] */ virtual tresult PLUGIN_API setupProcessing (ProcessSetup& setup /*in*/) = 0; /** Informs the plug-in about the processing state. This will be called before any process calls * start with true and after with false. * Note that setProcessing (false) may be called after setProcessing (true) without any process * calls. * Note this function could be called in the UI or in Processing Thread, thats why the plug-in * should only light operation (no memory allocation or big setup reconfiguration), * this could be used to reset some buffers (like Delay line or Reverb). * The host has to be sure that it is called only when the plug-in is enable (setActive (true) * was called). * \note [(UI-thread or processing-thread) & Activated] */ virtual tresult PLUGIN_API setProcessing (TBool state /*in*/) = 0; /** The Process call, where all information (parameter changes, event, audio buffer) are passed. * \note [processing-thread & Processing] */ virtual tresult PLUGIN_API process (ProcessData& data /*in*/) = 0; /** Gets tail size in samples. For example, if the plug-in is a Reverb plug-in and it knows that * the maximum length of the Reverb is 2sec, then it has to return in getTailSamples () * (in VST2 it was getGetTailSize ()): 2*sampleRate. * This information could be used by host for offline processing, process optimization and * downmix (avoiding signal cut (clicks)). * It should return: * - kNoTail when no tail * - x * sampleRate when x Sec tail. * - kInfiniteTail when infinite tail. * * \note [UI-thread & Setup Done] */ virtual uint32 PLUGIN_API getTailSamples () = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IAudioProcessor, 0x42043F99, 0xB7DA453C, 0xA569E79D, 0x9AAEC33D) //------------------------------------------------------------------------ /** Extended IAudioProcessor interface for a component: Vst::IAudioPresentationLatency \ingroup vstIPlug vst310 - [plug imp] - [extends IAudioProcessor] - [released: 3.1.0] - [optional] Inform the plug-in about how long from the moment of generation/acquiring (from file or from Input) it will take for its input to arrive, and how long it will take for its output to be presented (to output or to speaker). Note for Input Presentation Latency: when reading from file, the first plug-in will have an input presentation latency set to zero. When monitoring audio input from an audio device, the initial input latency is the input latency of the audio device itself. Note for Output Presentation Latency: when writing to a file, the last plug-in will have an output presentation latency set to zero. When the output of this plug-in is connected to an audio device, the initial output latency is the output latency of the audio device itself. A value of zero either means no latency or an unknown latency. Each plug-in adding a latency (returning a none zero value for IAudioProcessor::getLatencySamples) will modify the input presentation latency of the next plug-ins in the mixer routing graph and will modify the output presentation latency of the previous plug-ins. \n \image html "iaudiopresentationlatency_usage.png" \n \see IAudioProcessor \see IComponent */ class IAudioPresentationLatency : public FUnknown { public: //------------------------------------------------------------------------ /** Informs the plug-in about the Audio Presentation Latency in samples for a given direction * (kInput/kOutput) and bus index. * \note [UI-thread & Activated] */ virtual tresult PLUGIN_API setAudioPresentationLatencySamples ( BusDirection dir /*in*/, int32 busIndex /*in*/, uint32 latencyInSamples /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IAudioPresentationLatency, 0x309ECE78, 0xEB7D4fae, 0x8B2225D9, 0x09FD08B6) //------------------------------------------------------------------------ /** Extended IAudioProcessor interface for a component: Vst::IProcessContextRequirements \ingroup vstIPlug vst370 - [plug imp] - [extends IAudioProcessor] - [released: 3.7.0] - [mandatory] To get accurate process context information (Vst::ProcessContext), it is now required to implement this interface and return the desired bit mask of flags which your audio effect needs. If you do not implement this interface, you may not get any information at all of the process function! The host asks for this information once between initialize and setActive (in Setup Done). It cannot be changed afterwards. This gives the host the opportunity to better optimize the audio process graph when it knows which plug-ins need which information. Plug-ins built with an earlier SDK version (< 3.7) will still get the old information, but the information may not be as accurate as when using this interface. */ class IProcessContextRequirements : public FUnknown { public: enum Flags { kNeedSystemTime = 1 << 0, // kSystemTimeValid kNeedContinousTimeSamples = 1 << 1, // kContTimeValid kNeedProjectTimeMusic = 1 << 2, // kProjectTimeMusicValid kNeedBarPositionMusic = 1 << 3, // kBarPositionValid kNeedCycleMusic = 1 << 4, // kCycleValid kNeedSamplesToNextClock = 1 << 5, // kClockValid kNeedTempo = 1 << 6, // kTempoValid kNeedTimeSignature = 1 << 7, // kTimeSigValid kNeedChord = 1 << 8, // kChordValid kNeedFrameRate = 1 << 9, // kSmpteValid kNeedTransportState = 1 << 10, // kPlaying, kCycleActive, kRecording }; /** Allows the host to ask the plug-in what is really needed for the process context information * (Vst::ProcessContext). * \note [UI-thread & Setup Done] */ virtual uint32 PLUGIN_API getProcessContextRequirements () = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IProcessContextRequirements, 0x2A654303, 0xEF764E3D, 0x95B5FE83, 0x730EF6D0) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstunits.h0000644000000000000000000000013215124701711022251 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstunits.h0000644000175000001440000003111715124701711022244 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstunits.h // Created by : Steinberg, 2005 // Description : VST Units Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { class IBStream; //------------------------------------------------------------------------ namespace Vst { //------------------------------------------------------------------------ /** Special UnitIDs for UnitInfo */ static const UnitID kRootUnitId = 0; ///< identifier for the top level unit (root) static const UnitID kNoParentUnitId = -1; ///< used for the root unit which does not have a parent. //------------------------------------------------------------------------ /** Special ProgramListIDs for UnitInfo */ static const ProgramListID kNoProgramListId = -1; ///< no programs are used in the unit. //------------------------------------------------------------------------ /** Basic Unit Description. \see IUnitInfo */ struct UnitInfo { UnitID id; ///< unit identifier UnitID parentUnitId; ///< identifier of parent unit (kNoParentUnitId: does not apply, this unit is the root) String128 name; ///< name, optional for the root component, required otherwise ProgramListID programListId; ///< id of program list used in unit (kNoProgramListId = no programs used in this unit) }; //------------------------------------------------------------------------ /** Basic Program List Description. \see IUnitInfo */ struct ProgramListInfo { ProgramListID id; ///< program list identifier String128 name; ///< name of program list int32 programCount; ///< number of programs in this list }; //------------------------------------------------------------------------ /** Special programIndex value for IUnitHandler::notifyProgramListChange */ static const int32 kAllProgramInvalid = -1; ///< all program information is invalid //------------------------------------------------------------------------ /** Host callback for unit support: Vst::IUnitHandler \ingroup vstIHost vst300 - [host imp] - [extends IComponentHandler] - [released: 3.0.0] - [optional] Host callback interface, used with IUnitInfo. Retrieve via queryInterface from IComponentHandler. \see \ref vst3Units, IUnitInfo */ class IUnitHandler : public FUnknown { public: //------------------------------------------------------------------------ /** Notify host when a module is selected in plug-in GUI. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API notifyUnitSelection (UnitID unitId) = 0; /** Tell host that the plug-in controller changed a program list (rename, load, PitchName * changes). * \param listId is the specified program list ID to inform. * \param programIndex: when kAllProgramInvalid, all program information is invalid, otherwise * only the program of given index. * * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API notifyProgramListChange (ProgramListID listId, int32 programIndex) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IUnitHandler, 0x4B5147F8, 0x4654486B, 0x8DAB30BA, 0x163A3C56) //------------------------------------------------------------------------ /** Host callback for extended unit support: Vst::IUnitHandler2 \ingroup vstIHost vst365 - [host imp] - [extends IUnitHandler] - [released: 3.6.5] - [optional] Host callback interface, used with IUnitInfo. Retrieve via queryInterface from IComponentHandler. The plug-in has the possibility to inform the host with notifyUnitByBusChange that something has changed in the bus - unit assignment, the host then has to recall IUnitInfo::getUnitByBus in order to get the new relations between busses and unit. \see \ref vst3Units, IUnitHandler */ class IUnitHandler2 : public FUnknown { public: //------------------------------------------------------------------------ /** Tell host that assignment Unit-Bus defined by IUnitInfo::getUnitByBus has changed. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API notifyUnitByBusChange () = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IUnitHandler2, 0xF89F8CDF, 0x699E4BA5, 0x96AAC9A4, 0x81452B01) //------------------------------------------------------------------------ /** Edit controller extension to describe the plug-in structure: Vst::IUnitInfo \ingroup vstIPlug vst300 - [plug imp] - [extends IEditController] - [released: 3.0.0] - [optional] IUnitInfo describes the internal structure of the plug-in. - The root unit is the component itself, so getUnitCount must return 1 at least. - The root unit id has to be 0 (kRootUnitId). - Each unit can reference one program list - this reference must not change. - Each unit, using a program list, references one program of the list. \see \ref vst3Units, IUnitHandler */ class IUnitInfo : public FUnknown { public: //------------------------------------------------------------------------ /** Returns the flat count of units. * \note [UI-thread & (Initialized | Connected)] */ virtual int32 PLUGIN_API getUnitCount () = 0; /** Gets UnitInfo for a given index in the flat list of unit. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API getUnitInfo (int32 unitIndex, UnitInfo& info /*inout*/) = 0; //--- Component intern program structure ---------------- /** Gets the count of Program List. * \note [UI-thread & (Initialized | Connected)] */ virtual int32 PLUGIN_API getProgramListCount () = 0; /** Gets for a given index the Program List Info. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API getProgramListInfo (int32 listIndex, ProgramListInfo& info /*inout*/) = 0; /** Gets for a given program list ID and program index its program name. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API getProgramName (ProgramListID listId, int32 programIndex, String128 name /*inout*/) = 0; /** Gets for a given program list ID, program index and attributeId the associated attribute * value. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API getProgramInfo (ProgramListID listId, int32 programIndex, CString attributeId /*in*/, String128 attributeValue /*inout*/) = 0; /** Returns kResultTrue if the given program index of a given program list ID supports * PitchNames. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API hasProgramPitchNames (ProgramListID listId, int32 programIndex) = 0; /** Gets the PitchName for a given program list ID, program index and pitch. * If PitchNames are changed the plug-in should inform the host with * IUnitHandler::notifyProgramListChange. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API getProgramPitchName (ProgramListID listId, int32 programIndex, int16 midiPitch, String128 name /*inout*/) = 0; //--- units selection -------------------- /** Gets the current selected unit. * \note [UI-thread & (Initialized | Connected)] */ virtual UnitID PLUGIN_API getSelectedUnit () = 0; /** Sets a new selected unit. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API selectUnit (UnitID unitId) = 0; /** Gets the according unit if there is an unambiguous relation between a channel or a bus and a * unit. This method mainly is intended to find out which unit is related to a given MIDI input * channel. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API getUnitByBus (MediaType type, BusDirection dir, int32 busIndex, int32 channel, UnitID& unitId /*inout*/) = 0; /** Receives a preset data stream. * - If the component supports program list data (IProgramListData), the destination of the data * stream is the program specified by list-Id and program index (first and second parameter) * - If the component supports unit data (IUnitData), the destination is the unit specified by * the first parameter - in this case parameter programIndex is < 0). * * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API setUnitProgramData (int32 listOrUnitId, int32 programIndex, IBStream* data /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IUnitInfo, 0x3D4BD6B5, 0x913A4FD2, 0xA886E768, 0xA5EB92C1) //------------------------------------------------------------------------ /** Component extension to access program list data: Vst::IProgramListData \ingroup vstIPlug vst300 - [plug imp] - [extends IComponent] - [released: 3.0.0] - [optional] A component can support program list data via this interface or/and unit preset data (IUnitData). \see IUnitData, \ref vst3MultitimbralPrograms */ class IProgramListData : public FUnknown { public: //------------------------------------------------------------------------ /** Returns kResultTrue if the given Program List ID supports Program Data. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API programDataSupported (ProgramListID listId) = 0; /** Gets for a given program list ID and program index the program Data. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API getProgramData (ProgramListID listId, int32 programIndex, IBStream* data /*inout*/) = 0; /** Sets for a given program list ID and program index a program Data. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API setProgramData (ProgramListID listId, int32 programIndex, IBStream* data /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IProgramListData, 0x8683B01F, 0x7B354F70, 0xA2651DEC, 0x353AF4FF) //------------------------------------------------------------------------ /** Component extension to access unit data: Vst::IUnitData \ingroup vstIPlug vst300 - [plug imp] - [extends IComponent] - [released: 3.0.0] - [optional] A component can support unit preset data via this interface or program list data (IProgramListData). \see \ref vst3ProgramLists */ class IUnitData : public FUnknown { public: //------------------------------------------------------------------------ /** Returns kResultTrue if the specified unit supports export and import of preset data. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API unitDataSupported (UnitID unitID) = 0; /** Gets the preset data for the specified unit. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API getUnitData (UnitID unitId, IBStream* data /*inout*/) = 0; /** Sets the preset data for the specified unit. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API setUnitData (UnitID unitId, IBStream* data /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IUnitData, 0x6C389611, 0xD391455D, 0xB870B833, 0x94A0EFDD) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivsttestplugprovider.h0000644000000000000000000000013215124701711024531 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivsttestplugprovider.h0000644000175000001440000000647415124701711024534 0ustar00rncbcusers//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Validator // Filename : public.sdk/source/vst/testsuite/iplugprovider.h // Created by : Steinberg, 04/2005 // Description : VST Test Suite // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/istringresult.h" #include "pluginterfaces/vst/ivstcomponent.h" #include "pluginterfaces/vst/ivsteditcontroller.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Test Helper. * \ingroup TestClass * * This class provides access to the component and the controller of a plug-in when running a unit * test (see ITest). * You get this interface as the context argument in the ITestFactory::createTests method. */ class ITestPlugProvider : public FUnknown { public: //------------------------------------------------------------------------ /** get the component of the plug-in. * * The reference count of the component is increased in this function and you need to call * releasePlugIn when done with the component. */ virtual IComponent* PLUGIN_API getComponent () = 0; /** get the controller of the plug-in. * * The reference count of the controller is increased in this function and you need to call * releasePlugIn when done with the controller. */ virtual IEditController* PLUGIN_API getController () = 0; /** release the component and/or controller */ virtual tresult PLUGIN_API releasePlugIn (IComponent* component /*in*/, IEditController* controller /*in*/) = 0; /** get the sub categories of the plug-in */ virtual tresult PLUGIN_API getSubCategories (IStringResult& result /*out*/) const = 0; /** get the component UID of the plug-in */ virtual tresult PLUGIN_API getComponentUID (FUID& uid /*out*/) const = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (ITestPlugProvider, 0x86BE70EE, 0x4E99430F, 0x978F1E6E, 0xD68FB5BA) //------------------------------------------------------------------------ /** Test Helper extension. * \ingroup TestClass */ class ITestPlugProvider2 : public ITestPlugProvider { public: /** get the plugin factory. * * The reference count of the returned factory object is not increased when calling this * function. */ virtual IPluginFactory* PLUGIN_API getPluginFactory () = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (ITestPlugProvider2, 0xC7C75364, 0x7B8343AC, 0xA4495B0A, 0x3E5A46C7) //------------------------------------------------------------------------ } // Vst } // Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstautomationstate.h0000644000000000000000000000013215124701711024330 xustar0030 mtime=1767080905.194000456 30 atime=1767080905.194000456 30 ctime=1767080905.194000456 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstautomationstate.h0000644000175000001440000000472115124701711024324 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstautomationstate.h // Created by : Steinberg, 02/2015 // Description : VST Automation State Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Extended plug-in interface IEditController: Vst::IAutomationState \ingroup vstIPlug vst365 - [plug imp] - [extends IEditController] - [released: 3.6.5] - [optional] Hosts can inform the plug-in about its current automation state (Read/Write/Nothing). */ class IAutomationState : public FUnknown { public: //------------------------------------------------------------------------ enum AutomationStates : int32 { kNoAutomation = 0, ///< Not Read and not Write kReadState = 1 << 0, ///< Read state kWriteState = 1 << 1, ///< Write state kReadWriteState = kReadState | kWriteState, ///< Read and Write enable }; /** Sets the current Automation state. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API setAutomationState (int32 state /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IAutomationState, 0xB4E8287F, 0x1BB346AA, 0x83A46667, 0x68937BAB) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstprefetchablesupport.h0000644000000000000000000000013215124701711025170 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstprefetchablesupport.h0000644000175000001440000000755515124701711025174 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstprefetchablesupport.h // Created by : Steinberg, 02/2015 // Description : VST Prefetchable Support Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/vsttypes.h" #include "pluginterfaces/vst/ivstattributes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { // ------------------------------------------------------------------------ /** \ingroup vst3typedef */ /**@{*/ /** Prefetchable Support Type */ typedef uint32 PrefetchableSupport; /**@}*/ /** Prefetchable Support Enum */ enum ePrefetchableSupport { kIsNeverPrefetchable = 0, ///< every instance of the plug does not support prefetch processing kIsYetPrefetchable, ///< in the current state the plug support prefetch processing kIsNotYetPrefetchable, ///< in the current state the plug does not support prefetch processing kNumPrefetchableSupport }; //------------------------------------------------------------------------ // IPrefetchableSupport Interface //------------------------------------------------------------------------ /** Indicates that the plug-in could or not support Prefetch (dynamically): Vst::IPrefetchableSupport \ingroup vstIPlug vst365 - [plug imp] - [extends IComponent] - [released: 3.6.5] - [optional] The plug-in should implement this interface if it needs to dynamically change between prefetchable or not. By default (without implementing this interface) the host decides in which mode the plug-in is processed. For more info about the prefetch processing mode check the ProcessModes::kPrefetch documentation. \section IPrefetchableSupportExample Example \code{.cpp} //------------------------------------------------------------------------ tresult PLUGIN_API myPlug::getPrefetchableSupport (PrefetchableSupport& prefetchable) { prefetchable = kIsNeverPrefetchable; switch (myPrefetchableMode) { case 0: prefetchable = kIsNeverPrefetchable; break; case 1: prefetchable = kIsYetPrefetchable; break; case 2: prefetchable = kIsNotYetPrefetchable; break; } return kResultOk; } \endcode */ class IPrefetchableSupport : public FUnknown { public: //------------------------------------------------------------------------ /** retrieve the current prefetch support. Use IComponentHandler::restartComponent * (kPrefetchableSupportChanged) to inform the host that this support has changed. * \note [UI-thread & (Initialized | Connected | Setup Done | Activated | Processing)] */ virtual tresult PLUGIN_API getPrefetchableSupport (PrefetchableSupport& prefetchable /*out*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IPrefetchableSupport, 0x8AE54FDA, 0xE93046B9, 0xA28555BC, 0xDC98E21E) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstremapparamid.h0000644000000000000000000000013215124701711023551 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstremapparamid.h0000644000175000001440000000630515124701711023545 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstremapparamid.h // Created by : Steinberg, 02/2024 // Description : VST Edit Controller Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Extended IEditController interface for a component. \ingroup vstIPlug vst3711 - [plug imp] - [extends IEditController] - [released: 3.7.11] - [optional] When replacing one plug-in with another, the host can ask the new plug-in for remapping paramIDs to new ones. \n \see Moduleinfo \see \ref IPluginCompatibility \see IEditController */ class IRemapParamID : public FUnknown { public: //------------------------------------------------------------------------ /** Retrieve the appropriate paramID for a specific plug-in UID and paramID (or index for VST 2 * plug-ins). * The retrieved paramID should match the one it replaces, maintaining the same * behavior during automation playback. Called in UI-Thread context. * * @param[in] pluginToReplaceUID - TUID of plug-in (processor) that will be replaced * @param[in] oldParamID - paramID (or index for VST 2 plug-ins) to be replaced * @param[out] newParamID - contains the associated paramID to be used * * @return kResultTrue if a compatible parameter is available (newParamID has the appropriate * value, it could be the same than oldParamID), or kResultFalse if no compatible parameter is * available (newParamID is undefined). * * \note [UI-thread & Initialized] */ virtual tresult PLUGIN_API getCompatibleParamID (const TUID pluginToReplaceUID /*in*/, ParamID oldParamID /*in*/, ParamID& newParamID /*out*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IRemapParamID, 0x2B88021E, 0x6286B646, 0xB49DF76A, 0x5663061C) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstcomponent.h0000644000000000000000000000013215124701711023111 xustar0030 mtime=1767080905.194000456 30 atime=1767080905.194000456 30 ctime=1767080905.194000456 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstcomponent.h0000644000175000001440000002210315124701711023077 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstcomponent.h // Created by : Steinberg, 04/2005 // Description : Basic VST Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ipluginbase.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { class IBStream; //------------------------------------------------------------------------ /** All VST specific interfaces are located in Vst namespace */ namespace Vst { /** Standard value for PFactoryInfo::flags */ const int32 kDefaultFactoryFlags = PFactoryInfo::kUnicode; #define BEGIN_FACTORY_DEF(vendor,url,email) using namespace Steinberg; \ SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory () { \ if (!gPluginFactory) \ { \ static PFactoryInfo factoryInfo (vendor, url, email, Vst::kDefaultFactoryFlags); \ gPluginFactory = new CPluginFactory (factoryInfo); //------------------------------------------------------------------------ /** \defgroup vstBus VST busses Bus Description A bus can be understood as a "collection of data channels" belonging together. It describes a data input or a data output of the plug-in. A VST component can define any desired number of busses. Dynamic usage of busses is handled in the host by activating and deactivating busses. All busses are initially inactive. The component has to define the maximum number of supported busses and it has to define which of them have to be activated by default after instantiation of the plug-in (This is only a wish, the host is allow to not follow it, and only activate the first bus for example). A host that can handle multiple busses, allows the user to activate busses which are initially all inactive. The kMain busses have to place before any others kAux busses. See also: IComponent::getBusInfo, IComponent::activateBus */ /**@{*/ //------------------------------------------------------------------------ /** Bus media types */ enum MediaTypes { kAudio = 0, ///< audio kEvent, ///< events kNumMediaTypes }; //------------------------------------------------------------------------ /** Bus directions */ enum BusDirections { kInput = 0, ///< input bus kOutput ///< output bus }; //------------------------------------------------------------------------ /** Bus types */ enum BusTypes { kMain = 0, ///< main bus kAux ///< auxiliary bus (sidechain) }; //------------------------------------------------------------------------ /** BusInfo: This is the structure used with getBusInfo, informing the host about what is a specific given bus. \n See also: Steinberg::Vst::IComponent::getBusInfo */ struct BusInfo { MediaType mediaType; ///< Media type - has to be a value of \ref MediaTypes BusDirection direction; ///< input or output \ref BusDirections int32 channelCount; ///< number of channels (if used then need to be recheck after \ref /// IAudioProcessor::setBusArrangements is called). /// For a bus of type MediaTypes::kEvent the channelCount corresponds /// to the number of supported MIDI channels by this bus String128 name; ///< name of the bus BusType busType; ///< main or aux - has to be a value of \ref BusTypes uint32 flags; ///< flags - a combination of \ref BusFlags enum BusFlags { /** The bus should be activated by the host per default on instantiation (activateBus call is requested). By default a bus is inactive. */ kDefaultActive = 1 << 0, /** The bus does not contain ordinary audio data, but data used for control changes at sample rate. The data is in the same format as the audio data [-1..1]. A host has to prevent unintended routing to speakers to prevent damage. Only valid for audio media type busses. [released: 3.7.0] */ kIsControlVoltage = 1 << 1 }; }; /**@}*/ //------------------------------------------------------------------------ /** I/O modes */ enum IoModes { kSimple = 0, ///< 1:1 Input / Output. Only used for Instruments. See \ref vst3IoMode kAdvanced, ///< n:m Input / Output. Only used for Instruments. kOfflineProcessing ///< plug-in used in an offline processing context }; //------------------------------------------------------------------------ /** Routing Information: When the plug-in supports multiple I/O busses, a host may want to know how the busses are related. The relation of an event-input-channel to an audio-output-bus in particular is of interest to the host (in order to relate MIDI-tracks to audio-channels) \n See also: IComponent::getRoutingInfo, \ref vst3Routing */ struct RoutingInfo { MediaType mediaType; ///< media type see \ref MediaTypes int32 busIndex; ///< bus index int32 channel; ///< channel (-1 for all channels) }; //------------------------------------------------------------------------ // IComponent Interface //------------------------------------------------------------------------ /** Component base interface: Vst::IComponent \ingroup vstIPlug vst300 - [plug imp] - [released: 3.0.0] - [mandatory] This is the basic interface for a VST component and must always be supported. It contains the common parts of any kind of processing class. The parts that are specific to a media type are defined in a separate interface. An implementation component must provide both the specific interface and IComponent. \see IPluginBase */ class IComponent : public IPluginBase { public: //------------------------------------------------------------------------ /** Called before initializing the component to get information about the controller class. * \note [UI-thread & Created] */ virtual tresult PLUGIN_API getControllerClassId (TUID classId /*out*/) = 0; /** Called before 'initialize' to set the component usage (optional). See \ref IoModes. * \note [UI-thread & Created] */ virtual tresult PLUGIN_API setIoMode (IoMode mode /*in*/) = 0; /** Called after the plug-in is initialized. See \ref MediaTypes, BusDirections. * \note [UI-thread & Initialized] */ virtual int32 PLUGIN_API getBusCount (MediaType type /*in*/, BusDirection dir /*in*/) = 0; /** Called after the plug-in is initialized. See \ref MediaTypes, BusDirections. * \note [UI-thread & Initialized] */ virtual tresult PLUGIN_API getBusInfo (MediaType type /*in*/, BusDirection dir /*in*/, int32 index /*in*/, BusInfo& bus /*out*/) = 0; /** Retrieves routing information (to be implemented when more than one regular input or output * bus exists). The inInfo always refers to an input bus while the returned outInfo must refer * to an output bus! * \note [UI-thread & Initialized] */ virtual tresult PLUGIN_API getRoutingInfo (RoutingInfo& inInfo /*in*/, RoutingInfo& outInfo /*out*/) = 0; /** Called upon (de-)activating a bus in the host application. The plug-in should only processed * an activated bus, the host could provide less see \ref AudioBusBuffers in the process call * (see \ref IAudioProcessor::process) if last busses are not activated. An already activated * bus does not need to be reactivated after a IAudioProcessor::setBusArrangements call. * \note [UI-thread & Setup Done] */ virtual tresult PLUGIN_API activateBus (MediaType type /*in*/, BusDirection dir /*in*/, int32 index /*in*/, TBool state /*in*/) = 0; /** Activates / deactivates the component. * \note [UI-thread & Setup Done] */ virtual tresult PLUGIN_API setActive (TBool state /*in*/) = 0; /** Sets complete state of component. * \note [UI-thread & (Initialized | Connected | Setup Done | Activated | Processing)] */ virtual tresult PLUGIN_API setState (IBStream* state /*in*/) = 0; /** Retrieves complete state of component. * \note [UI-thread & (Initialized | Connected | Setup Done | Activated | Processing)] */ virtual tresult PLUGIN_API getState (IBStream* state /*inout*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IComponent, 0xE831FF31, 0xF2D54301, 0x928EBBEE, 0x25697802) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstpluginterfacesupport.h0000644000000000000000000000013215124701711025374 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstpluginterfacesupport.h0000644000175000001440000000457315124701711025375 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstpluginterfacesupport.h // Created by : Steinberg, 11/2018 // Description : VST Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Host callback interface for an edit controller: Vst::IPlugInterfaceSupport \ingroup vstIHost vst3612 - [host imp] - [released: 3.6.12] - [optional] Allows a plug-in to ask the host if a given plug-in interface is supported/used by the host. It is implemented by the hostContext given when the component is initialized. \section IPlugInterfaceSupportExample Example \code{.cpp} //------------------------------------------------------------------------ tresult PLUGIN_API MyPluginController::initialize (FUnknown* context) { // ... FUnknownPtr plugInterfaceSupport (context); if (plugInterfaceSupport) { if (plugInterfaceSupport->isPlugInterfaceSupported (IMidiMapping::iid) == kResultTrue) // IMidiMapping is used by the host } // ... } \endcode \see IPluginBase */ class IPlugInterfaceSupport : public FUnknown { public: /** Returns kResultTrue if the associated interface to the given _iid is supported/used by the host. */ virtual tresult PLUGIN_API isPlugInterfaceSupported (const TUID _iid) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IPlugInterfaceSupport, 0x4FB58B9E, 0x9EAA4E0F, 0xAB361C1C, 0xCCB56FEA) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstparameterchanges.h0000644000000000000000000000013215124701711024420 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstparameterchanges.h0000644000175000001440000001416215124701711024414 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstparameterchanges.h // Created by : Steinberg, 09/2005 // Description : VST Parameter Change Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //---------------------------------------------------------------------- namespace Steinberg { namespace Vst { //---------------------------------------------------------------------- /** Queue of changes for a specific parameter: Vst::IParamValueQueue \ingroup vstIHost vst300 - [host imp] - [released: 3.0.0] - [mandatory] The change queue can be interpreted as segment of an automation curve. For each processing block, a segment with the size of the block is transmitted to the processor. The curve is expressed as sampling points of a linear approximation of the original automation curve. If the original already is a linear curve, it can be transmitted precisely. A non-linear curve has to be converted to a linear approximation by the host. Every point of the value queue defines a linear section of the curve as a straight line from the previous point of a block to the new one. So the plug-in can calculate the value of the curve for any sample position in the block. Implicit Points: \n In each processing block, the section of the curve for each parameter is transmitted. In order to reduce the amount of points, the point at block position 0 can be omitted. - If the curve has a slope of 0 over a period of multiple blocks, only one point is transmitted for the block where the constant curve section starts. The queue for the following blocks will be empty as long as the curve slope is 0. - If the curve has a constant slope other than 0 over the period of several blocks, only the value for the last sample of the block is transmitted. In this case, the last valid point is at block position -1. The processor can calculate the value for each sample in the block by using a linear interpolation: \code{.cpp} //------------------------------------------------------------------------ double x1 = -1; // position of last point related to current buffer double y1 = currentParameterValue; // last transmitted value int32 pointTime = 0; ParamValue pointValue = 0; IParamValueQueue::getPoint (0, pointTime, pointValue); double x2 = pointTime; double y2 = pointValue; double slope = (y2 - y1) / (x2 - x1); double offset = y1 - (slope * x1); double curveValue = (slope * bufferTime) + offset; // bufferTime is any position in buffer \endcode \b Jumps: \n A jump in the automation curve has to be transmitted as two points: one with the old value and one with the new value at the next sample position. \image html "automation.jpg" See \ref IParameterChanges, \ref ProcessData */ class IParamValueQueue : public FUnknown { public: //------------------------------------------------------------------------ /** Returns its associated ID. */ virtual ParamID PLUGIN_API getParameterId () = 0; /** Returns count of points in the queue. */ virtual int32 PLUGIN_API getPointCount () = 0; /** Gets the value and offset at a given index. */ virtual tresult PLUGIN_API getPoint (int32 index /*in*/, int32& sampleOffset /*out*/, ParamValue& value /*out*/) = 0; /** Adds a new value at the end of the queue, its index is returned. */ virtual tresult PLUGIN_API addPoint (int32 sampleOffset /*in*/, ParamValue value /*in*/, int32& index /*out*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IParamValueQueue, 0x01263A18, 0xED074F6F, 0x98C9D356, 0x4686F9BA) //---------------------------------------------------------------------- /** All parameter changes of a processing block: Vst::IParameterChanges \ingroup vstIHost vst300 - [host imp] - [released: 3.0.0] - [mandatory] This interface is used to transmit any changes to be applied to parameters in the current processing block. A change can be caused by GUI interaction as well as automation. They are transmitted as a list of queues (\ref IParamValueQueue) containing only queues for parameters that actually did change. See \ref IParamValueQueue, \ref ProcessData */ class IParameterChanges : public FUnknown { public: //------------------------------------------------------------------------ /** Returns count of Parameter changes in the list. */ virtual int32 PLUGIN_API getParameterCount () = 0; /** Returns the queue at a given index. */ virtual IParamValueQueue* PLUGIN_API getParameterData (int32 index /*in*/) = 0; /** Adds a new parameter queue with a given ID at the end of the list, returns it and its index in the parameter changes list. */ virtual IParamValueQueue* PLUGIN_API addParameterData (const ParamID& id /*in*/, int32& index /*out*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IParameterChanges, 0xA4779663, 0x0BB64A56, 0xB44384A8, 0x466FEB9D) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstplugview.h0000644000000000000000000000013215124701711022751 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstplugview.h0000644000175000001440000000521415124701711022743 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstplugview.h // Created by : Steinberg, 01/2009 // Description : Plug-in User Interface Extension // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ // IParameterFinder Interface //------------------------------------------------------------------------ /** Extension for IPlugView to find view parameters (lookup value under mouse support): Vst::IParameterFinder \ingroup pluginGUI vst302 - [plug imp] - [extends IPlugView] - [released: 3.0.2] - [optional] It is highly recommended to implement this interface. A host can implement important functionality when a plug-in supports this interface. For example, all Steinberg hosts require this interface in order to support the "AI Knob". */ class IParameterFinder: public FUnknown { public: //------------------------------------------------------------------------ /** Find out which parameter in plug-in view is at given position (relative to plug-in view). * \note [UI-thread & (Initialized | Connected) & plugView] */ virtual tresult PLUGIN_API findParameter (int32 xPos, int32 yPos, ParamID& resultTag /*out*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IParameterFinder, 0x0F618302, 0x215D4587, 0xA512073C, 0x77B9D383) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstevents.h0000644000000000000000000000013215124701711022413 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstevents.h0000644000175000001440000002056415124701711022412 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstevents.h // Created by : Steinberg, 11/2005 // Description : VST Events Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstprocesscontext.h" #include "pluginterfaces/vst/ivstnoteexpression.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Reserved note identifier (noteId) range for a plug-in. Guaranteed not used by the host. */ enum NoteIDUserRange { kNoteIDUserRangeLowerBound = -10000, kNoteIDUserRangeUpperBound = -1000, }; //------------------------------------------------------------------------ /** Note-on event specific data. Used in \ref Event (union) \ingroup vstEventGrp Pitch uses the twelve-tone equal temperament tuning (12-TET). */ struct NoteOnEvent { int16 channel; ///< channel index in event bus int16 pitch; ///< range [0, 127] = [C-2, G8] with A3=440Hz (12-TET: twelve-tone equal temperament) float tuning; ///< 1.f = +1 cent, -1.f = -1 cent float velocity; ///< range [0.0, 1.0] int32 length; ///< in sample frames (optional, Note Off has to follow in any case!) int32 noteId; ///< note identifier (if not available then -1) }; //------------------------------------------------------------------------ /** Note-off event specific data. Used in \ref Event (union) \ingroup vstEventGrp */ struct NoteOffEvent { int16 channel; ///< channel index in event bus int16 pitch; ///< range [0, 127] = [C-2, G8] with A3=440Hz (12-TET) float velocity; ///< range [0.0, 1.0] int32 noteId; ///< associated noteOn identifier (if not available then -1) float tuning; ///< 1.f = +1 cent, -1.f = -1 cent }; //------------------------------------------------------------------------ /** Data event specific data. Used in \ref Event (union) \ingroup vstEventGrp */ struct DataEvent { uint32 size; ///< size in bytes of the data block bytes uint32 type; ///< type of this data block (see \ref DataTypes) const uint8* bytes; ///< pointer to the data block /** Value for DataEvent::type */ enum DataTypes { kMidiSysEx = 0 ///< for MIDI system exclusive message }; }; //------------------------------------------------------------------------ /** PolyPressure event specific data. Used in \ref Event (union) \ingroup vstEventGrp */ struct PolyPressureEvent { int16 channel; ///< channel index in event bus int16 pitch; ///< range [0, 127] = [C-2, G8] with A3=440Hz float pressure; ///< range [0.0, 1.0] int32 noteId; ///< event should be applied to the noteId (if not -1) }; //------------------------------------------------------------------------ /** Chord event specific data. Used in \ref Event (union) \ingroup vstEventGrp */ struct ChordEvent { int16 root; ///< range [0, 127] = [C-2, G8] with A3=440Hz int16 bassNote; ///< range [0, 127] = [C-2, G8] with A3=440Hz int16 mask; ///< root is bit 0 uint16 textLen; ///< the number of characters (TChar) between the beginning of text and the terminating ///< null character (without including the terminating null character itself) const TChar* text; ///< UTF-16, null terminated Hosts Chord Name }; //------------------------------------------------------------------------ /** Scale event specific data. Used in \ref Event (union) \ingroup vstEventGrp */ struct ScaleEvent { int16 root; ///< range [0, 127] = root Note/Transpose Factor int16 mask; ///< Bit 0 = C, Bit 1 = C#, ... (0x5ab5 = Major Scale) uint16 textLen; ///< the number of characters (TChar) between the beginning of text and the terminating ///< null character (without including the terminating null character itself) const TChar* text; ///< UTF-16, null terminated, Hosts Scale Name }; //------------------------------------------------------------------------ /** Legacy MIDI CC Out event specific data. Used in \ref Event (union) \ingroup vstEventGrp - [released: 3.6.12] This kind of event is reserved for generating MIDI CC as output event for kEvent Bus during the process call. */ struct LegacyMIDICCOutEvent { uint8 controlNumber;///< see enum ControllerNumbers [0, 255] int8 channel; ///< channel index in event bus [0, 15] int8 value; ///< value of Controller [0, 127] int8 value2; ///< [0, 127] used for pitch bend (kPitchBend) and polyPressure (kCtrlPolyPressure) }; //------------------------------------------------------------------------ /** Event \ingroup vstEventGrp Structure representing a single Event of different types associated to a specific event (\ref kEvent) bus. */ struct Event { int32 busIndex; ///< event bus index int32 sampleOffset; ///< sample frames related to the current block start sample position TQuarterNotes ppqPosition; ///< position in project uint16 flags; ///< combination of \ref EventFlags /** Event Flags - used for Event::flags */ enum EventFlags { kIsLive = 1 << 0, ///< indicates that the event is played live (directly from keyboard) kUserReserved1 = 1 << 14, ///< reserved for user (for internal use) kUserReserved2 = 1 << 15 ///< reserved for user (for internal use) }; /** Event Types - used for Event::type */ enum EventTypes { kNoteOnEvent = 0, ///< is \ref NoteOnEvent kNoteOffEvent = 1, ///< is \ref NoteOffEvent kDataEvent = 2, ///< is \ref DataEvent kPolyPressureEvent = 3, ///< is \ref PolyPressureEvent kNoteExpressionValueEvent = 4, ///< is \ref NoteExpressionValueEvent kNoteExpressionTextEvent = 5, ///< is \ref NoteExpressionTextEvent kChordEvent = 6, ///< is \ref ChordEvent kScaleEvent = 7, ///< is \ref ScaleEvent kNoteExpressionIntValueEvent = 8,///< is \ref NoteExpressionIntValueEvent kLegacyMIDICCOutEvent = 65535 ///< is \ref LegacyMIDICCOutEvent }; uint16 type; ///< a value from \ref EventTypes union { NoteOnEvent noteOn; ///< type == kNoteOnEvent NoteOffEvent noteOff; ///< type == kNoteOffEvent DataEvent data; ///< type == kDataEvent PolyPressureEvent polyPressure; ///< type == kPolyPressureEvent NoteExpressionValueEvent noteExpressionValue; ///< type == kNoteExpressionValueEvent NoteExpressionTextEvent noteExpressionText; ///< type == kNoteExpressionTextEvent NoteExpressionIntValueEvent noteExpressionIntValue; ///< type == kNoteExpressionIntValueEvent ChordEvent chord; ///< type == kChordEvent ScaleEvent scale; ///< type == kScaleEvent LegacyMIDICCOutEvent midiCCOut; ///< type == kLegacyMIDICCOutEvent }; }; //------------------------------------------------------------------------ /** List of events to process: Vst::IEventList \ingroup vstIHost vst300 - [host imp] - [released: 3.0.0] - [mandatory] \see ProcessData, Event */ class IEventList : public FUnknown { public: //------------------------------------------------------------------------ /** Returns the count of events. */ virtual int32 PLUGIN_API getEventCount () = 0; /** Gets parameter by index. */ virtual tresult PLUGIN_API getEvent (int32 index /*in*/, Event& e /*out*/) = 0; /** Adds a new event. */ virtual tresult PLUGIN_API addEvent (Event& e /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IEventList, 0x3A2C4214, 0x346349FE, 0xB2C4F397, 0xB9695A44) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstdataexchange.h0000644000000000000000000000013215124701711023523 xustar0030 mtime=1767080905.194000456 30 atime=1767080905.194000456 30 ctime=1767080905.194000456 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstdataexchange.h0000644000175000001440000002262615124701711023523 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstdataexchange.h // Created by : Steinberg, 06/2022 // Description : VST Data Exchange Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ class IAudioProcessor; //------------------------------------------------------------------------ /** \ingroup vst3typedef */ /**@{*/ typedef uint32 DataExchangeQueueID; typedef uint32 DataExchangeBlockID; typedef uint32 DataExchangeUserContextID; /**@}*/ //------------------------------------------------------------------------ static SMTG_CONSTEXPR DataExchangeQueueID InvalidDataExchangeQueueID = kMaxInt32; static SMTG_CONSTEXPR DataExchangeBlockID InvalidDataExchangeBlockID = kMaxInt32; //------------------------------------------------------------------------ struct DataExchangeBlock { /** pointer to the memory buffer */ void* data; /** size of the memory buffer */ uint32 size; /** block identifier */ DataExchangeBlockID blockID; }; //------------------------------------------------------------------------ /** Host Data Exchange handler interface: Vst::IDataExchangeHandler \ingroup vstHost vst379 - [host imp] - [context interface] - [released: 3.7.9] - [optional] The IDataExchangeHandler implements a direct and thread-safe connection from the realtime audio context of the audio processor to the non-realtime audio context of the edit controller. This should be used when the edit controller needs continuous data from the audio process for visualization or other use-cases. To circumvent the bottleneck on the main thread it is possible to configure the connection in a way that the calls to the edit controller will happen on a background thread. Opening a queue: The main operation for a plug-in is to open a queue via the handler before the plug-in is activated (but it must be connected to the edit controller via the IConnectionPoint when the plug-in is using the recommended separation of edit controller and audio processor). The best place to do this is in the IAudioProcessor::setupProcessing method as this is also the place where the plug-in knows the sample rate and maximum block size which the plug-in may need to calculate the queue block size. When a queue is opened the edit controller gets a notification about it and the controller can decide if it wishes to receive the data on the main thread or the background thread. Sending data: In the IAudioProcessor::process call the plug-in can now lock a block from the handler, fill it and when done free the block via the handler which then sends the block to the edit controller. The edit controller then receives the block either on the main thread or on a background thread depending on the setup of the queue. The host guarantees that all blocks are send before the plug-in is deactivated. Closing a queue: The audio processor must close an opened queue and this has to be done after the processor was deactivated and before it is disconnected from the edit controller (see IConnectionPoint). What to do when the queue is full and no block can be locked? The plug-in needs to be prepared for this situation as constraints in the overall system may cause the queue to get full. If you need to get this information to the controller you can declare a hidden parameter which you set to a special value and send this parameter change in your audio process method. */ class IDataExchangeHandler : public FUnknown { public: /** open a new queue * * only allowed to be called from the main thread when the component is not active but * initialized and connected (see IConnectionPoint) * * @param processor the processor who wants to open the queue * @param blockSize size of one block * @param numBlocks number of blocks in the queue * @param alignment data alignment, if zero will use the platform default alignment if any * @param userContextID an identifier internal to the processor * @param outID on return the ID of the queue * @return kResultTrue on success */ virtual tresult PLUGIN_API openQueue (IAudioProcessor* processor, uint32 blockSize, uint32 numBlocks, uint32 alignment, DataExchangeUserContextID userContextID, DataExchangeQueueID* outID) = 0; /** close a queue * * closes and frees all memory of a previously opened queue * if there are locked blocks in the queue, they are freed and made invalid * * only allowed to be called from the main thread when the component is not active but * initialized and connected * * @param queueID the ID of the queue to close * @return kResultTrue on success */ virtual tresult PLUGIN_API closeQueue (DataExchangeQueueID queueID) = 0; /** lock a block if available * * only allowed to be called from within the IAudioProcessor::process call * * @param queueID the ID of the queue * @param block on return will contain the data pointer and size of the block * @return kResultTrue if a free block was found and kOutOfMemory if all blocks are locked */ virtual tresult PLUGIN_API lockBlock (DataExchangeQueueID queueId, DataExchangeBlock* block) = 0; /** free a previously locked block * * only allowed to be called from within the IAudioProcessor::process call * * @param queueID the ID of the queue * @param blockID the ID of the block * @param sendToController if true the block data will be send to the IEditController otherwise * it will be discarded * @return kResultTrue on success */ virtual tresult PLUGIN_API freeBlock (DataExchangeQueueID queueId, DataExchangeBlockID blockID, TBool sendToController) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IDataExchangeHandler, 0x36D551BD, 0x6FF54F08, 0xB48E830D, 0x8BD5A03B) //------------------------------------------------------------------------ /** Data Exchange Receiver interface: Vst::IDataExchangeReceiver \ingroup vstPlug vst379 - [plug imp] - [released: 3.7.9 - [optional] The receiver interface is required to receive data from the realtime audio process via the IDataExchangeHandler. \see \ref IDataExchangeHandler */ class IDataExchangeReceiver : public FUnknown { public: /** queue opened notification * * called on the main thread when the processor has opened a queue * * @param userContextID the user context ID of the queue * @param blockSize the size of one block of the queue * @param dispatchedOnBackgroundThread if true on output the blocks are dispatched on a * background thread [defaults to false in which case the * blocks are dispatched on the main thread] */ virtual void PLUGIN_API queueOpened (DataExchangeUserContextID userContextID, uint32 blockSize, TBool& dispatchOnBackgroundThread) = 0; /** queue closed notification * * called on the main thread when the processor has closed a queue * * @param userContextID the user context ID of the queue */ virtual void PLUGIN_API queueClosed (DataExchangeUserContextID userContextID) = 0; /** one or more blocks were received * * called either on the main thread or a background thread depending on the * dispatchOnBackgroundThread value in the queueOpened call. * * the data of the blocks are only valid inside this call and the blocks only become available * to the queue afterwards. * * @param userContextID the user context ID of the queue * @param numBlocks number of blocks * @param blocks the blocks * @param onBackgroundThread true if the call is done on a background thread */ virtual void PLUGIN_API onDataExchangeBlocksReceived (DataExchangeUserContextID userContextID, uint32 numBlocks, DataExchangeBlock* blocks, TBool onBackgroundThread) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IDataExchangeReceiver, 0x45A759DC, 0x84FA4907, 0xABCB6175, 0x2FC786B6) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstmidilearn.h0000644000000000000000000000013215124701711023053 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstmidilearn.h0000644000175000001440000000755315124701711023055 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstmidilearn.h // Created by : Steinberg, 11/2018 // Description : VST MIDI Learn // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** MIDI Learn interface: Vst::IMidiLearn \ingroup vstIPlug vst3612 - [plug imp] - [extends IEditController] - [released: 3.6.12] - [optional] If this interface is implemented by the edit controller, the host will call this method whenever there is live MIDI-CC input for the plug-in. This way, the plug-in can change its MIDI-CC parameter mapping and notify the host using the IComponentHandler::restartComponent with the kMidiCCAssignmentChanged flag. Use this if you want to implement custom MIDI-Learn functionality in your plug-in. \code{.cpp} //------------------------------------------------ // in MyController class declaration class MyController : public Vst::EditController, public Vst::IMidiLearn { // ... //--- IMidiLearn --------------------------------- tresult PLUGIN_API onLiveMIDIControllerInput (int32 busIndex, int16 channel, CtrlNumber midiCC) SMTG_OVERRIDE; // ... OBJ_METHODS (MyController, Vst::EditController) DEFINE_INTERFACES // ... DEF_INTERFACE (Vst::IMidiLearn) END_DEFINE_INTERFACES (Vst::EditController) //... } //------------------------------------------------ // in mycontroller.cpp #include "pluginterfaces/vst/ivstmidilearn.h namespace Steinberg { namespace Vst { DEF_CLASS_IID (IMidiLearn) } } //------------------------------------------------------------------------ tresult PLUGIN_API MyController::onLiveMIDIControllerInput (int32 busIndex, int16 channel, CtrlNumber midiCC) { // if we are not in doMIDILearn (triggered by a UI button for example) // or wrong channel then return if (!doMIDILearn || busIndex != 0 || channel != 0 || midiLearnParamID == InvalidParamID) return kResultFalse; // adapt our internal MIDICC -> parameterID mapping midiCCMapping[midiCC] = midiLearnParamID; // new mapping then inform the host that our MIDI assignment has changed if (auto componentHandler = getComponentHandler ()) { componentHandler->restartComponent (kMidiCCAssignmentChanged); } return kResultTrue; } \endcode */ class IMidiLearn : public FUnknown { public: /** Called on live input MIDI-CC change associated to a given bus index and MIDI channel. * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API onLiveMIDIControllerInput (int32 busIndex /*in*/, int16 channel /*in*/, CtrlNumber midiCC /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IMidiLearn, 0x6B2449CC, 0x419740B5, 0xAB3C79DA, 0xC5FE5C86) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/vstspeaker.h0000644000000000000000000000013215124701711022370 xustar0030 mtime=1767080905.195211228 30 atime=1767080905.195211228 30 ctime=1767080905.195211228 qtractor-1.5.11/src/vst3/pluginterfaces/vst/vstspeaker.h0000644000175000001440000017073415124701711022374 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/vstspeaker.h // Created by : Steinberg, 01/2018 // Description : common defines // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** \defgroup speakerArrangements Speaker Arrangements \image html "vst3_speaker_types.jpg" \n A SpeakerArrangement is a bitset combination of speakers. For example: \code const SpeakerArrangement kStereo = kSpeakerL | kSpeakerR; // => hex: 0x03 / binary: 0011. \endcode \see IAudioProcessor::getBusArrangement () and IAudioProcessor::setBusArrangements () */ //------------------------------------------------------------------------ //------------------------------------------------------------------------ /** Speaker Definitions. * \ingroup speakerArrangements */ /**@{*/ const Speaker kSpeakerL = 1 << 0; ///< Left (L) const Speaker kSpeakerR = 1 << 1; ///< Right (R) const Speaker kSpeakerC = 1 << 2; ///< Center (C) const Speaker kSpeakerLfe = 1 << 3; ///< Subbass (Lfe) const Speaker kSpeakerLs = 1 << 4; ///< Left Surround (Ls) const Speaker kSpeakerRs = 1 << 5; ///< Right Surround (Rs) const Speaker kSpeakerLc = 1 << 6; ///< Left of Center (Lc) - Front Left Center const Speaker kSpeakerRc = 1 << 7; ///< Right of Center (Rc) - Front Right Center const Speaker kSpeakerS = 1 << 8; ///< Surround (S) const Speaker kSpeakerCs = kSpeakerS; ///< Center of Surround (Cs) - Back Center - Surround (S) const Speaker kSpeakerSl = 1 << 9; ///< Side Left (Sl) const Speaker kSpeakerSr = 1 << 10; ///< Side Right (Sr) const Speaker kSpeakerTc = 1 << 11; ///< Top Center Over-head, Top Middle (Tc) const Speaker kSpeakerTfl = 1 << 12; ///< Top Front Left (Tfl) const Speaker kSpeakerTfc = 1 << 13; ///< Top Front Center (Tfc) const Speaker kSpeakerTfr = 1 << 14; ///< Top Front Right (Tfr) const Speaker kSpeakerTrl = 1 << 15; ///< Top Rear/Back Left (Trl) const Speaker kSpeakerTrc = 1 << 16; ///< Top Rear/Back Center (Trc) const Speaker kSpeakerTrr = 1 << 17; ///< Top Rear/Back Right (Trr) const Speaker kSpeakerLfe2 = 1 << 18; ///< Subbass 2 (Lfe2) const Speaker kSpeakerM = 1 << 19; ///< Mono (M) const Speaker kSpeakerACN0 = (Speaker)1 << 20; ///< Ambisonic ACN 0 const Speaker kSpeakerACN1 = (Speaker)1 << 21; ///< Ambisonic ACN 1 const Speaker kSpeakerACN2 = (Speaker)1 << 22; ///< Ambisonic ACN 2 const Speaker kSpeakerACN3 = (Speaker)1 << 23; ///< Ambisonic ACN 3 const Speaker kSpeakerACN4 = (Speaker)1 << 38; ///< Ambisonic ACN 4 const Speaker kSpeakerACN5 = (Speaker)1 << 39; ///< Ambisonic ACN 5 const Speaker kSpeakerACN6 = (Speaker)1 << 40; ///< Ambisonic ACN 6 const Speaker kSpeakerACN7 = (Speaker)1 << 41; ///< Ambisonic ACN 7 const Speaker kSpeakerACN8 = (Speaker)1 << 42; ///< Ambisonic ACN 8 const Speaker kSpeakerACN9 = (Speaker)1 << 43; ///< Ambisonic ACN 9 const Speaker kSpeakerACN10 = (Speaker)1 << 44; ///< Ambisonic ACN 10 const Speaker kSpeakerACN11 = (Speaker)1 << 45; ///< Ambisonic ACN 11 const Speaker kSpeakerACN12 = (Speaker)1 << 46; ///< Ambisonic ACN 12 const Speaker kSpeakerACN13 = (Speaker)1 << 47; ///< Ambisonic ACN 13 const Speaker kSpeakerACN14 = (Speaker)1 << 48; ///< Ambisonic ACN 14 const Speaker kSpeakerACN15 = (Speaker)1 << 49; ///< Ambisonic ACN 15 const Speaker kSpeakerACN16 = (Speaker)1 << 50; ///< Ambisonic ACN 16 const Speaker kSpeakerACN17 = (Speaker)1 << 51; ///< Ambisonic ACN 17 const Speaker kSpeakerACN18 = (Speaker)1 << 52; ///< Ambisonic ACN 18 const Speaker kSpeakerACN19 = (Speaker)1 << 53; ///< Ambisonic ACN 19 const Speaker kSpeakerACN20 = (Speaker)1 << 54; ///< Ambisonic ACN 20 const Speaker kSpeakerACN21 = (Speaker)1 << 55; ///< Ambisonic ACN 21 const Speaker kSpeakerACN22 = (Speaker)1 << 56; ///< Ambisonic ACN 22 const Speaker kSpeakerACN23 = (Speaker)1 << 57; ///< Ambisonic ACN 23 const Speaker kSpeakerACN24 = (Speaker)1 << 58; ///< Ambisonic ACN 24 const Speaker kSpeakerTsl = (Speaker)1 << 24; ///< Top Side Left (Tsl) const Speaker kSpeakerTsr = (Speaker)1 << 25; ///< Top Side Right (Tsr) const Speaker kSpeakerLcs = (Speaker)1 << 26; ///< Left of Center Surround (Lcs) - Back Left Center const Speaker kSpeakerRcs = (Speaker)1 << 27; ///< Right of Center Surround (Rcs) - Back Right Center const Speaker kSpeakerBfl = (Speaker)1 << 28; ///< Bottom Front Left (Bfl) const Speaker kSpeakerBfc = (Speaker)1 << 29; ///< Bottom Front Center (Bfc) const Speaker kSpeakerBfr = (Speaker)1 << 30; ///< Bottom Front Right (Bfr) const Speaker kSpeakerPl = (Speaker)1 << 31; ///< Proximity Left (Pl) const Speaker kSpeakerPr = (Speaker)1 << 32; ///< Proximity Right (Pr) const Speaker kSpeakerBsl = (Speaker)1 << 33; ///< Bottom Side Left (Bsl) const Speaker kSpeakerBsr = (Speaker)1 << 34; ///< Bottom Side Right (Bsr) const Speaker kSpeakerBrl = (Speaker)1 << 35; ///< Bottom Rear Left (Brl) const Speaker kSpeakerBrc = (Speaker)1 << 36; ///< Bottom Rear Center (Brc) const Speaker kSpeakerBrr = (Speaker)1 << 37; ///< Bottom Rear Right (Brr) const Speaker kSpeakerLw = (Speaker)1 << 59; ///< Left Wide (Lw) const Speaker kSpeakerRw = (Speaker)1 << 60; ///< Right Wide (Rw) //------------------------------------------------------------------------ /** @}*/ //------------------------------------------------------------------------ /** Speaker Arrangement Definitions (SpeakerArrangement) */ namespace SpeakerArr { //------------------------------------------------------------------------ /** Speaker Arrangement Definitions. * for example: 5.0.5.3 for 5x Middle + 0x LFE + 5x Top + 3x Bottom * \ingroup speakerArrangements */ /**@{*/ const SpeakerArrangement kEmpty = 0; ///< empty arrangement const SpeakerArrangement kMono = kSpeakerM; ///< M const SpeakerArrangement kStereo = kSpeakerL | kSpeakerR; ///< L R const SpeakerArrangement kStereoWide = kSpeakerLw | kSpeakerRw; ///< Lw Rw const SpeakerArrangement kStereoSurround = kSpeakerLs | kSpeakerRs; ///< Ls Rs const SpeakerArrangement kStereoCenter = kSpeakerLc | kSpeakerRc; ///< Lc Rc const SpeakerArrangement kStereoSide = kSpeakerSl | kSpeakerSr; ///< Sl Sr const SpeakerArrangement kStereoCLfe = kSpeakerC | kSpeakerLfe; ///< C Lfe const SpeakerArrangement kStereoTF = kSpeakerTfl | kSpeakerTfr; ///< Tfl Tfr const SpeakerArrangement kStereoTS = kSpeakerTsl | kSpeakerTsr; ///< Tsl Tsr const SpeakerArrangement kStereoTR = kSpeakerTrl | kSpeakerTrr; ///< Trl Trr const SpeakerArrangement kStereoBF = kSpeakerBfl | kSpeakerBfr; ///< Bfl Bfr /** L R C Lc Rc */ const SpeakerArrangement kCineFront = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLc | kSpeakerRc; /** L R C */ // 3.0 const SpeakerArrangement k30Cine = kSpeakerL | kSpeakerR | kSpeakerC; /** L R C Lfe */ // 3.1 const SpeakerArrangement k31Cine = k30Cine | kSpeakerLfe; /** L R S */ const SpeakerArrangement k30Music = kSpeakerL | kSpeakerR | kSpeakerCs; /** L R Lfe S */ const SpeakerArrangement k31Music = k30Music | kSpeakerLfe; /** L R C S */ // LCRS const SpeakerArrangement k40Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerCs; /** L R C Lfe S */ // LCRS+Lfe const SpeakerArrangement k41Cine = k40Cine | kSpeakerLfe; /** L R Ls Rs */ // 4.0 (Quadro) const SpeakerArrangement k40Music = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs; /** L R Lfe Ls Rs */ // 4.1 (Quadro+Lfe) const SpeakerArrangement k41Music = k40Music | kSpeakerLfe; /** L R C Ls Rs */ // 5.0 (ITU 0+5+0.0 Sound System B) const SpeakerArrangement k50 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs; /** L R C Lfe Ls Rs */ // 5.1 (ITU 0+5+0.1 Sound System B) const SpeakerArrangement k51 = k50 | kSpeakerLfe; /** L R C Ls Rs Cs */ const SpeakerArrangement k60Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerCs; /** L R C Lfe Ls Rs Cs */ const SpeakerArrangement k61Cine = k60Cine | kSpeakerLfe; /** L R Ls Rs Sl Sr */ const SpeakerArrangement k60Music = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr; /** L R Lfe Ls Rs Sl Sr */ const SpeakerArrangement k61Music = k60Music | kSpeakerLfe; /** L R C Ls Rs Lc Rc */ const SpeakerArrangement k70Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc; /** L R C Lfe Ls Rs Lc Rc */ const SpeakerArrangement k71Cine = k70Cine | kSpeakerLfe; const SpeakerArrangement k71CineFullFront = k71Cine; /** L R C Ls Rs Sl Sr */ // (ITU 0+7+0.0 Sound System I) const SpeakerArrangement k70Music = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr; /** L R C Lfe Ls Rs Sl Sr */ // (ITU 0+7+0.1 Sound System I) const SpeakerArrangement k71Music = k70Music | kSpeakerLfe; /** L R C Lfe Ls Rs Lcs Rcs */ const SpeakerArrangement k71CineFullRear = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerLcs | kSpeakerRcs; const SpeakerArrangement k71CineSideFill = k71Music; /** L R C Lfe Ls Rs Pl Pr */ const SpeakerArrangement k71Proximity = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerPl | kSpeakerPr; /** L R C Ls Rs Lc Rc Cs */ const SpeakerArrangement k80Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerCs; /** L R C Lfe Ls Rs Lc Rc Cs */ const SpeakerArrangement k81Cine = k80Cine | kSpeakerLfe; /** L R C Ls Rs Cs Sl Sr */ const SpeakerArrangement k80Music = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerCs | kSpeakerSl | kSpeakerSr; /** L R C Lfe Ls Rs Cs Sl Sr */ const SpeakerArrangement k81Music = k80Music | kSpeakerLfe; /** L R C Ls Rs Lc Rc Sl Sr */ const SpeakerArrangement k90Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerSl | kSpeakerSr; /** L R C Lfe Ls Rs Lc Rc Sl Sr */ const SpeakerArrangement k91Cine = k90Cine | kSpeakerLfe; /** L R C Ls Rs Lc Rc Cs Sl Sr */ const SpeakerArrangement k100Cine = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerCs | kSpeakerSl | kSpeakerSr; /** L R C Lfe Ls Rs Lc Rc Cs Sl Sr */ const SpeakerArrangement k101Cine = k100Cine | kSpeakerLfe; /** First-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (4 channels) */ const SpeakerArrangement kAmbi1stOrderACN = kSpeakerACN0 | kSpeakerACN1 | kSpeakerACN2 | kSpeakerACN3; /** Second-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (9 channels) */ const SpeakerArrangement kAmbi2cdOrderACN = kAmbi1stOrderACN | kSpeakerACN4 | kSpeakerACN5 | kSpeakerACN6 | kSpeakerACN7 | kSpeakerACN8; /** Third-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (16 channels) */ const SpeakerArrangement kAmbi3rdOrderACN = kAmbi2cdOrderACN | kSpeakerACN9 | kSpeakerACN10 | kSpeakerACN11 | kSpeakerACN12 | kSpeakerACN13 | kSpeakerACN14 | kSpeakerACN15; /** Fourth-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (25 channels) */ const SpeakerArrangement kAmbi4thOrderACN = kAmbi3rdOrderACN | kSpeakerACN16 | kSpeakerACN17 | kSpeakerACN18 | kSpeakerACN19 | kSpeakerACN20 | kSpeakerACN21 | kSpeakerACN22 | kSpeakerACN23 | kSpeakerACN24; /** Fifth-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (36 channels) */ const SpeakerArrangement kAmbi5thOrderACN = 0x000FFFFFFFFF; /** Sixth-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (49 channels) */ const SpeakerArrangement kAmbi6thOrderACN = 0x0001FFFFFFFFFFFF; /** Seventh-Order with Ambisonic Channel Number (ACN) ordering and SN3D normalization (64 channels) */ const SpeakerArrangement kAmbi7thOrderACN = 0xFFFFFFFFFFFFFFFF; /*-----------*/ /* 3D formats */ /*-----------*/ /** L R Ls Rs Tfl Tfr Trl Trr */ // 4.0.4 const SpeakerArrangement k80Cube = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | kSpeakerTfl| kSpeakerTfr| kSpeakerTrl | kSpeakerTrr; const SpeakerArrangement k40_4 = k80Cube; /** L R C Lfe Ls Rs Cs Tc */ // 6.1.1 const SpeakerArrangement k71CineTopCenter = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerCs | kSpeakerTc; /** L R C Lfe Ls Rs Cs Tfc */ // 6.1.1 const SpeakerArrangement k71CineCenterHigh = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerCs | kSpeakerTfc; /** L R C Ls Rs Tfl Tfr */ // 5.0.2 (ITU 2+5+0.0 Sound System C) const SpeakerArrangement k70CineFrontHigh = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfr; const SpeakerArrangement k70MPEG3D = k70CineFrontHigh; const SpeakerArrangement k50_2 = k70CineFrontHigh; /** L R C Lfe Ls Rs Tfl Tfr */ // 5.1.2 (ITU 2+5+0.1 Sound System C) const SpeakerArrangement k71CineFrontHigh = k70CineFrontHigh | kSpeakerLfe; const SpeakerArrangement k71MPEG3D = k71CineFrontHigh; const SpeakerArrangement k51_2 = k71CineFrontHigh; /** L R C Ls Rs Tsl Tsr */ // 5.0.2 (Side) const SpeakerArrangement k70CineSideHigh = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTsl | kSpeakerTsr; const SpeakerArrangement k50_2_TS = k70CineSideHigh; /** L R C Lfe Ls Rs Tsl Tsr */ // 5.1.2 (Side) const SpeakerArrangement k71CineSideHigh = k70CineSideHigh | kSpeakerLfe; const SpeakerArrangement k51_2_TS = k71CineSideHigh; /** L R Lfe Ls Rs Tfl Tfc Tfr Bfc */ // 4.1.3.1 const SpeakerArrangement k81MPEG3D = kSpeakerL | kSpeakerR | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerBfc; const SpeakerArrangement k41_4_1 = k81MPEG3D; /** L R C Ls Rs Tfl Tfr Trl Trr */ // 5.0.4 (ITU 4+5+0.0 Sound System D) const SpeakerArrangement k90 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTfl| kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; const SpeakerArrangement k50_4 = k90; /** L R C Lfe Ls Rs Tfl Tfr Trl Trr */ // 5.1.4 (ITU 4+5+0.1 Sound System D) const SpeakerArrangement k91 = k90 | kSpeakerLfe; const SpeakerArrangement k51_4 = k91; /** L R C Ls Rs Tfl Tfr Trl Trr Bfc */ // 5.0.4.1 (ITU 4+5+1.0 Sound System E) const SpeakerArrangement k50_4_1 = k50_4 | kSpeakerBfc; /** L R C Lfe Ls Rs Tfl Tfr Trl Trr Bfc */ // 5.1.4.1 (ITU 4+5+1.1 Sound System E) const SpeakerArrangement k51_4_1 = k50_4_1 | kSpeakerLfe; /** L R C Ls Rs Sl Sr Tsl Tsr */ // 7.0.2 const SpeakerArrangement k70_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | kSpeakerTsl | kSpeakerTsr; /** L R C Lfe Ls Rs Sl Sr Tsl Tsr */ // 7.1.2 const SpeakerArrangement k71_2 = k70_2 | kSpeakerLfe; const SpeakerArrangement k91Atmos = k71_2; // 9.1 Dolby Atmos (3D) /** L R C Ls Rs Sl Sr Tfl Tfr */ // 7.0.2 (~ITU 2+7+0.0) const SpeakerArrangement k70_2_TF = k70Music | kSpeakerTfl | kSpeakerTfr; /** L R C Lfe Ls Rs Sl Sr Tfl Tfr */ // 7.1.2 (~ITU 2+7+0.1) const SpeakerArrangement k71_2_TF = k70_2_TF | kSpeakerLfe; /** L R C Ls Rs Sl Sr Tfl Tfr Trc */ // 7.0.3 (ITU 3+7+0.0 Sound System F) const SpeakerArrangement k70_3 = k70_2_TF | kSpeakerTrc; /** L R C Lfe Ls Rs Sl Sr Tfl Tfr Trc Lfe2 */ // 7.2.3 (ITU 3+7+0.2 Sound System F) const SpeakerArrangement k72_3 = k70_3 | kSpeakerLfe | kSpeakerLfe2; /** L R C Ls Rs Sl Sr Tfl Tfr Trl Trr */ // 7.0.4 (ITU 4+7+0.0 Sound System J) const SpeakerArrangement k70_4 = k70_2_TF | kSpeakerTrl | kSpeakerTrr; /** L R C Lfe Ls Rs Sl Sr Tfl Tfr Trl Trr */ // 7.1.4 (ITU 4+7+0.1 Sound System J) const SpeakerArrangement k71_4 = k70_4 | kSpeakerLfe; const SpeakerArrangement k111MPEG3D = k71_4; /** L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr */ // 7.0.6 const SpeakerArrangement k70_6 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerTsl | kSpeakerTsr; /** L R C Lfe Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr */ // 7.1.6 const SpeakerArrangement k71_6 = k70_6 | kSpeakerLfe; /** L R C Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr */ // 9.0.4 (ITU 4+9+0.0 Sound System G) const SpeakerArrangement k90_4 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; /** L R C Lfe Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr */ // 9.1.4 (ITU 4+9+0.1 Sound System G) const SpeakerArrangement k91_4 = k90_4 | kSpeakerLfe; /** L R C Lfe Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr Tsl Tsr */ // 9.0.6 const SpeakerArrangement k90_6 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerTsl | kSpeakerTsr; /** L R C Lfe Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr Tsl Tsr */ // 9.1.6 const SpeakerArrangement k91_6 = k90_6 | kSpeakerLfe; /** L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Lw Rw */ // 9.0.4 (Dolby) const SpeakerArrangement k90_4_W = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLw | kSpeakerRw | kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; /** L R C Lfe Ls Rs Sl Sr Tfl Tfr Trl Trr Lw Rw */ // 9.1.4 (Dolby) const SpeakerArrangement k91_4_W = k90_4_W | kSpeakerLfe; /** L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr Lw Rw */ // 9.0.6 (Dolby) const SpeakerArrangement k90_6_W = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLw | kSpeakerRw | kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerTsl | kSpeakerTsr; /** L R C Lfe Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr Lw Rw */ // 9.1.6 (Dolby) const SpeakerArrangement k91_6_W = k90_6_W | kSpeakerLfe; /** L R C Ls Rs Tc Tfl Tfr Trl Trr */ // 5.0.5 (10.0 Auro-3D) const SpeakerArrangement k100 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTc | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; const SpeakerArrangement k50_5 = k100; /** L R C Lfe Ls Rs Tc Tfl Tfr Trl Trr */ // 5.1.5 (10.1 Auro-3D) const SpeakerArrangement k101 = k50_5 | kSpeakerLfe; const SpeakerArrangement k101MPEG3D = k101; const SpeakerArrangement k51_5 = k101; /** L R C Lfe Ls Rs Tfl Tfc Tfr Trl Trr Lfe2 */ // 5.2.5 const SpeakerArrangement k102 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerTfl| kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerLfe2; const SpeakerArrangement k52_5 = k102; /** L R C Ls Rs Tc Tfl Tfc Tfr Trl Trr */ // 5.0.6 (11.0 Auro-3D) const SpeakerArrangement k110 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTc | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; const SpeakerArrangement k50_6 = k110; /** L R C Lfe Ls Rs Tc Tfl Tfc Tfr Trl Trr */ // 5.1.6 (11.1 Auro-3D) const SpeakerArrangement k111 = k110 | kSpeakerLfe; const SpeakerArrangement k51_6 = k111; /** L R C Lfe Ls Rs Lc Rc Tfl Tfc Tfr Trl Trr Lfe2 */ // 7.2.5 const SpeakerArrangement k122 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerTfl| kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerLfe2; const SpeakerArrangement k72_5 = k122; /** L R C Ls Rs Sl Sr Tc Tfl Tfc Tfr Trl Trr */ // 7.0.6 (13.0 Auro-3D) const SpeakerArrangement k130 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | kSpeakerTc | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; /** L R C Lfe Ls Rs Sl Sr Tc Tfl Tfc Tfr Trl Trr */ // 7.1.6 (13.1 Auro-3D) const SpeakerArrangement k131 = k130 | kSpeakerLfe; /** L R Ls Rs Sl Sr Tfl Tfr Trl Trr Bfl Bfr Brl Brr */ // 6.0.4.4 const SpeakerArrangement k140 = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerBfl | kSpeakerBfr | kSpeakerBrl | kSpeakerBrr; const SpeakerArrangement k60_4_4 = k140; /** L R C Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr Tsl Tsr Bfl Bfc Bfr */ // 10.0.9.3 (ITU 9+10+3.0 Sound System H) const SpeakerArrangement k220 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerCs | kSpeakerSl | kSpeakerSr | kSpeakerTc | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrc | kSpeakerTrr | kSpeakerTsl | kSpeakerTsr | kSpeakerBfl| kSpeakerBfc | kSpeakerBfr; const SpeakerArrangement k100_9_3 = k220; /** L R C Lfe Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr Lfe2 Tsl Tsr Bfl Bfc Bfr */ // 10.2.9.3 (ITU 9+10+3.2 Sound System H) const SpeakerArrangement k222 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLfe | kSpeakerLs | kSpeakerRs | kSpeakerLc | kSpeakerRc | kSpeakerCs | kSpeakerSl | kSpeakerSr | kSpeakerTc | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrc | kSpeakerTrr | kSpeakerLfe2 | kSpeakerTsl | kSpeakerTsr | kSpeakerBfl| kSpeakerBfc | kSpeakerBfr; const SpeakerArrangement k102_9_3 = k222; /** L R C Ls Rs Tfl Tfc Tfr Trl Trr Bfl Bfc Bfr */ // 5.0.5.3 const SpeakerArrangement k50_5_3 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerBfl | kSpeakerBfc | kSpeakerBfr; /** L R C Lfe Ls Rs Tfl Tfc Tfr Trl Trr Bfl Bfc Bfr */ // 5.1.5.3 const SpeakerArrangement k51_5_3 = k50_5_3 | kSpeakerLfe; /** L R C Ls Rs Tsl Tsr Bfl Bfr */ // 5.0.2.2 const SpeakerArrangement k50_2_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTsl | kSpeakerTsr | kSpeakerBfl | kSpeakerBfr; /** L R C Ls Rs Tfl Tfr Trl Trr Bfl Bfr */ // 5.0.4.2 const SpeakerArrangement k50_4_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerBfl | kSpeakerBfr; /** L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Bfl Bfr */ // 7.0.4.2 const SpeakerArrangement k70_4_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerSl | kSpeakerSr | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerBfl | kSpeakerBfr; /** L R C Ls Rs Tfl Tfc Tfr Trl Trr */ // 5.0.5.0 (Sony 360RA) const SpeakerArrangement k50_5_Sony = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr; /** C Sl Sr Cs Tsl Tsr Bsl Bsr */ // 4.0.2.2 (Sony 360RA) const SpeakerArrangement k40_2_2 = kSpeakerC | kSpeakerSl | kSpeakerSr | kSpeakerCs | kSpeakerTsl | kSpeakerTsr | kSpeakerBsl | kSpeakerBsr; /** L R Ls Rs Tfl Tfr Trl Trr Bfl Bfr */ // 4.0.4.2 (Sony 360RA) const SpeakerArrangement k40_4_2 = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerBfl | kSpeakerBfr; /** L R C Ls Rs Tfl Tfc Tfr Bfl Bfr */ // 5.0.3.2 (Sony 360RA) const SpeakerArrangement k50_3_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerBfl | kSpeakerBfr; /** L R C Tfl Tfc Tfr Trl Trr Bfl Bfr */ // 3.0.5.2 (Sony 360RA) const SpeakerArrangement k30_5_2 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerTfl | kSpeakerTfc | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerBfl | kSpeakerBfr; /** L R Ls Rs Tfl Tfr Trl Trr Bfl Bfr Brl Brr */ // 4.0.4.4 (Sony 360RA) const SpeakerArrangement k40_4_4 = kSpeakerL | kSpeakerR | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerBfl | kSpeakerBfr | kSpeakerBrl | kSpeakerBrr; /** L R C Ls Rs Tfl Tfr Trl Trr Bfl Bfr Brl Brr */ // 5.0.4.4 (Sony 360RA) const SpeakerArrangement k50_4_4 = kSpeakerL | kSpeakerR | kSpeakerC | kSpeakerLs | kSpeakerRs | kSpeakerTfl | kSpeakerTfr | kSpeakerTrl | kSpeakerTrr | kSpeakerBfl | kSpeakerBfr | kSpeakerBrl | kSpeakerBrr; //------------------------------------------------------------------------ /** Speaker Arrangement String Representation. * \ingroup speakerArrangements */ /**@{*/ const CString kStringEmpty = ""; const CString kStringMono = "Mono"; const CString kStringStereo = "Stereo"; const CString kStringStereoWide = "Stereo (Lw Rw)"; const CString kStringStereoR = "Stereo (Ls Rs)"; const CString kStringStereoC = "Stereo (Lc Rc)"; const CString kStringStereoSide = "Stereo (Sl Sr)"; const CString kStringStereoCLfe = "Stereo (C LFE)"; const CString kStringStereoTF = "Stereo (Tfl Tfr)"; const CString kStringStereoTS = "Stereo (Tsl Tsr)"; const CString kStringStereoTR = "Stereo (Trl Trr)"; const CString kStringStereoBF = "Stereo (Bfl Bfr)"; const CString kStringCineFront = "Cine Front"; const CString kString30Cine = "LRC"; const CString kString30Music = "LRS"; const CString kString31Cine = "LRC+LFE"; const CString kString31Music = "LRS+LFE"; const CString kString40Cine = "LRCS"; const CString kString40Music = "Quadro"; const CString kString41Cine = "LRCS+LFE"; const CString kString41Music = "Quadro+LFE"; const CString kString50 = "5.0"; const CString kString51 = "5.1"; const CString kString60Cine = "6.0 Cine"; const CString kString60Music = "6.0 Music"; const CString kString61Cine = "6.1 Cine"; const CString kString61Music = "6.1 Music"; const CString kString70Cine = "7.0 SDDS"; const CString kString70CineOld = "7.0 Cine (SDDS)"; const CString kString70Music = "7.0"; const CString kString70MusicOld = "7.0 Music (Dolby)"; const CString kString71Cine = "7.1 SDDS"; const CString kString71CineOld = "7.1 Cine (SDDS)"; const CString kString71Music = "7.1"; const CString kString71MusicOld = "7.1 Music (Dolby)"; const CString kString71CineTopCenter = "7.1 Cine Top Center"; const CString kString71CineCenterHigh = "7.1 Cine Center High"; const CString kString71CineFullRear = "7.1 Cine Full Rear"; const CString kString51_2 = "5.1.2"; const CString kString50_2 = "5.0.2"; const CString kString50_2TopSide = "5.0.2 Top Side"; const CString kString51_2TopSide = "5.1.2 Top Side"; const CString kString71Proximity = "7.1 Proximity"; const CString kString80Cine = "8.0 Cine"; const CString kString80Music = "8.0 Music"; const CString kString40_4 = "8.0 Cube"; const CString kString81Cine = "8.1 Cine"; const CString kString81Music = "8.1 Music"; const CString kString90Cine = "9.0 Cine"; const CString kString91Cine = "9.1 Cine"; const CString kString100Cine = "10.0 Cine"; const CString kString101Cine = "10.1 Cine"; const CString kString52_5 = "5.2.5"; const CString kString72_5 = "12.2"; const CString kString50_4 = "5.0.4"; const CString kString51_4 = "5.1.4"; const CString kString50_4_1 = "5.0.4.1"; const CString kString51_4_1 = "5.1.4.1"; const CString kString70_2 = "7.0.2"; const CString kString71_2 = "7.1.2"; const CString kString70_2_TF = "7.0.2 Top Front"; const CString kString71_2_TF = "7.1.2 Top Front"; const CString kString70_3 = "7.0.3"; const CString kString72_3 = "7.2.3"; const CString kString70_4 = "7.0.4"; const CString kString71_4 = "7.1.4"; const CString kString70_6 = "7.0.6"; const CString kString71_6 = "7.1.6"; const CString kString90_4 = "9.0.4 ITU"; const CString kString91_4 = "9.1.4 ITU"; const CString kString90_6 = "9.0.6 ITU"; const CString kString91_6 = "9.1.6 ITU"; const CString kString90_4_W = "9.0.4"; const CString kString91_4_W = "9.1.4"; const CString kString90_6_W = "9.0.6"; const CString kString91_6_W = "9.1.6"; const CString kString50_5 = "10.0 Auro-3D"; const CString kString51_5 = "10.1 Auro-3D"; const CString kString50_6 = "11.0 Auro-3D"; const CString kString51_6 = "11.1 Auro-3D"; const CString kString130 = "13.0 Auro-3D"; const CString kString131 = "13.1 Auro-3D"; const CString kString41_4_1 = "8.1 MPEG"; const CString kString60_4_4 = "14.0"; const CString kString220 = "22.0"; const CString kString222 = "22.2"; const CString kString50_5_3 = "5.0.5.3"; const CString kString51_5_3 = "5.1.5.3"; const CString kString50_2_2 = "5.0.2.2"; const CString kString50_4_2 = "5.0.4.2"; const CString kString70_4_2 = "7.0.4.2"; const CString kString50_5_Sony = "5.0.5 Sony"; const CString kString40_2_2 = "4.0.3.2"; const CString kString40_4_2 = "4.0.4.2"; const CString kString50_3_2 = "5.0.3.2"; const CString kString30_5_2 = "3.0.5.2"; const CString kString40_4_4 = "4.0.4.4"; const CString kString50_4_4 = "5.0.4.4"; const CString kStringAmbi1stOrder = "1OA"; const CString kStringAmbi2cdOrder = "2OA"; const CString kStringAmbi3rdOrder = "3OA"; const CString kStringAmbi4thOrder = "4OA"; const CString kStringAmbi5thOrder = "5OA"; const CString kStringAmbi6thOrder = "6OA"; const CString kStringAmbi7thOrder = "7OA"; /**@}*/ //------------------------------------------------------------------------ /** Speaker Arrangement String Representation with Speakers Name. * \ingroup speakerArrangements */ /**@{*/ const CString kStringMonoS = "M"; const CString kStringStereoS = "L R"; const CString kStringStereoWideS = "Lw Rw"; const CString kStringStereoRS = "Ls Rs"; const CString kStringStereoCS = "Lc Rc"; const CString kStringStereoSS = "Sl Sr"; const CString kStringStereoCLfeS= "C LFE"; const CString kStringStereoTFS = "Tfl Tfr"; const CString kStringStereoTSS = "Tsl Tsr"; const CString kStringStereoTRS = "Trl Trr"; const CString kStringStereoBFS = "Bfl Bfr"; const CString kStringCineFrontS = "L R C Lc Rc"; const CString kString30CineS = "L R C"; const CString kString30MusicS = "L R S"; const CString kString31CineS = "L R C LFE"; const CString kString31MusicS = "L R LFE S"; const CString kString40CineS = "L R C S"; const CString kString40MusicS = "L R Ls Rs"; const CString kString41CineS = "L R C LFE S"; const CString kString41MusicS = "L R LFE Ls Rs"; const CString kString50S = "L R C Ls Rs"; const CString kString51S = "L R C LFE Ls Rs"; const CString kString60CineS = "L R C Ls Rs Cs"; const CString kString60MusicS = "L R Ls Rs Sl Sr"; const CString kString61CineS = "L R C LFE Ls Rs Cs"; const CString kString61MusicS = "L R LFE Ls Rs Sl Sr"; const CString kString70CineS = "L R C Ls Rs Lc Rc"; const CString kString70MusicS = "L R C Ls Rs Sl Sr"; const CString kString71CineS = "L R C LFE Ls Rs Lc Rc"; const CString kString71MusicS = "L R C LFE Ls Rs Sl Sr"; const CString kString80CineS = "L R C Ls Rs Lc Rc Cs"; const CString kString80MusicS = "L R C Ls Rs Cs Sl Sr"; const CString kString81CineS = "L R C LFE Ls Rs Lc Rc Cs"; const CString kString81MusicS = "L R C LFE Ls Rs Cs Sl Sr"; const CString kString40_4S = "L R Ls Rs Tfl Tfr Trl Trr"; const CString kString71CineTopCenterS = "L R C LFE Ls Rs Cs Tc"; const CString kString71CineCenterHighS = "L R C LFE Ls Rs Cs Tfc"; const CString kString71CineFullRearS = "L R C LFE Ls Rs Lcs Rcs"; const CString kString50_2S = "L R C Ls Rs Tfl Tfr"; const CString kString51_2S = "L R C LFE Ls Rs Tfl Tfr"; const CString kString50_2TopSideS = "L R C Ls Rs Tsl Tsr"; const CString kString51_2TopSideS = "L R C LFE Ls Rs Tsl Tsr"; const CString kString71ProximityS = "L R C LFE Ls Rs Pl Pr"; const CString kString90CineS = "L R C Ls Rs Lc Rc Sl Sr"; const CString kString91CineS = "L R C LFE Ls Rs Lc Rc Sl Sr"; const CString kString100CineS = "L R C Ls Rs Lc Rc Cs Sl Sr"; const CString kString101CineS = "L R C LFE Ls Rs Lc Rc Cs Sl Sr"; const CString kString50_4S = "L R C Ls Rs Tfl Tfr Trl Trr"; const CString kString51_4S = "L R C LFE Ls Rs Tfl Tfr Trl Trr"; const CString kString50_4_1S = "L R C Ls Rs Tfl Tfr Trl Trr Bfc"; const CString kString51_4_1S = "L R C LFE Ls Rs Tfl Tfr Trl Trr Bfc"; const CString kString70_2S = "L R C Ls Rs Sl Sr Tsl Tsr"; const CString kString71_2S = "L R C LFE Ls Rs Sl Sr Tsl Tsr"; const CString kString70_2_TFS = "L R C Ls Rs Sl Sr Tfl Tfr"; const CString kString71_2_TFS = "L R C LFE Ls Rs Sl Sr Tfl Tfr"; const CString kString70_3S = "L R C Ls Rs Sl Sr Tfl Tfr Trc"; const CString kString72_3S = "L R C LFE Ls Rs Sl Sr Tfl Tfr Trc LFE2"; const CString kString70_4S = "L R C Ls Rs Sl Sr Tfl Tfr Trl Trr"; const CString kString71_4S = "L R C LFE Ls Rs Sl Sr Tfl Tfr Trl Trr"; const CString kString70_6S = "L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr"; const CString kString71_6S = "L R C LFE Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr"; const CString kString90_4S = "L R C Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr"; const CString kString91_4S = "L R C LFE Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr"; const CString kString90_6S = "L R C Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr Tsl Tsr"; const CString kString91_6S = "L R C LFE Ls Rs Lc Rc Sl Sr Tfl Tfr Trl Trr Tsl Tsr"; const CString kString90_4_WS = "L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Lw Rw"; const CString kString91_4_WS = "L R C LFE Ls Rs Sl Sr Tfl Tfr Trl Trr Lw Rw"; const CString kString90_6_WS = "L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr Lw Rw"; const CString kString91_6_WS = "L R C LFE Ls Rs Sl Sr Tfl Tfr Trl Trr Tsl Tsr Lw Rw"; const CString kString50_5S = "L R C Ls Rs Tc Tfl Tfr Trl Trr"; const CString kString51_5S = "L R C LFE Ls Rs Tc Tfl Tfr Trl Trr"; const CString kString50_5_SonyS = "L R C Ls Rs Tfl Tfc Tfr Trl Trr"; const CString kString50_6S = "L R C Ls Rs Tc Tfl Tfc Tfr Trl Trr"; const CString kString51_6S = "L R C LFE Ls Rs Tc Tfl Tfc Tfr Trl Trr"; const CString kString130S = "L R C Ls Rs Sl Sr Tc Tfl Tfc Tfr Trl Trr"; const CString kString131S = "L R C LFE Ls Rs Sl Sr Tc Tfl Tfc Tfr Trl Trr"; const CString kString52_5S = "L R C LFE Ls Rs Tfl Tfc Tfr Trl Trr LFE2"; const CString kString72_5S = "L R C LFE Ls Rs Lc Rc Tfl Tfc Tfr Trl Trr LFE2"; const CString kString41_4_1S = "L R LFE Ls Rs Tfl Tfc Tfr Bfc"; const CString kString30_5_2S = "L R C Tfl Tfc Tfr Trl Trr Bfl Bfr"; const CString kString40_2_2S = "C Sl Sr Cs Tfc Tsl Tsr Trc"; const CString kString40_4_2S = "L R Ls Rs Tfl Tfr Trl Trr Bfl Bfr"; const CString kString40_4_4S = "L R Ls Rs Tfl Tfr Trl Trr Bfl Bfr Brl Brr"; const CString kString50_4_4S = "L R C Ls Rs Tfl Tfr Trl Trr Bfl Bfr Brl Brr"; const CString kString60_4_4S = "L R Ls Rs Sl Sr Tfl Tfr Trl Trr Bfl Bfr Brl Brr"; const CString kString50_5_3S = "L R C Ls Rs Tfl Tfc Tfr Trl Trr Bfl Bfc Bfr"; const CString kString51_5_3S = "L R C LFE Ls Rs Tfl Tfc Tfr Trl Trr Bfl Bfc Bfr"; const CString kString50_2_2S = "L R C Ls Rs Tsl Tsr Bfl Bfr"; const CString kString50_3_2S = "L R C Ls Rs Tfl Tfc Tfr Bfl Bfr"; const CString kString50_4_2S = "L R C Ls Rs Tfl Tfr Trl Trr Bfl Bfr"; const CString kString70_4_2S = "L R C Ls Rs Sl Sr Tfl Tfr Trl Trr Bfl Bfr"; const CString kString222S = "L R C LFE Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr LFE2 Tsl Tsr Bfl Bfc Bfr"; const CString kString220S = "L R C Ls Rs Lc Rc Cs Sl Sr Tc Tfl Tfc Tfr Trl Trc Trr Tsl Tsr Bfl Bfc Bfr"; const CString kStringAmbi1stOrderS = "0 1 2 3"; const CString kStringAmbi2cdOrderS = "0 1 2 3 4 5 6 7 8"; const CString kStringAmbi3rdOrderS = "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"; const CString kStringAmbi4thOrderS = "0..24"; const CString kStringAmbi5thOrderS = "0..35"; const CString kStringAmbi6thOrderS = "0..48"; const CString kStringAmbi7thOrderS = "0..63"; /**@}*/ //------------------------------------------------------------------------ /** Returns number of channels used in speaker arrangement. * \ingroup speakerArrangements */ /**@{*/ inline int32 getChannelCount (SpeakerArrangement arr) { int32 count = 0; while (arr) { if (arr & (SpeakerArrangement)1) ++count; arr >>= 1; } return count; } //------------------------------------------------------------------------ /** Returns the index of a given speaker in a speaker arrangement (-1 if speaker is not part of the * arrangement). * \ingroup speakerArrangements */ inline int32 getSpeakerIndex (Speaker speaker, SpeakerArrangement arrangement) { // check if speaker is present in arrangement if ((arrangement & speaker) == 0) return -1; int32 result = 0; Speaker i = 1; while (i < speaker) { if (arrangement & i) result++; i <<= 1; } return result; } //------------------------------------------------------------------------ /** Returns the speaker for a given index in a speaker arrangement * Return 0 when out of range. * \ingroup speakerArrangements */ inline Speaker getSpeaker (const SpeakerArrangement& arr, int32 index) { SpeakerArrangement arrTmp = arr; int32 index2 = -1; int32 pos = -1; while (arrTmp) { if (arrTmp & 0x1) index2++; pos++; if (index2 == index) return (Speaker)1 << pos; arrTmp = arrTmp >> 1; } return 0; } //------------------------------------------------------------------------ /** Returns true if arrSubSet is a subset speaker of arr (means each speaker of arrSubSet is * included in arr). * \ingroup speakerArrangements */ inline bool isSubsetOf (const SpeakerArrangement& arrSubSet, const SpeakerArrangement& arr) { return (arrSubSet == (arrSubSet & arr)); } //------------------------------------------------------------------------ /** Returns true if arrangement is a Auro configuration. * \ingroup speakerArrangements */ inline bool isAuro (const SpeakerArrangement& arr) { if (arr == k90 || arr == k91 || arr == k100 || arr == k101 || arr == k110 || arr == k111 || arr == k130 || arr == k131) { return true; } return false; } //------------------------------------------------------------------------ /** Returns true if arrangement contains top (upper layer) speakers * \ingroup speakerArrangements */ inline bool hasTopSpeakers (const SpeakerArrangement& arr) { if (arr & kSpeakerTc || arr & kSpeakerTfl || arr & kSpeakerTfc || arr & kSpeakerTfr || arr & kSpeakerTrl || arr & kSpeakerTrc || arr & kSpeakerTrr || arr & kSpeakerTsl || arr & kSpeakerTsr) return true; return false; } //------------------------------------------------------------------------ /** Returns true if arrangement contains bottom (lower layer) speakers * \ingroup speakerArrangements */ inline bool hasBottomSpeakers (const SpeakerArrangement& arr) { if (arr & kSpeakerBfl || arr & kSpeakerBfc || arr & kSpeakerBfr || arr & kSpeakerBsl || arr & kSpeakerBsr || arr & kSpeakerBrr || arr & kSpeakerBrl || arr & kSpeakerBrc) return true; return false; } //------------------------------------------------------------------------ /** Returns true if arrangement contains middle layer (at ears level) speakers * \ingroup speakerArrangements */ inline bool hasMiddleSpeakers (const SpeakerArrangement& arr) { if (arr & kSpeakerL || arr & kSpeakerR || arr & kSpeakerC || arr & kSpeakerLs || arr & kSpeakerRs || arr & kSpeakerLc || arr & kSpeakerRc || arr & kSpeakerCs || arr & kSpeakerSl || arr & kSpeakerSr || arr & kSpeakerM || arr & kSpeakerPl || arr & kSpeakerPr || arr & kSpeakerLcs || arr & kSpeakerRcs || arr & kSpeakerLw || arr & kSpeakerRw) return true; return false; } //------------------------------------------------------------------------ /** Returns true if arrangement contains LFE speakers * \ingroup speakerArrangements */ inline bool hasLfe (const SpeakerArrangement& arr) { if (arr & kSpeakerLfe || arr & kSpeakerLfe2) return true; return false; } //------------------------------------------------------------------------ /** Returns true if arrangement is a 3D configuration ((top or bottom) and middle) * \ingroup speakerArrangements */ inline bool is3D (const SpeakerArrangement& arr) { bool top = hasTopSpeakers (arr); bool bottom = hasBottomSpeakers (arr); bool middle = hasMiddleSpeakers (arr); if (((top || bottom) && middle) || (top && bottom)) return true; return false; } //------------------------------------------------------------------------ /** Returns true if arrangement is a Ambisonic configuration. * \ingroup speakerArrangements */ inline bool isAmbisonics (const SpeakerArrangement& arr) { if (arr == kAmbi1stOrderACN || arr == kAmbi2cdOrderACN || arr == kAmbi3rdOrderACN || arr == kAmbi4thOrderACN || arr == kAmbi5thOrderACN || arr == kAmbi6thOrderACN || arr == kAmbi7thOrderACN) { return true; } return false; } //------------------------------------------------------------------------ /** Converts a speaker of a Ambisonic order 1 to 4 to a Ambisonic order 7 (5 to 7) * Return 0 when out of range. * \ingroup speakerArrangements */ inline Speaker convertSpeaker_Ambi_1234Order_to_Ambi567Order (Speaker speaker_1234_order) { int32 idx = getSpeakerIndex (speaker_1234_order, kAmbi4thOrderACN); if (idx < 0) return 0; return (Speaker)1 << idx; } //------------------------------------------------------------------------ /** Converts a speaker of a Ambisonic order 5 to 7 to a Ambisonic order 4 (1 to 4). * Return 0 when out of range. * \ingroup speakerArrangements */ inline Speaker convertSpeaker_Ambi_567Order_to_Ambi1234Order (Speaker speaker_567_order) { int32 idx = getSpeakerIndex (speaker_567_order, kAmbi7thOrderACN); if (idx < 0) return 0; return getSpeaker (kAmbi4thOrderACN, idx); } //------------------------------------------------------------------------ /** Returns the speaker arrangement associated to a string representation. * Returns kEmpty if no associated arrangement is known. * \ingroup speakerArrangements */ inline SpeakerArrangement getSpeakerArrangementFromString (CString arrStr) { if (!strcmp8 (arrStr, kStringMono)) return kMono; if (!strcmp8 (arrStr, kStringStereo)) return kStereo; if (!strcmp8 (arrStr, kStringStereoR)) return kStereoSurround; if (!strcmp8 (arrStr, kStringStereoWide)) return kStereoWide; if (!strcmp8 (arrStr, kStringStereoC)) return kStereoCenter; if (!strcmp8 (arrStr, kStringStereoSide)) return kStereoSide; if (!strcmp8 (arrStr, kStringStereoCLfe)) return kStereoCLfe; if (!strcmp8 (arrStr, kStringStereoTF)) return kStereoTF; if (!strcmp8 (arrStr, kStringStereoTS)) return kStereoTS; if (!strcmp8 (arrStr, kStringStereoTR)) return kStereoTR; if (!strcmp8 (arrStr, kStringStereoBF)) return kStereoBF; if (!strcmp8 (arrStr, kStringCineFront)) return kCineFront; if (!strcmp8 (arrStr, kString30Cine)) return k30Cine; if (!strcmp8 (arrStr, kString30Music)) return k30Music; if (!strcmp8 (arrStr, kString31Cine)) return k31Cine; if (!strcmp8 (arrStr, kString31Music)) return k31Music; if (!strcmp8 (arrStr, kString40Cine)) return k40Cine; if (!strcmp8 (arrStr, kString40Music)) return k40Music; if (!strcmp8 (arrStr, kString41Cine)) return k41Cine; if (!strcmp8 (arrStr, kString41Music)) return k41Music; if (!strcmp8 (arrStr, kString50)) return k50; if (!strcmp8 (arrStr, kString51)) return k51; if (!strcmp8 (arrStr, kString60Cine)) return k60Cine; if (!strcmp8 (arrStr, kString60Music)) return k60Music; if (!strcmp8 (arrStr, kString61Cine)) return k61Cine; if (!strcmp8 (arrStr, kString61Music)) return k61Music; if (!strcmp8 (arrStr, kString70Cine) || !strcmp8 (arrStr, kString70CineOld)) return k70Cine; if (!strcmp8 (arrStr, kString70Music) || !strcmp8 (arrStr, kString70MusicOld)) return k70Music; if (!strcmp8 (arrStr, kString71Cine) || !strcmp8 (arrStr, kString71CineOld)) return k71Cine; if (!strcmp8 (arrStr, kString71Music) || !strcmp8 (arrStr, kString71MusicOld)) return k71Music; if (!strcmp8 (arrStr, kString71Proximity)) return k71Proximity; if (!strcmp8 (arrStr, kString80Cine)) return k80Cine; if (!strcmp8 (arrStr, kString80Music)) return k80Music; if (!strcmp8 (arrStr, kString81Cine)) return k81Cine; if (!strcmp8 (arrStr, kString81Music)) return k81Music; if (!strcmp8 (arrStr, kString52_5)) return k52_5; if (!strcmp8 (arrStr, kString72_5)) return k72_5; if (!strcmp8 (arrStr, kString40_4)) return k40_4; if (!strcmp8 (arrStr, kString71CineTopCenter)) return k71CineTopCenter; if (!strcmp8 (arrStr, kString71CineCenterHigh)) return k71CineCenterHigh; if (!strcmp8 (arrStr, kString50_2)) return k50_2; if (!strcmp8 (arrStr, kString51_2)) return k51_2; if (!strcmp8 (arrStr, kString50_2TopSide)) return k50_2_TS; if (!strcmp8 (arrStr, kString51_2TopSide)) return k51_2_TS; if (!strcmp8 (arrStr, kString71CineFullRear)) return k71CineFullRear; if (!strcmp8 (arrStr, kString90Cine)) return k90Cine; if (!strcmp8 (arrStr, kString91Cine)) return k91Cine; if (!strcmp8 (arrStr, kString100Cine)) return k100Cine; if (!strcmp8 (arrStr, kString101Cine)) return k101Cine; if (!strcmp8 (arrStr, kString50_4)) return k50_4; if (!strcmp8 (arrStr, kString51_4)) return k51_4; if (!strcmp8 (arrStr, kString50_4_1)) return k50_4_1; if (!strcmp8 (arrStr, kString51_4_1)) return k51_4_1; if (!strcmp8 (arrStr, kString41_4_1)) return k41_4_1; if (!strcmp8 (arrStr, kString70_2)) return k70_2; if (!strcmp8 (arrStr, kString71_2)) return k71_2; if (!strcmp8 (arrStr, kString70_2_TF)) return k70_2_TF; if (!strcmp8 (arrStr, kString71_2_TF)) return k71_2_TF; if (!strcmp8 (arrStr, kString70_3)) return k70_3; if (!strcmp8 (arrStr, kString72_3)) return k72_3; if (!strcmp8 (arrStr, kString70_4)) return k70_4; if (!strcmp8 (arrStr, kString71_4)) return k71_4; if (!strcmp8 (arrStr, kString70_6)) return k70_6; if (!strcmp8 (arrStr, kString71_6)) return k71_6; if (!strcmp8 (arrStr, kString90_4)) return k90_4; if (!strcmp8 (arrStr, kString91_4)) return k91_4; if (!strcmp8 (arrStr, kString90_6)) return k90_6; if (!strcmp8 (arrStr, kString91_6)) return k91_6; if (!strcmp8 (arrStr, kString90_4_W)) return k90_4_W; if (!strcmp8 (arrStr, kString91_4_W)) return k91_4_W; if (!strcmp8 (arrStr, kString90_6_W)) return k90_6_W; if (!strcmp8 (arrStr, kString91_6_W)) return k91_6_W; if (!strcmp8 (arrStr, kString50_5)) return k50_5; if (!strcmp8 (arrStr, kString51_5)) return k51_5; if (!strcmp8 (arrStr, kString50_6)) return k50_6; if (!strcmp8 (arrStr, kString51_6)) return k51_6; if (!strcmp8 (arrStr, kString130)) return k130; if (!strcmp8 (arrStr, kString131)) return k131; if (!strcmp8 (arrStr, kString60_4_4)) return k60_4_4; if (!strcmp8 (arrStr, kString222)) return k222; if (!strcmp8 (arrStr, kString220)) return k220; if (!strcmp8 (arrStr, kString50_5_3)) return k50_5_3; if (!strcmp8 (arrStr, kString51_5_3)) return k51_5_3; if (!strcmp8 (arrStr, kString50_2_2)) return k50_2_2; if (!strcmp8 (arrStr, kString50_4_2)) return k50_4_2; if (!strcmp8 (arrStr, kString70_4_2)) return k70_4_2; if (!strcmp8 (arrStr, kString50_5_Sony)) return k50_5_Sony; if (!strcmp8 (arrStr, kString40_2_2)) return k40_2_2; if (!strcmp8 (arrStr, kString40_4_2)) return k40_4_2; if (!strcmp8 (arrStr, kString50_3_2)) return k50_3_2; if (!strcmp8 (arrStr, kString30_5_2)) return k30_5_2; if (!strcmp8 (arrStr, kString40_4_4)) return k40_4_4; if (!strcmp8 (arrStr, kString50_4_4)) return k50_4_4; if (!strcmp8 (arrStr, kStringAmbi1stOrder)) return kAmbi1stOrderACN; if (!strcmp8 (arrStr, kStringAmbi2cdOrder)) return kAmbi2cdOrderACN; if (!strcmp8 (arrStr, kStringAmbi3rdOrder)) return kAmbi3rdOrderACN; if (!strcmp8 (arrStr, kStringAmbi4thOrder)) return kAmbi4thOrderACN; if (!strcmp8 (arrStr, kStringAmbi5thOrder)) return kAmbi5thOrderACN; if (!strcmp8 (arrStr, kStringAmbi6thOrder)) return kAmbi6thOrderACN; if (!strcmp8 (arrStr, kStringAmbi7thOrder)) return kAmbi7thOrderACN; return kEmpty; } //------------------------------------------------------------------------ /** Returns the string representation of a given speaker arrangement. * Returns kStringEmpty if arr is unknown. * \ingroup speakerArrangements */ inline CString getSpeakerArrangementString (SpeakerArrangement arr, bool withSpeakersName) { switch (arr) { case kMono: return withSpeakersName ? kStringMonoS : kStringMono; //--- Stereo pairs--- case kStereo: return withSpeakersName ? kStringStereoS : kStringStereo; case kStereoSurround: return withSpeakersName ? kStringStereoRS : kStringStereoR; case kStereoWide: return withSpeakersName ? kStringStereoWideS : kStringStereoWide; case kStereoCenter: return withSpeakersName ? kStringStereoCS : kStringStereoC; case kStereoSide: return withSpeakersName ? kStringStereoSS : kStringStereoSide; case kStereoCLfe: return withSpeakersName ? kStringStereoCLfeS: kStringStereoCLfe; case kStereoTF: return withSpeakersName ? kStringStereoTFS : kStringStereoTF; case kStereoTS: return withSpeakersName ? kStringStereoTSS : kStringStereoTS; case kStereoTR: return withSpeakersName ? kStringStereoTRS : kStringStereoTR; case kStereoBF: return withSpeakersName ? kStringStereoBFS : kStringStereoBF; //--- --- case kCineFront: return withSpeakersName ? kStringCineFrontS : kStringCineFront; case k30Cine: return withSpeakersName ? kString30CineS : kString30Cine; case k31Cine: return withSpeakersName ? kString31CineS : kString31Cine; case k30Music: return withSpeakersName ? kString30MusicS : kString30Music; case k31Music: return withSpeakersName ? kString31MusicS : kString31Music; case k40Cine: return withSpeakersName ? kString40CineS : kString40Cine; case k41Cine: return withSpeakersName ? kString41CineS : kString41Cine; case k40Music: return withSpeakersName ? kString40MusicS : kString40Music; case k41Music: return withSpeakersName ? kString41MusicS : kString41Music; case k50: return withSpeakersName ? kString50S : kString50; case k51: return withSpeakersName ? kString51S : kString51; case k60Cine: return withSpeakersName ? kString60CineS : kString60Cine; case k61Cine: return withSpeakersName ? kString61CineS : kString61Cine; case k60Music: return withSpeakersName ? kString60MusicS : kString60Music; case k61Music: return withSpeakersName ? kString61MusicS : kString61Music; case k70Cine: return withSpeakersName ? kString70CineS : kString70Cine; case k71Cine: return withSpeakersName ? kString71CineS : kString71Cine; case k70Music: return withSpeakersName ? kString70MusicS : kString70Music; case k71Music: return withSpeakersName ? kString71MusicS : kString71Music; case k71Proximity: return withSpeakersName ? kString71ProximityS : kString71Proximity; case k80Cine: return withSpeakersName ? kString80CineS : kString80Cine; case k81Cine: return withSpeakersName ? kString81CineS : kString81Cine; case k80Music: return withSpeakersName ? kString80MusicS : kString80Music; case k81Music: return withSpeakersName ? kString81MusicS : kString81Music; case k71CineFullRear: return withSpeakersName ? kString71CineFullRearS : kString71CineFullRear; case k90Cine: return withSpeakersName ? kString90CineS : kString90Cine; case k91Cine: return withSpeakersName ? kString91CineS : kString91Cine; case k100Cine: return withSpeakersName ? kString100CineS : kString100Cine; case k101Cine: return withSpeakersName ? kString101CineS : kString101Cine; //---With Tops --- case k71CineTopCenter: return withSpeakersName ? kString71CineTopCenterS : kString71CineTopCenter; case k71CineCenterHigh: return withSpeakersName ? kString71CineCenterHighS : kString71CineCenterHigh; case k50_2_TS: return withSpeakersName ? kString50_2TopSideS : kString50_2TopSide; case k51_2_TS: return withSpeakersName ? kString51_2TopSideS : kString51_2TopSide; case k40_4: return withSpeakersName ? kString40_4S : kString40_4; case k50_2: return withSpeakersName ? kString50_2S : kString50_2; case k51_2: return withSpeakersName ? kString51_2S : kString51_2; case k50_4: return withSpeakersName ? kString50_4S : kString50_4; case k51_4: return withSpeakersName ? kString51_4S : kString51_4; case k50_5: return withSpeakersName ? kString50_5S : kString50_5; case k51_5: return withSpeakersName ? kString51_5S : kString51_5; case k52_5: return withSpeakersName ? kString52_5S : kString52_5; case k50_6: return withSpeakersName ? kString50_6S : kString50_6; case k51_6: return withSpeakersName ? kString51_6S : kString51_6; case k70_2: return withSpeakersName ? kString70_2S : kString70_2; case k71_2: return withSpeakersName ? kString71_2S : kString71_2; case k70_2_TF: return withSpeakersName ? kString70_2_TFS : kString70_2_TF; case k71_2_TF: return withSpeakersName ? kString71_2_TFS : kString71_2_TF; case k70_3: return withSpeakersName ? kString70_3S : kString70_3; case k72_3: return withSpeakersName ? kString72_3S : kString72_3; case k70_4: return withSpeakersName ? kString70_4S : kString70_4; case k71_4: return withSpeakersName ? kString71_4S : kString71_4; case k72_5: return withSpeakersName ? kString72_5S : kString72_5; case k70_6: return withSpeakersName ? kString70_6S : kString70_6; case k71_6: return withSpeakersName ? kString71_6S : kString71_6; case k90_4: return withSpeakersName ? kString90_4S : kString90_4; case k91_4: return withSpeakersName ? kString91_4S : kString91_4; case k90_6: return withSpeakersName ? kString90_6S : kString90_6; case k91_6: return withSpeakersName ? kString91_6S : kString91_6; case k90_4_W: return withSpeakersName ? kString90_4_WS : kString90_4_W; case k91_4_W: return withSpeakersName ? kString91_4_WS : kString91_4_W; case k90_6_W: return withSpeakersName ? kString90_6_WS : kString90_6_W; case k91_6_W: return withSpeakersName ? kString91_6_WS : kString91_6_W; case k130: return withSpeakersName ? kString130S : kString130; case k131: return withSpeakersName ? kString131S : kString131; //--- With Tops and Bottoms --- case k41_4_1: return withSpeakersName ? kString41_4_1S : kString41_4_1; case k50_4_1: return withSpeakersName ? kString50_4_1S : kString50_4_1; case k51_4_1: return withSpeakersName ? kString51_4_1S : kString51_4_1; case k50_5_3: return withSpeakersName ? kString50_5_3S : kString50_5_3; case k51_5_3: return withSpeakersName ? kString51_5_3S : kString51_5_3; case k50_2_2: return withSpeakersName ? kString50_2_2S : kString50_2_2; case k50_4_2: return withSpeakersName ? kString50_4_2S : kString50_4_2; case k60_4_4: return withSpeakersName ? kString60_4_4S : kString60_4_4; case k70_4_2: return withSpeakersName ? kString70_4_2S : kString70_4_2; case k50_5_Sony: return withSpeakersName ? kString50_5_SonyS : kString50_5_Sony; case k40_2_2: return withSpeakersName ? kString40_2_2S : kString40_2_2; case k40_4_2: return withSpeakersName ? kString40_4_2S : kString40_4_2; case k50_3_2: return withSpeakersName ? kString50_3_2S : kString50_3_2; case k30_5_2: return withSpeakersName ? kString30_5_2S : kString30_5_2; case k40_4_4: return withSpeakersName ? kString40_4_4S : kString40_4_4; case k50_4_4: return withSpeakersName ? kString50_4_4S : kString50_4_4; case k220: return withSpeakersName ? kString220S : kString220; case k222: return withSpeakersName ? kString222S : kString222; } //--- Ambisonics --- if (arr == kAmbi1stOrderACN) return withSpeakersName ? kStringAmbi1stOrderS : kStringAmbi1stOrder; if (arr == kAmbi2cdOrderACN) return withSpeakersName ? kStringAmbi2cdOrderS : kStringAmbi2cdOrder; if (arr == kAmbi3rdOrderACN) return withSpeakersName ? kStringAmbi3rdOrderS : kStringAmbi3rdOrder; if (arr == kAmbi4thOrderACN) return withSpeakersName ? kStringAmbi4thOrderS : kStringAmbi4thOrder; if (arr == kAmbi5thOrderACN) return withSpeakersName ? kStringAmbi5thOrderS : kStringAmbi5thOrder; if (arr == kAmbi6thOrderACN) return withSpeakersName ? kStringAmbi6thOrderS : kStringAmbi6thOrder; if (arr == kAmbi7thOrderACN) return withSpeakersName ? kStringAmbi7thOrderS : kStringAmbi7thOrder; return kStringEmpty; } //------------------------------------------------------------------------ /** Returns a CString representation of a given speaker in a given arrangement. * \ingroup speakerArrangements */ inline CString getSpeakerShortName (const SpeakerArrangement& arr, int32 index) { SpeakerArrangement arrTmp = arr; bool found = false; int32 index2 = -1; int32 pos = -1; while (arrTmp) { if (arrTmp & 0x1) index2++; pos++; if (index2 == index) { found = true; break; } arrTmp = arrTmp >> 1; } if (!found) return ""; Speaker speaker = (Speaker)1 << pos; if (speaker == kSpeakerL) return "L"; if (speaker == kSpeakerR) return "R"; if (speaker == kSpeakerC) return "C"; if (speaker == kSpeakerLfe) return "LFE"; if (speaker == kSpeakerLs) return "Ls"; if (speaker == kSpeakerRs) return "Rs"; if (speaker == kSpeakerLc) return "Lc"; if (speaker == kSpeakerRc) return "Rc"; if (speaker == kSpeakerCs) return "S"; if (speaker == kSpeakerSl) return "Sl"; if (speaker == kSpeakerSr) return "Sr"; if (speaker == kSpeakerTc) return "Tc"; if (speaker == kSpeakerTfl) return "Tfl"; if (speaker == kSpeakerTfc) return "Tfc"; if (speaker == kSpeakerTfr) return "Tfr"; if (speaker == kSpeakerTrl) return "Trl"; if (speaker == kSpeakerTrc) return "Trc"; if (speaker == kSpeakerTrr) return "Trr"; if (speaker == kSpeakerLfe2) return "LFE2"; if (speaker == kSpeakerM) return "M"; if (speaker == kSpeakerACN0) return "0"; if (speaker == kSpeakerACN1) return "1"; if (speaker == kSpeakerACN2) return "2"; if (speaker == kSpeakerACN3) return "3"; if (speaker == kSpeakerACN4) return "4"; if (speaker == kSpeakerACN5) return "5"; if (speaker == kSpeakerACN6) return "6"; if (speaker == kSpeakerACN7) return "7"; if (speaker == kSpeakerACN8) return "8"; if (speaker == kSpeakerACN9) return "9"; if (speaker == kSpeakerACN10) return "10"; if (speaker == kSpeakerACN11) return "11"; if (speaker == kSpeakerACN12) return "12"; if (speaker == kSpeakerACN13) return "13"; if (speaker == kSpeakerACN14) return "14"; if (speaker == kSpeakerACN15) return "15"; if (speaker == kSpeakerACN16) return "16"; if (speaker == kSpeakerACN17) return "17"; if (speaker == kSpeakerACN18) return "18"; if (speaker == kSpeakerACN19) return "19"; if (speaker == kSpeakerACN20) return "20"; if (speaker == kSpeakerACN21) return "21"; if (speaker == kSpeakerACN22) return "22"; if (speaker == kSpeakerACN23) return "23"; if (speaker == kSpeakerACN24) return "24"; if (speaker == kSpeakerTsl) return "Tsl"; if (speaker == kSpeakerTsr) return "Tsr"; if (speaker == kSpeakerLcs) return "Lcs"; if (speaker == kSpeakerRcs) return "Rcs"; if (speaker == kSpeakerBfl) return "Bfl"; if (speaker == kSpeakerBfc) return "Bfc"; if (speaker == kSpeakerBfr) return "Bfr"; if (speaker == kSpeakerPl) return "Pl"; if (speaker == kSpeakerPr) return "Pr"; if (speaker == kSpeakerBsl) return "Bsl"; if (speaker == kSpeakerBsr) return "Bsr"; if (speaker == kSpeakerBrl) return "Brl"; if (speaker == kSpeakerBrc) return "Brc"; if (speaker == kSpeakerBrr) return "Brr"; if (speaker == kSpeakerLw) return "Lw"; if (speaker == kSpeakerRw) return "Rw"; return ""; } /**@}*/ //------------------------------------------------------------------------ } // namespace SpeakerArr } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstcontextmenu.h0000644000000000000000000000013215124701711023460 xustar0030 mtime=1767080905.194000456 30 atime=1767080905.194000456 30 ctime=1767080905.194000456 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstcontextmenu.h0000644000175000001440000002053015124701711023450 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstcontextmenu.h // Created by : Steinberg, 10/2010 // Description : VST Context Menu Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ namespace Steinberg { class IPlugView; namespace Vst { class IContextMenu; //------------------------------------------------------------------------ /** Extended host callback interface Vst::IComponentHandler3 for an edit controller. \ingroup vstIHost vst350 - [host imp] - [extends IComponentHandler] - [released: 3.5.0] - [optional] A plug-in can ask the host to create a context menu for a given exported parameter ID or a generic context menu.\n The host may pre-fill this context menu with specific items regarding the parameter ID like "Show automation for parameter", "MIDI learn" etc...\n The plug-in can use the context menu in two ways : - add its own items to the menu via the IContextMenu interface and call IContextMenu::popup(..) to create the pop-up. See the \ref IContextMenuExample. - extract the host menu items and add them to a context menu created by the plug-in. \b Note: You can and should use this even if you do not add your own items to the menu as this is considered to be a big user value. \sa IContextMenu \sa IContextMenuTarget \section IContextMenuExample Examples - For example, Cubase adds its owned entries in the context menu opened with right-click on an exported parameter when the plug-in uses createContextMenu. \image html "contextmenuexample.png" \n - Adding plug-in specific items to the context menu: \code{.cpp} //------------------------------------------------------------------------ class PluginContextMenuTarget : public IContextMenuTarget, public FObject { public: PluginContextMenuTarget () {} tresult PLUGIN_API executeMenuItem (int32 tag) override { // this will be called if the user has executed one of the menu items of the plug-in. // It will not be called for items of the host. switch (tag) { case 1: break; case 2: break; } return kResultTrue; } OBJ_METHODS(PluginContextMenuTarget, FObject) DEFINE_INTERFACES DEF_INTERFACE (IContextMenuTarget) END_DEFINE_INTERFACES (FObject) REFCOUNT_METHODS(FObject) }; // The following is the code to create the context menu void popupContextMenu (IComponentHandler* componentHandler, IPlugView* view, const ParamID* paramID, UCoord x, UCoord y) { if (componentHandler == 0 || view == 0) return; FUnknownPtr handler (componentHandler); if (handler == 0) return; if (IContextMenu* menu = handler->createContextMenu (view, paramID)) { // here you can add your entries (optional) PluginContextMenuTarget* target = new PluginContextMenuTarget (); IContextMenu::Item item = {0}; UString128 ("My Item 1").copyTo (item.name, 128); item.tag = 1; menu->addItem (item, target); UString128 ("My Item 2").copyTo (item.name, 128); item.tag = 2; menu->addItem (item, target); target->release (); //--end of adding new entries // here the the context menu will be pop-up (and it waits a user interaction) menu->popup (x, y); menu->release (); } } \endcode */ class IComponentHandler3 : public FUnknown { public: /** Creates a host context menu for a plug-in: * - If paramID is zero, the host may create a generic context menu. * - The IPlugView object must be valid. * - The return IContextMenu object needs to be released afterwards by the plug-in. * * \note [UI-thread & (Initialized | Connected) & plugView] */ virtual IContextMenu* PLUGIN_API createContextMenu (IPlugView* plugView /*in*/, const ParamID* paramID /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IComponentHandler3, 0x69F11617, 0xD26B400D, 0xA4B6B964, 0x7B6EBBAB) //------------------------------------------------------------------------ /** Context Menu Item Target interface: Vst::IContextMenuTarget \ingroup vstIHost vstIPlug vst350 - [host imp] - [plug imp] - [released: 3.5.0] - [optional] A receiver of a menu item should implement this interface, which will be called after the user has selected this menu item. \see IComponentHandler3 for more information. */ class IContextMenuTarget : public FUnknown { public: /** Called when an menu item was executed. * \note [UI-thread & (Initialized | Connected) & plugView] */ virtual tresult PLUGIN_API executeMenuItem (int32 tag /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IContextMenuTarget, 0x3CDF2E75, 0x85D34144, 0xBF86D36B, 0xD7C4894D) //------------------------------------------------------------------------ /** IContextMenuItem is an entry element of the context menu. */ struct IContextMenuItem { String128 name; ///< Name of the item int32 tag; ///< Identifier tag of the item int32 flags; ///< Flags of the item enum Flags { kIsSeparator = 1 << 0, ///< Item is a separator kIsDisabled = 1 << 1, ///< Item is disabled kIsChecked = 1 << 2, ///< Item is checked kIsGroupStart = 1 << 3 | kIsDisabled, ///< Item is a group start (like sub folder) kIsGroupEnd = 1 << 4 | kIsSeparator, ///< Item is a group end }; }; //------------------------------------------------------------------------ /** Context Menu interface: Vst::IContextMenu \ingroup vstIHost vst350 - [host imp] - [create with IComponentHandler3::createContextMenu(..)] - [released: 3.5.0] - [optional] A context menu is composed of Item (entry). A Item is defined by a name, a tag, a flag and a associated target (called when this item will be selected/executed). With IContextMenu the plug-in can retrieve a Item, add a Item, remove a Item and pop-up the menu. \see IComponentHandler3 for more information. */ class IContextMenu : public FUnknown { public: typedef IContextMenuItem Item; /** Gets the number of menu items. * \note [UI-thread] */ virtual int32 PLUGIN_API getItemCount () = 0; /** Gets a menu item and its target (target could be not assigned). * \note [UI-thread] */ virtual tresult PLUGIN_API getItem (int32 index /*in*/, Item& item /*out*/, IContextMenuTarget** target /*out*/) = 0; /** Adds a menu item and its target. * \note [UI-thread] */ virtual tresult PLUGIN_API addItem (const Item& item /*in*/, IContextMenuTarget* target /*in*/) = 0; /** Removes a menu item. * \note [UI-thread] */ virtual tresult PLUGIN_API removeItem (const Item& item /*in*/, IContextMenuTarget* target /*in*/) = 0; /** Pop-ups the menu. Coordinates are relative to the top-left position of the plug-ins view. * \note [UI-thread] */ virtual tresult PLUGIN_API popup (UCoord x /*in*/, UCoord y /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IContextMenu, 0x2E93C863, 0x0C9C4588, 0x97DBECF5, 0xAD17817D) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/vsttypes.h0000644000000000000000000000013215124701711022102 xustar0030 mtime=1767080905.195211228 30 atime=1767080905.195211228 30 ctime=1767080905.195211228 qtractor-1.5.11/src/vst3/pluginterfaces/vst/vsttypes.h0000644000175000001440000001736315124701711022104 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/vsttypes.h // Created by : Steinberg, 12/2005 // Description : Common Defines // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/fstrdefs.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** VST 3 SDK Version */ #ifndef kVstVersionString #define kVstVersionString "VST 3.8.0" ///< SDK version for PClassInfo2 #endif #define kVstVersionMajor 3 #define kVstVersionMinor 8 #define kVstVersionSub 0 #define VST_VERSION ((kVstVersionMajor << 16) | (kVstVersionMinor << 8) | kVstVersionSub) // Versions History which allows to write such code: // #if VST_VERSION >= VST_3_6_5_VERSION #define VST_3_8_0_VERSION 0x030800 #define VST_3_7_14_VERSION 0x03070E #define VST_3_7_13_VERSION 0x03070D #define VST_3_7_12_VERSION 0x03070C #define VST_3_7_11_VERSION 0x03070B #define VST_3_7_10_VERSION 0x03070A #define VST_3_7_9_VERSION 0x030709 #define VST_3_7_8_VERSION 0x030708 #define VST_3_7_7_VERSION 0x030707 #define VST_3_7_6_VERSION 0x030706 #define VST_3_7_5_VERSION 0x030705 #define VST_3_7_4_VERSION 0x030704 #define VST_3_7_3_VERSION 0x030703 #define VST_3_7_2_VERSION 0x030702 #define VST_3_7_1_VERSION 0x030701 #define VST_3_7_0_VERSION 0x030700 #define VST_3_6_14_VERSION 0x03060E #define VST_3_6_13_VERSION 0x03060D #define VST_3_6_12_VERSION 0x03060C #define VST_3_6_11_VERSION 0x03060B #define VST_3_6_10_VERSION 0x03060A #define VST_3_6_9_VERSION 0x030609 #define VST_3_6_8_VERSION 0x030608 #define VST_3_6_7_VERSION 0x030607 #define VST_3_6_6_VERSION 0x030606 #define VST_3_6_5_VERSION 0x030605 #define VST_3_6_0_VERSION 0x030600 #define VST_3_5_0_VERSION 0x030500 #define VST_3_1_0_VERSION 0x030100 #define VST_3_0_0_VERSION 0x030000 //------------------------------------------------------------------------ /** \defgroup vst3typedef VST 3 Data Types * Data Types defined by VST 3 */ /**@{*/ //------------------------------------------------------------------------ // String Types //------------------------------------------------------------------------ typedef char16 TChar; ///< UTF-16 character typedef TChar String128[128]; ///< 128 character UTF-16 string typedef const char8* CString; ///< C-String //------------------------------------------------------------------------ // General //------------------------------------------------------------------------ typedef int32 MediaType; ///< media type (audio/event) typedef int32 BusDirection; ///< bus direction (in/out) typedef int32 BusType; ///< bus type (main/aux) typedef int32 IoMode; ///< I/O mode (see \ref vst3IoMode) typedef int32 UnitID; ///< unit identifier typedef double ParamValue; ///< parameter value type: normalized value => [0.0, 1.0] typedef uint32 ParamID; ///< parameter identifier: value in range [0, 0x7FFFFFFF]. /// The range [0x80000000, 0xFFFFFFFF], is reserved for host application. typedef int32 ProgramListID; ///< program list identifier typedef int16 CtrlNumber; ///< MIDI controller number (see \ref ControllerNumbers for allowed values) typedef double TQuarterNotes; ///< time expressed in quarter notes typedef int64 TSamples; ///< time expressed in audio samples typedef uint32 ColorSpec; ///< color defining by 4 component ARGB value (Alpha/Red/Green/Blue) //------------------------------------------------------------------------ static const ParamID kNoParamId = 0xFFFFFFFF; ///< default for uninitialized parameter ID static const ParamID kMinParamId = 0; ///< value min for a parameter ID static const ParamID kMaxParamId = 0x7FFFFFFF; ///< value max for a parameter ID //------------------------------------------------------------------------ // Audio Types //------------------------------------------------------------------------ typedef float Sample32; ///< 32-bit precision audio sample typedef double Sample64; ///< 64-bit precision audio sample typedef double SampleRate; ///< sample rate //------------------------------------------------------------------------ // Speaker Arrangements Types //------------------------------------------------------------------------ typedef uint64 SpeakerArrangement; ///< Bitset of speakers typedef uint64 Speaker; ///< Bit for one speaker /**@}*/ static SMTG_CONSTEXPR const FIDString SDKVersionString = kVstVersionString; static SMTG_CONSTEXPR const uint32 SDKVersionMajor = kVstVersionMajor; static SMTG_CONSTEXPR const uint32 SDKVersionMinor = kVstVersionMinor; static SMTG_CONSTEXPR const uint32 SDKVersionSub = kVstVersionSub; static SMTG_CONSTEXPR const uint32 SDKVersion = ((SDKVersionMajor << 16) | (SDKVersionMinor << 8) | SDKVersionSub); // Versions History which allows to write such code: // if constexpr (SDKVersion >= SDKVersion_3_6_5) { ... } static SMTG_CONSTEXPR const uint32 SDKVersion_3_8_0 = VST_3_8_0_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_14 = VST_3_7_14_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_13 = VST_3_7_13_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_12 = VST_3_7_12_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_11 = VST_3_7_11_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_10 = VST_3_7_10_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_9 = VST_3_7_9_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_8 = VST_3_7_8_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_7 = VST_3_7_7_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_6 = VST_3_7_6_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_5 = VST_3_7_5_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_4 = VST_3_7_4_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_3 = VST_3_7_3_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_2 = VST_3_7_2_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_1 = VST_3_7_1_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_7_0 = VST_3_7_0_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_14 = VST_3_6_14_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_13 = VST_3_6_13_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_12 = VST_3_6_12_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_11 = VST_3_6_11_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_10 = VST_3_6_10_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_9 = VST_3_6_9_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_8 = VST_3_6_8_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_7 = VST_3_6_7_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_6 = VST_3_6_6_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_5 = VST_3_6_5_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_6_0 = VST_3_6_0_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_5_0 = VST_3_5_0_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_1_0 = VST_3_1_0_VERSION; static SMTG_CONSTEXPR const uint32 SDKVersion_3_0_0 = VST_3_0_0_VERSION; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/vstpshpack4.h0000644000000000000000000000013215124701711022453 xustar0030 mtime=1767080905.195211228 30 atime=1767080905.195211228 30 ctime=1767080905.195211228 qtractor-1.5.11/src/vst3/pluginterfaces/vst/vstpshpack4.h0000644000175000001440000000227215124701711022446 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/vstpshpack4.h // Created by : Steinberg, 05/2010 // Description : This file turns 4 Bytes packing of structures on. The file // pluginterfaces/base/falignpop.h is the complement to this file. // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once //---------------------------------------------------------------------------------------------- #if defined __BORLANDC__ #pragma -a4 #else #if (_MSC_VER >= 800 && !defined(_M_I86)) || defined(_PUSHPOP_SUPPORTED) #pragma warning(disable:4103) #endif #pragma pack(push) #pragma pack(4) #endif qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstphysicalui.h0000644000000000000000000000013215124701711023261 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstphysicalui.h0000644000175000001440000001417615124701711023262 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstphysicalui.h // Created by : Steinberg, 06/2018 // Description : VST Physical User Interface support // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstnoteexpression.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** \ingroup vst3typedef */ /**@{*/ /** Physical UI Type */ typedef uint32 PhysicalUITypeID; /**@}*/ //------------------------------------------------------------------------ /** PhysicalUITypeIDs describes the type of Physical UI (PUI) which could be associated to a note expression. \see PhysicalUIMap */ enum PhysicalUITypeIDs { /** absolute X position when touching keys of PUIs. Range [0=left, 0.5=middle, 1=right] */ kPUIXMovement = 0, /** absolute Y position when touching keys of PUIs. Range [0=bottom/near, 0.5=center, 1=top/far] */ kPUIYMovement, /** pressing a key down on keys of PUIs. Range [0=No Pressure, 1=Full Pressure] */ kPUIPressure, kPUITypeCount, ///< count of current defined PUIs kInvalidPUITypeID = 0xFFFFFFFF ///< indicates an invalid or not initialized PUI type }; //------------------------------------------------------------------------ /** PhysicalUIMap describes a mapping of a noteExpression Type to a Physical UI Type. It is used in PhysicalUIMapList. \see PhysicalUIMapList */ struct PhysicalUIMap { /** This represents the physical UI. /see PhysicalUITypeIDs, this is set by the caller of * getPhysicalUIMapping */ PhysicalUITypeID physicalUITypeID; /** This represents the associated noteExpression TypeID to the given physicalUITypeID. This * will be filled by the plug-in in the call getPhysicalUIMapping, set it to kInvalidTypeID if * no Note Expression is associated to the given PUI. */ NoteExpressionTypeID noteExpressionTypeID; }; //------------------------------------------------------------------------ /** PhysicalUIMapList describes a list of PhysicalUIMap \see INoteExpressionPhysicalUIMapping */ struct PhysicalUIMapList { /** Count of entries in the map array, set by the caller of getPhysicalUIMapping. */ uint32 count; /** Pointer to a list of PhysicalUIMap containing count entries. */ PhysicalUIMap* map; }; //------------------------------------------------------------------------ /** Extended plug-in interface IEditController for note expression event support: Vst::INoteExpressionPhysicalUIMapping \ingroup vstIPlug vst3611 - [plug imp] - [extends IEditController] - [released: 3.6.11] - [optional] With this plug-in interface, the host can retrieve the preferred physical mapping associated to note expression supported by the plug-in. When the mapping changes (for example when switching presets) the plug-in needs to inform the host about it via \ref IComponentHandler::restartComponent (kNoteExpressionChanged). \section INoteExpressionPhysicalUIMappingExample Example \code{.cpp} //------------------------------------------------------------------------ // here an example of how a VST3 plug-in could support this INoteExpressionPhysicalUIMapping interface. // we need to define somewhere the iids: //in MyController class declaration class MyController : public Vst::EditController, public Vst::INoteExpressionPhysicalUIMapping { // ... //--- INoteExpressionPhysicalUIMapping --------------------------------- tresult PLUGIN_API getPhysicalUIMapping (int32 busIndex, int16 channel, PhysicalUIMapList& list) SMTG_OVERRIDE; // ... OBJ_METHODS (MyController, Vst::EditController) DEFINE_INTERFACES // ... DEF_INTERFACE (Vst::INoteExpressionPhysicalUIMapping) END_DEFINE_INTERFACES (Vst::EditController) //... } // In mycontroller.cpp #include "pluginterfaces/vst/ivstnoteexpression.h" namespace Steinberg { namespace Vst { DEF_CLASS_IID (INoteExpressionPhysicalUIMapping) } } //------------------------------------------------------------------------ tresult PLUGIN_API MyController::getPhysicalUIMapping (int32 busIndex, int16 channel, PhysicalUIMapList& list) { if (busIndex == 0 && channel == 0) { for (uint32 i = 0; i < list.count; ++i) { NoteExpressionTypeID type = kInvalidTypeID; if (kPUIXMovement == list.map[i].physicalUITypeID) list.map[i].noteExpressionTypeID = kCustomStart + 1; else if (kPUIYMovement == list.map[i].physicalUITypeID) list.map[i].noteExpressionTypeID = kCustomStart + 2; } return kResultTrue; } return kResultFalse; } \endcode */ class INoteExpressionPhysicalUIMapping : public FUnknown { public: /** Fills the list of mapped [physical UI (in) - note expression (out)] for a given bus index * and channel. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getPhysicalUIMapping (int32 busIndex /*in*/, int16 channel /*in*/, PhysicalUIMapList& list /*inout*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (INoteExpressionPhysicalUIMapping, 0xB03078FF, 0x94D24AC8, 0x90CCD303, 0xD4133324) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstrepresentation.h0000644000000000000000000000013215124701711024151 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstrepresentation.h0000644000175000001440000003423715124701711024152 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstrepresentation.h // Created by : Steinberg, 08/2010 // Description : VST Representation Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { class IBStream; namespace Vst { //------------------------------------------------------------------------ /** RepresentationInfo is the structure describing a representation This structure is used in the function \see IXmlRepresentationController::getXmlRepresentationStream. \see IXmlRepresentationController */ struct RepresentationInfo { RepresentationInfo () { memset (vendor, 0, kNameSize); memset (name, 0, kNameSize); memset (version, 0, kNameSize); memset (host, 0, kNameSize); } RepresentationInfo (char8* _vendor, char8* _name = nullptr, char8* _version = nullptr, char8* _host = nullptr) { memset (vendor, 0, kNameSize); if (_vendor) strcpy (vendor, _vendor); memset (name, 0, kNameSize); if (_name) strcpy (name, _name); memset (version, 0, kNameSize); if (_version) strcpy (version, _version); memset (host, 0, kNameSize); if (_host) strcpy (host, _host); } enum { kNameSize = 64 }; char8 vendor[kNameSize]; ///< Vendor name of the associated representation (remote) (eg. "Yamaha"). char8 name[kNameSize]; ///< Representation (remote) Name (eg. "O2"). char8 version[kNameSize]; ///< Version of this "Remote" (eg. "1.0"). char8 host[kNameSize]; ///< Optional: used if the representation is for a given host only (eg. "Nuendo"). }; //------------------------------------------------------------------------ //------------------------------------------------------------------------ /** Extended plug-in interface IEditController for a component: Vst::IXmlRepresentationController \ingroup vstIPlug vst350 - [plug imp] - [extends IEditController] - [released: 3.5.0] - [optional] A representation based on XML is a way to export, structure, and group plug-ins parameters for a specific remote (hardware or software rack (such as quick controls)). \n It allows to describe each parameter more precisely (what is the best matching to a knob, different title lengths matching limited remote display,...).\n See an \ref Example. \n\n - A representation is composed of pages (this means that to see all exported parameters, the user has to navigate through the pages). - A page is composed of cells (for example 8 cells per page). - A cell is composed of layers (for example a cell could have a knob, a display, and a button, which means 3 layers). - A layer is associated to a plug-in parameter using the ParameterID as identifier: - it could be a knob with a display for title and/or value, this display uses the same parameterId, but it could an another one. - switch - link which allows to jump directly to a subpage (another page) - more... See Vst::LayerType . \n This representation is implemented as XML text following the Document Type Definition (DTD): http://dtd.steinberg.net/VST-Remote-1.1.dtd \section Example Here an example of what should be passed in the stream of getXmlRepresentationStream: \code My name 2010-12-31 This is an example for 4 Cells per Page for the Remote named ProductRemote from company HardwareCompany. Mix dry/wet Mix Delay Dly Spatial Spat Width + Widt Sync Note + Note Rate \endcode */ class IXmlRepresentationController : public FUnknown { public: /** Retrieves a stream containing a XmlRepresentation for a wanted representation info. * \note [UI-thread & Initialized] */ virtual tresult PLUGIN_API getXmlRepresentationStream (RepresentationInfo& info /*in*/, IBStream* stream /*inout*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IXmlRepresentationController, 0xA81A0471, 0x48C34DC4, 0xAC30C9E1, 0x3C8393D5) //------------------------------------------------------------------------ /** Defines for XML representation Tags and Attributes */ #define ROOTXML_TAG "vstXML" #define COMMENT_TAG "comment" #define CELL_TAG "cell" #define CELLGROUP_TAG "cellGroup" #define CELLGROUPTEMPLATE_TAG "cellGroupTemplate" #define CURVE_TAG "curve" #define CURVETEMPLATE_TAG "curveTemplate" #define DATE_TAG "date" #define LAYER_TAG "layer" #define NAME_TAG "name" #define ORIGINATOR_TAG "originator" #define PAGE_TAG "page" #define PAGETEMPLATE_TAG "pageTemplate" #define PLUGIN_TAG "plugin" #define VALUE_TAG "value" #define VALUEDISPLAY_TAG "valueDisplay" #define VALUELIST_TAG "valueList" #define REPRESENTATION_TAG "representation" #define SEGMENT_TAG "segment" #define SEGMENTLIST_TAG "segmentList" #define TITLEDISPLAY_TAG "titleDisplay" #define ATTR_CATEGORY "category" #define ATTR_CLASSID "classID" #define ATTR_ENDPOINT "endPoint" #define ATTR_INDEX "index" #define ATTR_FLAGS "flags" #define ATTR_FUNCTION "function" #define ATTR_HOST "host" #define ATTR_LEDSTYLE "ledStyle" #define ATTR_LENGTH "length" #define ATTR_LINKEDTO "linkedTo" #define ATTR_NAME "name" #define ATTR_ORDER "order" #define ATTR_PAGE "page" #define ATTR_PARAMID "parameterID" #define ATTR_STARTPOINT "startPoint" #define ATTR_STYLE "style" #define ATTR_SWITCHSTYLE "switchStyle" #define ATTR_TEMPLATE "template" #define ATTR_TURNSPERFULLRANGE "turnsPerFullRange" #define ATTR_TYPE "type" #define ATTR_UNITID "unitID" #define ATTR_VARIABLES "variables" #define ATTR_VENDOR "vendor" #define ATTR_VERSION "version" //------------------------------------------------------------------------ /** Defines some predefined Representation Remote Names */ #define GENERIC "Generic" #define GENERIC_4_CELLS "Generic 4 Cells" #define GENERIC_8_CELLS "Generic 8 Cells" #define GENERIC_12_CELLS "Generic 12 Cells" #define GENERIC_24_CELLS "Generic 24 Cells" #define GENERIC_N_CELLS "Generic %d Cells" #define QUICK_CONTROL_8_CELLS "Quick Controls 8 Cells" //------------------------------------------------------------------------ /** Layer Types used in a VST XML Representation */ namespace LayerType { enum { kKnob = 0, ///< a knob (encoder or not) kPressedKnob, ///< a knob which is used by pressing and turning kSwitchKnob, ///< knob could be pressed to simulate a switch kSwitch, ///< a "on/off" button kLED, ///< LED like VU-meter or display around a knob kLink, ///< indicates that this layer is a folder linked to an another INode (page) kDisplay, ///< only for text display (not really a control) kFader, ///< a fader kEndOfLayerType }; /** FIDString variant of the LayerType */ static const FIDString layerTypeFIDString[] = { "knob" ,"pressedKnob" ,"switchKnob" ,"switch" ,"LED" ,"link" ,"display" ,"fader" ,nullptr }; }; //------------------------------------------------------------------------ /** Curve Types used in a VST XML Representation */ namespace CurveType { const CString kSegment = "segment"; ///< const CString kValueList = "valueList"; ///< }; //------------------------------------------------------------------------ /** Attributes used to defined a Layer in a VST XML Representation */ namespace Attributes { const CString kStyle = ATTR_STYLE; ///< string attribute : See AttributesStyle for available string value const CString kLEDStyle = ATTR_LEDSTYLE; ///< string attribute : See AttributesStyle for available string value const CString kSwitchStyle = ATTR_SWITCHSTYLE; ///< string attribute : See AttributesStyle for available string value const CString kKnobTurnsPerFullRange = ATTR_TURNSPERFULLRANGE; ///< float attribute const CString kFunction = ATTR_FUNCTION; ///< string attribute : See AttributesFunction for available string value const CString kFlags = ATTR_FLAGS; ///< string attribute : See AttributesFlags for available string value }; //------------------------------------------------------------------------ /** Attributes Function used to defined the function of a Layer in a VST XML Representation */ namespace AttributesFunction { /// Global Style const CString kPanPosCenterXFunc = "PanPosCenterX"; ///< Gravity point X-axis (L-R) (for stereo: middle between left and right) const CString kPanPosCenterYFunc = "PanPosCenterY"; ///< Gravity point Y-axis (Front-Rear) const CString kPanPosFrontLeftXFunc = "PanPosFrontLeftX"; ///< Left channel Position in X-axis const CString kPanPosFrontLeftYFunc = "PanPosFrontLeftY"; ///< Left channel Position in Y-axis const CString kPanPosFrontRightXFunc = "PanPosFrontRightX"; ///< Right channel Position in X-axis const CString kPanPosFrontRightYFunc = "PanPosFrontRightY"; ///< Right channel Position in Y-axis const CString kPanRotationFunc = "PanRotation"; ///< Rotation around the Center (gravity point) const CString kPanLawFunc = "PanLaw"; ///< Panning Law const CString kPanMirrorModeFunc = "PanMirrorMode"; ///< Panning Mirror Mode const CString kPanLfeGainFunc = "PanLfeGain"; ///< Panning LFE Gain const CString kGainReductionFunc = "GainReduction"; ///< Gain Reduction for compressor const CString kSoloFunc = "Solo"; ///< Solo const CString kMuteFunc = "Mute"; ///< Mute const CString kVolumeFunc = "Volume"; ///< Volume }; //------------------------------------------------------------------------ /** Attributes Style associated a specific Layer Type in a VST XML Representation */ namespace AttributesStyle { /// Global Style const CString kInverseStyle = "inverse"; ///< the associated layer should use the inverse value of parameter (1 - x). /// LED Style const CString kLEDWrapLeftStyle = "wrapLeft"; ///< |======>----- (the default one if not specified) const CString kLEDWrapRightStyle = "wrapRight"; ///< -------<====| const CString kLEDSpreadStyle = "spread"; ///< ---<==|==>--- const CString kLEDBoostCutStyle = "boostCut"; ///< ------|===>-- const CString kLEDSingleDotStyle = "singleDot"; ///< --------|---- /// Switch Style const CString kSwitchPushStyle = "push"; ///< Apply only when pressed, unpressed will reset the value to min. const CString kSwitchPushIncLoopedStyle = "pushIncLooped"; ///< Push will increment the value. When the max is reached it will restart with min. ///< The default one if not specified (with 2 states values it is a OnOff switch). const CString kSwitchPushDecLoopedStyle = "pushDecLooped"; ///< Push will decrement the value. When the min is reached it will restart with max. const CString kSwitchPushIncStyle = "pushInc"; ///< Increment after each press (delta depends of the curve). const CString kSwitchPushDecStyle = "pushDec"; ///< Decrement after each press (delta depends of the curve). const CString kSwitchLatchStyle = "latch"; ///< Each push-release will change the value between min and max. ///< A timeout between push and release could be used to simulate a push style (if timeout is reached). }; //------------------------------------------------------------------------ /** Attributes Flags defining a Layer in a VST XML Representation */ namespace AttributesFlags { const CString kHideableFlag = "hideable"; ///< the associated layer marked as hideable allows a remote to hide or make it not usable a parameter when the associated value is inactive }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstmessage.h0000644000000000000000000000013215124701711022533 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstmessage.h0000644000175000001440000000730715124701711022532 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstmessage.h // Created by : Steinberg, 04/2005 // Description : VST Message Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstattributes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Private plug-in message: Vst::IMessage \ingroup vstIHost vst300 - [host imp] - [create via IHostApplication::createInstance] - [released: 3.0.0] - [mandatory] Messages are sent from a VST controller component to a VST editor component and vice versa. \see IAttributeList, IConnectionPoint, \ref vst3Communication */ class IMessage : public FUnknown { public: //------------------------------------------------------------------------ /** Returns the message ID (for example "TextMessage"). */ virtual FIDString PLUGIN_API getMessageID () = 0; /** Sets a message ID (for example "TextMessage"). */ virtual void PLUGIN_API setMessageID (FIDString id /*in*/) = 0; /** Returns the attribute list associated to the message. */ virtual IAttributeList* PLUGIN_API getAttributes () = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IMessage, 0x936F033B, 0xC6C047DB, 0xBB0882F8, 0x13C1E613) //------------------------------------------------------------------------ /** Connect a component with another one: Vst::IConnectionPoint \ingroup vstIPlug vst300 - [plug imp] - [host imp] - [released: 3.0.0] - [mandatory] This interface is used for the communication of separate components. Note that some hosts will place a proxy object between the components so that they are not directly connected. \see \ref vst3Communication */ class IConnectionPoint : public FUnknown { public: //------------------------------------------------------------------------ /** Connects this instance with another connection point. * \note [UI-thread & Initialized] */ virtual tresult PLUGIN_API connect (IConnectionPoint* other /*in*/) = 0; /** Disconnects a given connection point from this. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API disconnect (IConnectionPoint* other /*in*/) = 0; /** Called when a message has been sent from the connection point to this. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API notify (IMessage* message /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IConnectionPoint, 0x70A4156F, 0x6E6E4026, 0x989148BF, 0xAA60D8D1) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstparameterfunctionname.h0000644000000000000000000000013215124701711025476 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstparameterfunctionname.h0000644000175000001440000001463115124701711025473 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstparameterfunctionname.h // Created by : Steinberg, 03/2020 // Description : VST Parameter Function Name Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { namespace FunctionNameType { //-------------------------------------------------------------------- const CString kCompGainReduction = "Comp:GainReduction"; /** */ const CString kCompGainReductionMax = "Comp:GainReductionMax"; const CString kCompGainReductionPeakHold = "Comp:GainReductionPeakHold"; const CString kCompResetGainReductionMax = "Comp:ResetGainReductionMax"; const CString kLowLatencyMode = "LowLatencyMode"; /** Useful for live situation where low latency is required: 0 means LowLatency disable, 1 means LowLatency enable */ const CString kDryWetMix = "DryWetMix"; /** Allowing to mix the original (Dry) Signal with the processed one (Wet): 0.0 means Dry Signal only, 0.5 means 50% Dry Signal + 50% Wet Signal, 1.0 means Wet Signal only */ const CString kRandomize = "Randomize"; /** Allow to assign some randomized values to some parameters in a controlled way*/ /// Panner Type const CString kPanPosCenterX = "PanPosCenterX"; ///< Gravity point X-axis [0, 1]=>[L-R] (for stereo: middle between left and right) const CString kPanPosCenterY = "PanPosCenterY"; ///< Gravity point Y-axis [0, 1]=>[Front-Rear] const CString kPanPosCenterZ = "PanPosCenterZ"; ///< Gravity point Z-axis [0, 1]=>[Bottom-Top] } // FunctionNameType //------------------------------------------------------------------------ /** Edit controller component interface extension: Vst::IParameterFunctionName \ingroup vstIPlug vst370 - [plug imp] - [extends IEditController] - [released: 3.7.0] - [optional] This interface allows the host to get a parameter associated to a specific meaning (a functionName) for a given unit. The host can use this information, for example, for drawing a Gain Reduction meter in its own UI. In order to get the plain value of this parameter, the host should use the IEditController::normalizedParamToPlain. The host can automatically map parameters to dedicated UI controls, such as the wet-dry mix knob or Randomize button. \section IParameterFunctionNameExample Example \code{.cpp} //------------------------------------------------------------------------ // here an example of how a VST3 plug-in could support this IParameterFunctionName interface. // we need to define somewhere the iids: // in MyController class declaration class MyController : public Vst::EditController, public Vst::IParameterFunctionName { // ... tresult PLUGIN_API getParameterIDFromFunctionName (UnitID unitID, FIDString functionName, Vst::ParamID& paramID) override; // ... OBJ_METHODS (MyController, Vst::EditController) DEFINE_INTERFACES // ... DEF_INTERFACE (Vst::IParameterFunctionName) END_DEFINE_INTERFACES (Vst::EditController) DELEGATE_REFCOUNT (Vst::EditController) // ... } #include "ivstparameterfunctionname.h" namespace Steinberg { namespace Vst { DEF_CLASS_IID (IParameterFunctionName) } } //------------------------------------------------------------------------ tresult PLUGIN_API MyController::getParameterIDFromFunctionName (UnitID unitID, FIDString functionName, Vst::ParamID& paramID) { using namespace Vst; paramID = kNoParamId; if (unitID == kRootUnitId && FIDStringsEqual (functionName, kCompGainReduction)) paramID = kMyGainReductionId; return (paramID != kNoParamId) ? kResultOk : kResultFalse; } //--- a host implementation example: -------------------- // ... FUnknownPtr functionName (mEditController->getIEditController ()); if (functionName) { Vst::ParamID paramID; if (functionName->getParameterIDFromFunctionName (kRootUnitId, Vst::FunctionNameType::kCompGainReduction, paramID) == kResultTrue) { // paramID could be cached for performance issue ParamValue norm = mEditController->getIEditController ()->getParamNormalized (paramID); ParamValue plain = mEditController->getIEditController ()->normalizedParamToPlain (paramID, norm); // plain is something like -6 (-6dB) } } \endcode */ class IParameterFunctionName : public FUnknown { public: //------------------------------------------------------------------------ /** Gets for the given unitID the associated paramID to a function Name. * Returns kResultFalse when no found parameter (paramID is set to kNoParamId in this case). * \note [UI-thread & (Initialized | Connected)] */ virtual tresult PLUGIN_API getParameterIDFromFunctionName (UnitID unitID /*in*/, FIDString functionName /*in*/, ParamID& paramID /*inout*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IParameterFunctionName, 0x6D21E1DC, 0x91199D4B, 0xA2A02FEF, 0x6C1AE55C) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstchannelcontextinfo.h0000644000000000000000000000013215124701711025000 xustar0030 mtime=1767080905.194000456 30 atime=1767080905.194000456 30 ctime=1767080905.194000456 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstchannelcontextinfo.h0000644000175000001440000002212515124701711024772 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstchannelcontextinfo.h // Created by : Steinberg, 02/2014 // Description : VST Channel Context Info Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/vsttypes.h" #include "pluginterfaces/vst/ivstattributes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { /** For Channel Context Info Interface */ namespace ChannelContext { //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ /** Channel context interface: Vst::IInfoListener \ingroup vstIHost vst365 - [plug imp] - [extends IEditController] - [released: 3.6.5] - [optional] Allows the host to inform the plug-in about the context in which the plug-in is instantiated, mainly channel based info (color, name, index,...). Index can be defined inside a namespace (for example, index start from 1 to N for Type Input/Output Channel (Index namespace) and index start from 1 to M for Type Audio Channel).\n As soon as the plug-in provides this IInfoListener interface, the host will call setChannelContextInfos for each change occurring to this channel (new name, new color, new indexation,...) \section IChannelContextExample Example \code{.cpp} //------------------------------------------------------------------------ tresult PLUGIN_API MyPlugin::setChannelContextInfos (IAttributeList* list) { if (list) { // optional we can ask for the Channel Name Length int64 length; if (list->getInt (ChannelContext::kChannelNameLengthKey, length) == kResultTrue) { ... } // get the Channel Name where we, as plug-in, are instantiated String128 name; if (list->getString (ChannelContext::kChannelNameKey, name, sizeof (name)) == kResultTrue) { ... } // get the Channel UID if (list->getString (ChannelContext::kChannelUIDKey, name, sizeof (name)) == kResultTrue) { ... } // get Channel RuntimeID int64 runtimeId; if (list->getInt (ChannelContext::kChannelRuntimeIDKey, runtimeId) == kResultTrue) { ... } // get Channel Index int64 index; if (list->getInt (ChannelContext::kChannelIndexKey, index) == kResultTrue) { ... } // get the Channel Color int64 color; if (list->getInt (ChannelContext::kChannelColorKey, color) == kResultTrue) { uint32 channelColor = (uint32)color; String str; str.printf ("%x%x%x%x", ChannelContext::GetAlpha (channelColor), ChannelContext::GetRed (channelColor), ChannelContext::GetGreen (channelColor), ChannelContext::GetBlue (channelColor)); String128 string128; Steinberg::UString (string128, 128).fromAscii (str); ... } // get Channel Index Namespace Order of the current used index namespace if (list->getInt (ChannelContext::kChannelIndexNamespaceOrderKey, index) == kResultTrue) { ... } // get the channel Index Namespace Length if (list->getInt (ChannelContext::kChannelIndexNamespaceLengthKey, length) == kResultTrue) { ... } // get the channel Index Namespace String128 namespaceName; if (list->getString (ChannelContext::kChannelIndexNamespaceKey, namespaceName, sizeof (namespaceName)) == kResultTrue) { ... } // get plug-in Channel Location int64 location; if (list->getInt (ChannelContext::kChannelPluginLocationKey, location) == kResultTrue) { String128 string128; switch (location) { case ChannelContext::kPreVolumeFader: Steinberg::UString (string128, 128).fromAscii ("PreVolFader"); break; case ChannelContext::kPostVolumeFader: Steinberg::UString (string128, 128).fromAscii ("PostVolFader"); break; case ChannelContext::kUsedAsPanner: Steinberg::UString (string128, 128).fromAscii ("UsedAsPanner"); break; default: Steinberg::UString (string128, 128).fromAscii ("unknown!"); break; } } // do not forget to call addRef () if you want to keep this list } } \endcode */ class IInfoListener : public FUnknown { public: /** Receive the channel context infos from host. * \note [UI-thread & (Initialized | Connected | Setup Done | Activated | Processing)] */ virtual tresult PLUGIN_API setChannelContextInfos (IAttributeList* list /*in*/) = 0; static const FUID iid; }; DECLARE_CLASS_IID (IInfoListener, 0x0F194781, 0x8D984ADA, 0xBBA0C1EF, 0xC011D8D0) //------------------------------------------------------------------------ /** Values used for kChannelPluginLocationKey */ enum ChannelPluginLocation { kPreVolumeFader = 0, kPostVolumeFader, kUsedAsPanner }; //------------------------------------------------------------------------ //------------------------------------------------------------------------ // Colors //------------------------------------------------------------------------ /** \ingroup vst3typedef */ /**@{*/ //------------------------------------------------------------------------ /** ARGB (Alpha-Red-Green-Blue) */ typedef uint32 ColorSpec; typedef uint8 ColorComponent; /**@}*/ /** Returns the Blue part of the given ColorSpec */ inline ColorComponent GetBlue (ColorSpec cs) { return (ColorComponent) (cs & 0x000000FF); } /** Returns the Green part of the given ColorSpec */ inline ColorComponent GetGreen (ColorSpec cs) { return (ColorComponent) ((cs >> 8) & 0x000000FF); } /** Returns the Red part of the given ColorSpec */ inline ColorComponent GetRed (ColorSpec cs) { return (ColorComponent) ((cs >> 16) & 0x000000FF); } /** Returns the Alpha part of the given ColorSpec */ inline ColorComponent GetAlpha (ColorSpec cs) { return (ColorComponent) ((cs >> 24) & 0x000000FF); } //------------------------------------------------------------------------ /** Keys used as AttrID (Attribute ID) in the return IAttributeList of * IInfoListener::setChannelContextInfos */ //------------------------------------------------------------------------ /** string (TChar) [optional]: unique id string used to identify a channel */ const CString kChannelUIDKey = "channel uid"; /** integer (int64) [optional]: number of characters in kChannelUIDKey */ const CString kChannelUIDLengthKey = "channel uid length"; /** integer (int64) [optional]: runtime id to identify a channel (may change when reloading project) */ const CString kChannelRuntimeIDKey = "channel runtime id"; /** string (TChar) [optional]: name of the channel like displayed in the mixer */ const CString kChannelNameKey = "channel name"; /** integer (int64) [optional]: number of characters in kChannelNameKey */ const CString kChannelNameLengthKey = "channel name length"; /** color (ColorSpec) [optional]: used color for the channel in mixer or track */ const CString kChannelColorKey = "channel color"; /** integer (int64) [optional]: index of the channel in a channel index namespace, * start with 1 not 0! */ const CString kChannelIndexKey = "channel index"; /** integer (int64) [optional]: define the order of the current used index namespace, * start with 1 not 0! * For example: * index namespace is "Input" -> order 1, * index namespace is "Channel" -> order 2, * index namespace is "Output" -> order 3 */ const CString kChannelIndexNamespaceOrderKey = "channel index namespace order"; /** string (TChar) [optional]: name of the channel index namespace * for example "Input", "Output","Channel", ... */ const CString kChannelIndexNamespaceKey = "channel index namespace"; /** integer (int64) [optional]: number of characters in kChannelIndexNamespaceKey */ const CString kChannelIndexNamespaceLengthKey = "channel index namespace length"; /** PNG image representation as binary [optional] */ const CString kChannelImageKey = "channel image"; /** integer (int64) [optional]: routing position of the plug-in in the channel * (see ChannelPluginLocation) */ const CString kChannelPluginLocationKey = "channel plugin location"; //------------------------------------------------------------------------ } // namespace ChannelContext } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivsteditcontroller.h0000644000000000000000000000013215124701711024140 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194000456 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivsteditcontroller.h0000644000175000001440000007457215124701711024147 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivsteditcontroller.h // Created by : Steinberg, 09/2005 // Description : VST Edit Controller Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ipluginbase.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ /** Class Category Name for Controller Component */ //------------------------------------------------------------------------ #ifndef kVstComponentControllerClass #define kVstComponentControllerClass "Component Controller Class" #endif //------------------------------------------------------------------------ namespace Steinberg { class IPlugView; class IBStream; //------------------------------------------------------------------------ namespace Vst { //------------------------------------------------------------------------ /** Controller Parameter Info. * A parameter info describes a parameter of the controller. * The id must always be the same for a parameter as this uniquely identifies the parameter. */ struct ParameterInfo { //------------------------------------------------------------------------ ParamID id; ///< unique identifier of this parameter (named tag too) String128 title; ///< parameter title (e.g. "Volume") String128 shortTitle; ///< parameter shortTitle (e.g. "Vol") String128 units; ///< parameter unit (e.g. "dB") int32 stepCount; ///< number of discrete steps (0: continuous, 1: toggle, discrete value /// otherwise (corresponding to max - min, for example: /// 127 for min = 0 and max = 127) - see \ref vst3ParameterIntro) ParamValue defaultNormalizedValue; ///< default normalized value [0,1] /// in case of discrete value: /// defaultNormalizedValue = defDiscreteValue/stepCount UnitID unitId; ///< id of unit this parameter belongs to (see \ref vst3Units) int32 flags; ///< ParameterFlags (see below) /** Parameter flags * * Note that all non defined bits are reserved for future use! */ enum ParameterFlags : int32 { /** No flags wanted. * [SDK 3.0.0] */ kNoFlags = 0, /** Parameter can be automated. * [SDK 3.0.0] */ kCanAutomate = 1 << 0, /** Parameter cannot be changed from outside the plug-in * (implies that kCanAutomate is NOT set). * [SDK 3.0.0] */ kIsReadOnly = 1 << 1, /** Attempts to set the parameter value out of the limits will result in a wrap around. * [SDK 3.0.2] */ kIsWrapAround = 1 << 2, /** Parameter should be displayed as list in generic editor or automation editing. * [SDK 3.1.0] */ kIsList = 1 << 3, /** Parameter should be NOT displayed and cannot be changed from outside the plug-in. * It implies that kCanAutomate is NOT set and kIsReadOnly is set. * [SDK 3.7.0] */ kIsHidden = 1 << 4, /** Parameter is a program change (unitId gives info about associated unit - see \ref * vst3ProgramLists). * [SDK 3.0.0] */ kIsProgramChange = 1 << 15, /** Special bypass parameter (only one allowed): plug-in can handle bypass. * Highly recommended to export a bypass parameter for effect plug-in. * [SDK 3.0.0] */ kIsBypass = 1 << 16 }; //------------------------------------------------------------------------ }; //------------------------------------------------------------------------ /** View Types used for IEditController::createView */ //------------------------------------------------------------------------ namespace ViewType { const CString kEditor = "editor"; } //------------------------------------------------------------------------ /** Flags used for IComponentHandler::restartComponent */ enum RestartFlags : int32 { /** The Component should be reloaded * The host has to unload completely the plug-in (controller/processor) and reload it. * [SDK 3.0.0] */ kReloadComponent = 1 << 0, /** Input/Output Bus configuration has changed * The plug-in informs the host that either the bus configuration or the bus count has changed. * The host has to deactivate the plug-in, asks the plug-in for its wanted new bus * configurations, adapts its processing graph and reactivate the plug-in. * [SDK 3.0.0] */ kIoChanged = 1 << 1, /** Multiple parameter values have changed (as result of a program change for example) * The host invalidates all caches of parameter values and asks the edit controller for the * current values. * [SDK 3.0.0] */ kParamValuesChanged = 1 << 2, /** Latency has changed * The plug informs the host that its latency has changed, getLatencySamples should return the new latency after setActive (true) was called * The host has to deactivate and reactivate the plug-in, then afterwards the host could ask for the current latency (getLatencySamples) * See IAudioProcessor::getLatencySamples * [SDK 3.0.0] */ kLatencyChanged = 1 << 3, /** Parameter titles (title, shortTitle and units), default values, stepCount or flags (ParameterFlags) have changed * The host invalidates all caches of parameter infos and asks the edit controller for the * current infos. * [SDK 3.0.0] */ kParamTitlesChanged = 1 << 4, /** MIDI Controllers and/or Program Changes Assignments have changed * The plug-in informs the host that its MIDI-CC mapping has changed (for example after a MIDI learn or new loaded preset) * or if the stepCount or UnitID of a ProgramChange parameter has changed. * The host has to rebuild the MIDI-CC => parameter mapping (getMidiControllerAssignment) * and reread program changes parameters (stepCount and associated unitID) * [SDK 3.0.1] */ kMidiCCAssignmentChanged = 1 << 5, /** Note Expression has changed (info, count, PhysicalUIMapping, ...) * Either the note expression type info, the count of note expressions or the physical UI mapping has changed. * The host invalidates all caches of note expression infos and asks the edit controller for the current ones. * See INoteExpressionController, NoteExpressionTypeInfo and INoteExpressionPhysicalUIMapping * [SDK 3.5.0] */ kNoteExpressionChanged = 1 << 6, /** Input / Output bus titles have changed * The host invalidates all caches of bus titles and asks the edit controller for the current titles. * [SDK 3.5.0] */ kIoTitlesChanged = 1 << 7, /** Prefetch support has changed * The plug-in informs the host that its PrefetchSupport has changed * The host has to deactivate the plug-in, calls IPrefetchableSupport::getPrefetchableSupport * and reactivate the plug-in. * See IPrefetchableSupport * [SDK 3.6.1] */ kPrefetchableSupportChanged = 1 << 8, /** RoutingInfo has changed * The plug-in informs the host that its internal routing (relation of an event-input-channel to * an audio-output-bus) has changed. The host asks the plug-in for the new routing with * IComponent::getRoutingInfo, \ref vst3Routing * See IComponent * [SDK 3.6.6] */ kRoutingInfoChanged = 1 << 9, /** Key switches has changed (info, count) * Either the Key switches info, the count of Key switches has changed. * The host invalidates all caches of Key switches infos and asks the edit controller * (IKeyswitchController) for the current ones. * See IKeyswitchController * [SDK 3.7.3] */ kKeyswitchChanged = 1 << 10, /** Mapping of ParamID has changed * The Plug-in informs the host that its parameters ID has changed. This has to be called by the * edit controller in the method setComponentState or setState (during projects loading) when the * plug-in detects that the given state was associated to an older version of the plug-in, or to a * plug-in to replace (for ex. migrating VST2 => VST3), with a different set of parameter IDs, then * the host could remap any used parameters like automation by asking the IRemapParamID interface * (which extends IEditController). * See IRemapParamID * [SDK 3.7.11] */ kParamIDMappingChanged = 1 << 11 }; //------------------------------------------------------------------------ /** Host callback interface for an edit controller: Vst::IComponentHandler \ingroup vstIHost vst300 - [host imp] - [released: 3.0.0] - [mandatory] Allow transfer of parameter editing to component (processor) via host and support automation. Cause the host to react on configuration changes (restartComponent). \see \ref IEditController */ class IComponentHandler : public FUnknown { public: //------------------------------------------------------------------------ /** To be called before calling a performEdit (e.g. on mouse-click-down event). * This must be called in the UI-Thread context! * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API beginEdit (ParamID id /*in*/) = 0; /** Called between beginEdit and endEdit to inform the handler that a given parameter has a new * value. This must be called in the UI-Thread context! * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API performEdit (ParamID id /*in*/, ParamValue valueNormalized /*in*/) = 0; /** To be called after calling a performEdit (e.g. on mouse-click-up event). * This must be called in the UI-Thread context! * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API endEdit (ParamID id /*in*/) = 0; /** Instructs host to restart the component. This must be called in the UI-Thread context! * @param[in] flags is a combination of RestartFlags * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API restartComponent (int32 flags /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IComponentHandler, 0x93A0BEA3, 0x0BD045DB, 0x8E890B0C, 0xC1E46AC6) //------------------------------------------------------------------------ /** Extended host callback interface for an edit controller: Vst::IComponentHandler2 \ingroup vstIHost vst310 - [host imp] - [extends IComponentHandler] - [released: 3.1.0] - [optional] One part handles: - Setting dirty state of the plug-in - Requesting the host to open the editor The other part handles parameter group editing from the plug-in UI. It wraps a set of \ref IComponentHandler::beginEdit / \ref Steinberg::Vst::IComponentHandler::performEdit / \ref Steinberg::Vst::IComponentHandler::endEdit functions (see \ref IComponentHandler) which should use the same timestamp in the host when writing automation. This allows for better synchronizing of multiple parameter changes at once. \section IComponentHandler2Example Examples of different use cases \code{.cpp} //-------------------------------------- // we are in the editcontroller... // in case of multiple switch buttons (with associated ParamID 1 and 3) // on mouse down : hostHandler2->startGroupEdit (); hostHandler->beginEdit (1); hostHandler->beginEdit (3); hostHandler->performEdit (1, 1.0); hostHandler->performEdit (3, 0.0); // the opposite of paramID 1 for example .... // on mouse up : hostHandler->endEdit (1); hostHandler->endEdit (3); hostHandler2->finishGroupEdit (); .... .... //-------------------------------------- // in case of multiple faders (with associated ParamID 1 and 3) // on mouse down : hostHandler2->startGroupEdit (); hostHandler->beginEdit (1); hostHandler->beginEdit (3); hostHandler2->finishGroupEdit (); .... // on mouse move : hostHandler2->startGroupEdit (); hostHandler->performEdit (1, x); // x the wanted value hostHandler->performEdit (3, x); hostHandler2->finishGroupEdit (); .... // on mouse up : hostHandler2->startGroupEdit (); hostHandler->endEdit (1); hostHandler->endEdit (3); hostHandler2->finishGroupEdit (); \endcode \see \ref IComponentHandler, \ref IEditController */ class IComponentHandler2 : public FUnknown { public: //------------------------------------------------------------------------ /** Tells host that the plug-in is dirty (something besides parameters has changed since last * save), if true the host should apply a save before quitting. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API setDirty (TBool state /*in*/) = 0; /** Tells host that it should open the plug-in editor the next time it's possible. You should * use this instead of showing an alert and blocking the program flow (especially on loading * projects). * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API requestOpenEditor (FIDString name = ViewType::kEditor /*in*/) = 0; //------------------------------------------------------------------------ /** Starts the group editing (call before a \ref IComponentHandler::beginEdit), * the host will keep the current timestamp at this call and will use it for all * \ref IComponentHandler::beginEdit, \ref IComponentHandler::performEdit, * \ref IComponentHandler::endEdit calls until a \ref finishGroupEdit (). * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API startGroupEdit () = 0; /** Finishes the group editing started by a \ref startGroupEdit (call after a \ref * IComponentHandler::endEdit). * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API finishGroupEdit () = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IComponentHandler2, 0xF040B4B3, 0xA36045EC, 0xABCDC045, 0xB4D5A2CC) //------------------------------------------------------------------------ /** Extended host callback interface for an edit controller: Vst::IComponentHandlerBusActivation \ingroup vstIHost vst368 - [host imp] - [extends IComponentHandler] - [released: 3.6.8] - [optional] Allows the plug-in to request the host to activate or deactivate a specific bus. If the host accepts this request, it will call later on \ref IComponent::activateBus. This is particularly useful for instruments with more than 1 outputs, where the user could request from the plug-in UI a given output bus activation. \code{.cpp} // somewhere in your code when you need to inform the host to enable a specific Bus. FUnknownPtr busActivation (componentHandler); if (busActivation) { // here we want to activate our audio input sidechain (the 2cd input bus: index 1) busActivation->requestBusActivation (kAudio, kInput, 1, true); } \endcode \see \ref IComponentHandler */ class IComponentHandlerBusActivation : public FUnknown { public: //------------------------------------------------------------------------ /** request the host to activate or deactivate a specific bus. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API requestBusActivation (MediaType type /*in*/, BusDirection dir /*in*/, int32 index /*in*/, TBool state /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IComponentHandlerBusActivation, 0x067D02C1, 0x5B4E274D, 0xA92D90FD, 0x6EAF7240) //------------------------------------------------------------------------ /** Extended host callback interface for an edit controller: Vst::IProgress \ingroup vstIHost vst370 - [host imp] - [extends IComponentHandler] - [released: 3.7.0] - [optional] Allows the plug-in to request the host to create a progress for some specific tasks which take some time. The host can visualize the progress as read-only UI elements. For example, after loading a project where a plug-in needs to load extra data (e.g. samples) in a background thread, this enables the host to get and visualize the current status of the loading progress and to inform the user when the loading is finished. Note: During the progress, the host can unload the plug-in at any time. Make sure that the plug-in supports this use case. \section IProgressExample Example \code{.cpp} //-------------------------------------- // we are in the editcontroller: // as member: IProgress::ID mProgressID; FUnknownPtr progress (componentHandler); if (progress) progress->start (IProgress::ProgressType::UIBackgroundTask, STR ("Load Samples..."), mProgressID); // ... myProgressValue += incProgressStep; FUnknownPtr progress (componentHandler); if (progress) progress->update (mProgressID, myProgressValue); // ... FUnknownPtr progress (componentHandler); if (progress) progress->finish (mProgressID); \endcode \see \ref IComponentHandler */ class IProgress : public FUnknown { public: //------------------------------------------------------------------------ enum ProgressType : uint32 { /** plug-in state is restored async (in a background Thread) */ AsyncStateRestoration = 0, /** a plug-in task triggered by a UI action */ UIBackgroundTask }; using ID = uint64; /** Start a new progress of a given type and optional Description. outID is as ID created by the * host to identify this newly created progress (for update and finish method). * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API start (ProgressType type /*in*/, const tchar* optionalDescription /*in*/, ID& outID /*out*/) = 0; /** Update the progress value (normValue between [0, 1]) associated to the given id. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API update (ID id /*in*/, ParamValue normValue /*in*/) = 0; /** Finish the progress associated to the given id. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API finish (ID id /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IProgress, 0x00C9DC5B, 0x9D904254, 0x91A388C8, 0xB4E91B69) //------------------------------------------------------------------------ /** Edit controller component interface: Vst::IEditController \ingroup vstIPlug vst300 - [plug imp] - [released: 3.0.0] - [mandatory] The controller part of an effect or instrument with parameter handling (export, definition, conversion, ...). \see \ref IComponent::getControllerClassId, \ref IMidiMapping */ class IEditController : public IPluginBase { public: //------------------------------------------------------------------------ /** Receives the component state. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API setComponentState (IBStream* state /*in*/) = 0; /** Sets the controller state. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API setState (IBStream* state /*in*/) = 0; /** Gets the controller state. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getState (IBStream* state /*inout*/) = 0; // parameters ------------------------- /** Returns the number of parameters exported. * \note [UI-thread & Connected] */ virtual int32 PLUGIN_API getParameterCount () = 0; /** Gets for a given index the parameter information. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getParameterInfo (int32 paramIndex /*in*/, ParameterInfo& info /*out*/) = 0; /** Gets for a given paramID and normalized value its associated string representation. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getParamStringByValue (ParamID id /*in*/, ParamValue valueNormalized /*in*/, String128 string /*out*/) = 0; /** Gets for a given paramID and string its normalized value. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getParamValueByString (ParamID id /*in*/, TChar* string /*in*/, ParamValue& valueNormalized /*out*/) = 0; /** Returns for a given paramID and a normalized value its plain representation * (for example -6 for -6dB - see \ref vst3AutomationIntro). * \note [UI-thread & Connected] */ virtual ParamValue PLUGIN_API normalizedParamToPlain (ParamID id /*in*/, ParamValue valueNormalized /*in*/) = 0; /** Returns for a given paramID and a plain value its normalized value. (see \ref * vst3AutomationIntro). * \note [UI-thread & Connected] */ virtual ParamValue PLUGIN_API plainParamToNormalized (ParamID id /*in*/, ParamValue plainValue /*in*/) = 0; /** Returns the normalized value of the parameter associated to the paramID. * \note [UI-thread & Connected] */ virtual ParamValue PLUGIN_API getParamNormalized (ParamID id /*in*/) = 0; /** Sets the normalized value to the parameter associated to the paramID. The controller must * never pass this value-change back to the host via the IComponentHandler. * It should update the according GUI element(s) only! * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API setParamNormalized (ParamID id /*in*/, ParamValue value /*in*/) = 0; // handler ---------------------------- /** Gets from host a handler which allows the Plugin-in to communicate with the host. * \note This is mandatory if the host is using the IEditController! * \note [UI-thread & Initialized] */ virtual tresult PLUGIN_API setComponentHandler (IComponentHandler* handler /*in*/) = 0; // view ------------------------------- /** Creates the editor view of the plug-in, currently only "editor" is supported, see \ref * ViewType. The life time of the editor view will never exceed the life time of this controller * instance. * \note [UI-thread & Connected] */ virtual IPlugView* PLUGIN_API createView (FIDString name /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IEditController, 0xDCD7BBE3, 0x7742448D, 0xA874AACC, 0x979C759E) //------------------------------------------------------------------------ /** \ingroup vst3typedef */ /**@{*/ /** Knob Mode Type */ using KnobMode = int32; /**@}*/ //------------------------------------------------------------------------ /** Knob Mode */ enum KnobModes : KnobMode { kCircularMode = 0, ///< Circular with jump to clicked position kRelativCircularMode, ///< Circular without jump to clicked position kLinearMode ///< Linear: depending on vertical movement }; //------------------------------------------------------------------------ /** Edit controller component interface extension: Vst::IEditController2 \ingroup vstIPlug vst310 - [plug imp] - [extends IEditController] - [released: 3.1.0] - [optional] Extension to allow the host to inform the plug-in about the host Knob Mode, and to open the plug-in about box or help documentation. \see \ref IEditController, \ref EditController */ class IEditController2 : public FUnknown { public: /** Host could set the Knob Mode for the plug-in. Return kResultFalse means not supported mode. * \see KnobModes. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API setKnobMode (KnobMode mode /*in*/) = 0; /** Host could ask to open the plug-in help (could be: opening a PDF document or link to a web * page). The host could call it with onlyCheck set to true for testing support of open Help. * Return kResultFalse means not supported function. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API openHelp (TBool onlyCheck /*in*/) = 0; /** Host could ask to open the plug-in about box. * The host could call it with onlyCheck set to true for testing support of open AboutBox. * Return kResultFalse means not supported function. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API openAboutBox (TBool onlyCheck /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IEditController2, 0x7F4EFE59, 0xF3204967, 0xAC27A3AE, 0xAFB63038) //------------------------------------------------------------------------ /** MIDI Mapping interface: Vst::IMidiMapping \ingroup vstIPlug vst301 - [plug imp] - [extends IEditController] - [released: 3.0.1] - [optional] MIDI controllers are not transmitted directly to a VST component. MIDI as hardware protocol has restrictions that can be avoided in software. Controller data in particular come along with unclear and often ignored semantics. On top of this they can interfere with regular parameter automation and the host is unaware of what happens in the plug-in when passing MIDI controllers directly. So any functionality that is to be controlled by MIDI controllers must be exported as regular parameter. The host will transform incoming MIDI controller data using this interface and transmit them as regular parameter change. This allows the host to automate them in the same way as other parameters. CtrlNumber can be a typical MIDI controller value extended to some others values like pitchbend or aftertouch (see \ref ControllerNumbers). If the mapping has changed, the plug-in must call IComponentHandler::restartComponent (kMidiCCAssignmentChanged) to inform the host about this change. \section IMidiMappingExample Example \code{.cpp} //-------------------------------------- // in myeditcontroller.h class MyEditController: public EditControllerEx1, public IMidiMapping { //... //---IMidiMapping--------------------------- tresult PLUGIN_API getMidiControllerAssignment (int32 busIndex, int16 channel, CtrlNumber midiControllerNumber, ParamID& id) override; //---Interface--------- OBJ_METHODS (MyEditController, EditControllerEx1) DEFINE_INTERFACES DEF_INTERFACE (IMidiMapping) END_DEFINE_INTERFACES (MyEditController) REFCOUNT_METHODS (MyEditController) }; //-------------------------------------- // in myeditcontroller.cpp tresult PLUGIN_API MyEditController::getMidiControllerAssignment (int32 busIndex, int16 midiChannel, CtrlNumber midiControllerNumber, ParamID& tag) { // for my first Event bus and for MIDI channel 0 and for MIDI CC Volume only if (busIndex == 0 && midiChannel == 0 && midiControllerNumber == kCtrlVolume) { tag = kGainId; return kResultTrue; } return kResultFalse; } \endcode */ class IMidiMapping : public FUnknown { public: /** Gets an (preferred) associated ParamID for a given Input Event Bus index, channel and MIDI * Controller. * @param[in] busIndex - index of Input Event Bus * @param[in] channel - channel of the bus * @param[in] midiControllerNumber - see \ref ControllerNumbers for expected values (could be * bigger than 127) * @param[out] id - return the associated ParamID to the given midiControllerNumber * * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getMidiControllerAssignment (int32 busIndex /*in*/, int16 channel /*in*/, CtrlNumber midiControllerNumber /*in*/, ParamID& id /*out*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IMidiMapping, 0xDF0FF9F7, 0x49B74669, 0xB63AB732, 0x7ADBF5E5) //------------------------------------------------------------------------ /** Parameter Editing from host: Vst::IEditControllerHostEditing \ingroup vstIPlug vst350 - [plug imp] - [extends IEditController] - [released: 3.5.0] - [optional] If this interface is implemented by the edit controller, and when performing edits from outside the plug-in (host / remote) of a not automatable and not read-only, and not hidden flagged parameter (kind of helper parameter), the host will start with a beginEditFromHost before calling setParamNormalized and end with an endEditFromHost. Here the sequence that the host will call: \section IEditControllerExample Example \code{.cpp} //------------------------------------------------------------------------ plugEditController->beginEditFromHost (id); plugEditController->setParamNormalized (id, value); plugEditController->setParamNormalized (id, value + 0.1); // ... plugEditController->endEditFromHost (id); \endcode \see \ref IEditController */ class IEditControllerHostEditing : public FUnknown { public: /** Called before a setParamNormalized sequence, a endEditFromHost will be call at the end of * the editing action. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API beginEditFromHost (ParamID paramID /*in*/) = 0; /** Called after a beginEditFromHost and a sequence of setParamNormalized. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API endEditFromHost (ParamID paramID /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IEditControllerHostEditing, 0xC1271208, 0x70594098, 0xB9DD34B3, 0x6BB0195E) //------------------------------------------------------------------------ /** Extended plug-in interface IComponentHandler for an edit controller \ingroup vstIHost vst379 - [host imp] - [extends IComponentHandler] - [released: 3.7.9] - [optional] */ //------------------------------------------------------------------------ class IComponentHandlerSystemTime : public FUnknown { public: //------------------------------------------------------------------------ /** get the current systemTime (the same as the one used in ProcessContext::systemTime). * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getSystemTime (int64& systemTime /*out*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IComponentHandlerSystemTime, 0xF9E53056, 0xD1554CD5, 0xB7695E1B, 0x7B0F7745) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstmidicontrollers.h0000644000000000000000000000013215124701711024320 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstmidicontrollers.h0000644000175000001440000001303415124701711024311 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstmidicontrollers.h // Created by : Steinberg, 02/2006 // Description : VST MIDI Controller Enumeration // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Controller Numbers (MIDI) */ enum ControllerNumbers { kCtrlBankSelectMSB = 0, ///< Bank Select MSB kCtrlModWheel = 1, ///< Modulation Wheel kCtrlBreath = 2, ///< Breath controller kCtrlFoot = 4, ///< Foot Controller kCtrlPortaTime = 5, ///< Portamento Time kCtrlDataEntryMSB = 6, ///< Data Entry MSB kCtrlVolume = 7, ///< Channel Volume (formerly Main Volume) kCtrlBalance = 8, ///< Balance kCtrlPan = 10, ///< Pan kCtrlExpression = 11, ///< Expression kCtrlEffect1 = 12, ///< Effect Control 1 kCtrlEffect2 = 13, ///< Effect Control 2 //---General Purpose Controllers #1 to #4--- kCtrlGPC1 = 16, ///< General Purpose Controller #1 kCtrlGPC2 = 17, ///< General Purpose Controller #2 kCtrlGPC3 = 18, ///< General Purpose Controller #3 kCtrlGPC4 = 19, ///< General Purpose Controller #4 kCtrlBankSelectLSB = 32, ///< Bank Select LSB kCtrlDataEntryLSB = 38, ///< Data Entry LSB kCtrlSustainOnOff = 64, ///< Damper Pedal On/Off (Sustain) kCtrlPortaOnOff = 65, ///< Portamento On/Off kCtrlSustenutoOnOff = 66, ///< Sustenuto On/Off kCtrlSoftPedalOnOff = 67, ///< Soft Pedal On/Off kCtrlLegatoFootSwOnOff= 68, ///< Legato Footswitch On/Off kCtrlHold2OnOff = 69, ///< Hold 2 On/Off //---Sound Controllers #1 to #10--- kCtrlSoundVariation = 70, ///< Sound Variation kCtrlFilterCutoff = 71, ///< Filter Cutoff (Timbre/Harmonic Intensity) kCtrlReleaseTime = 72, ///< Release Time kCtrlAttackTime = 73, ///< Attack Time kCtrlFilterResonance= 74, ///< Filter Resonance (Brightness) kCtrlDecayTime = 75, ///< Decay Time kCtrlVibratoRate = 76, ///< Vibrato Rate kCtrlVibratoDepth = 77, ///< Vibrato Depth kCtrlVibratoDelay = 78, ///< Vibrato Delay kCtrlSoundCtrler10 = 79, ///< undefined //---General Purpose Controllers #5 to #8--- kCtrlGPC5 = 80, ///< General Purpose Controller #5 kCtrlGPC6 = 81, ///< General Purpose Controller #6 kCtrlGPC7 = 82, ///< General Purpose Controller #7 kCtrlGPC8 = 83, ///< General Purpose Controller #8 kCtrlPortaControl = 84, ///< Portamento Control //---Effect Controllers--- kCtrlEff1Depth = 91, ///< Effect 1 Depth (Reverb Send Level) kCtrlEff2Depth = 92, ///< Effect 2 Depth (Tremolo Level) kCtrlEff3Depth = 93, ///< Effect 3 Depth (Chorus Send Level) kCtrlEff4Depth = 94, ///< Effect 4 Depth (Delay/Variation/Detune Level) kCtrlEff5Depth = 95, ///< Effect 5 Depth (Phaser Level) kCtrlDataIncrement = 96, ///< Data Increment (+1) kCtrlDataDecrement = 97, ///< Data Decrement (-1) kCtrlNRPNSelectLSB = 98, ///< NRPN Select LSB kCtrlNRPNSelectMSB = 99, ///< NRPN Select MSB kCtrlRPNSelectLSB = 100, ///< RPN Select LSB kCtrlRPNSelectMSB = 101, ///< RPN Select MSB //---Other Channel Mode Messages--- kCtrlAllSoundsOff = 120, ///< All Sounds Off kCtrlResetAllCtrlers = 121, ///< Reset All Controllers kCtrlLocalCtrlOnOff = 122, ///< Local Control On/Off kCtrlAllNotesOff = 123, ///< All Notes Off kCtrlOmniModeOff = 124, ///< Omni Mode Off + All Notes Off kCtrlOmniModeOn = 125, ///< Omni Mode On + All Notes Off kCtrlPolyModeOnOff = 126, ///< Poly Mode On/Off + All Sounds Off kCtrlPolyModeOn = 127, ///< Poly Mode On //---Extra-------------------------- kAfterTouch = 128, ///< After Touch (associated to Channel Pressure) kPitchBend = 129, ///< Pitch Bend Change kCountCtrlNumber, ///< Count of Controller Number //---Extra for kLegacyMIDICCOutEvent- kCtrlProgramChange = 130, ///< Program Change (use LegacyMIDICCOutEvent.value only) kCtrlPolyPressure = 131, ///< Polyphonic Key Pressure (use LegacyMIDICCOutEvent.value for pitch and /// LegacyMIDICCOutEvent.value2 for pressure) kCtrlQuarterFrame = 132, ///< Quarter Frame ((use LegacyMIDICCOutEvent.value only) kSystemSongSelect = 133, ///< Song Select (use LegacyMIDICCOutEvent.value only) kSystemSongPointer = 134, ///< Song Pointer (use LegacyMIDICCOutEvent.value for LSB and /// LegacyMIDICCOutEvent.value2 for MSB) kSystemCableSelect = 135, ///< Cable Select (use LegacyMIDICCOutEvent.value only) kSystemTuneRequest = 136, ///< Tune Request (use LegacyMIDICCOutEvent.value only) kSystemMidiClockStart = 137, ///< Midi Clock Start (use LegacyMIDICCOutEvent.value only) kSystemMidiClockContinue = 138, ///< Midi Clock Continue (use LegacyMIDICCOutEvent.value only) kSystemMidiClockStop = 139, ///< Midi Clock Stop (use LegacyMIDICCOutEvent.value only) kSystemActiveSensing = 140, ///< Active Sensing (use LegacyMIDICCOutEvent.value only) }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/vstpresetkeys.h0000644000000000000000000000013215124701711023134 xustar0030 mtime=1767080905.195211228 30 atime=1767080905.194211232 30 ctime=1767080905.195211228 qtractor-1.5.11/src/vst3/pluginterfaces/vst/vstpresetkeys.h0000644000175000001440000004501015124701711023124 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/vstpresetkeys.h // Created by : Steinberg, 2006 // Description : VST Preset Keys // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Predefined Preset Attributes */ //------------------------------------------------------------------------ namespace PresetAttributes { /** * \defgroup presetAttributes Predefined Preset Attributes */ /**@{*/ const CString kPlugInName = "PlugInName"; ///< plug-in name const CString kPlugInCategory = "PlugInCategory"; ///< eg. "Fx|Dynamics", "Instrument", "Instrument|Synth" const CString kInstrument = "MusicalInstrument";///< eg. instrument group (like 'Piano' or 'Piano|A. Piano') const CString kStyle = "MusicalStyle"; ///< eg. 'Pop', 'Jazz', 'Classic' const CString kCharacter = "MusicalCharacter"; ///< eg. instrument nature (like 'Soft' 'Dry' 'Acoustic') const CString kStateType = "StateType"; ///< Type of the given state see \ref StateType : Project / Default Preset or Normal Preset const CString kFilePathStringType = "FilePathString"; ///< Full file path string (if available) where the preset comes from (be sure to use a bigger string when asking for it (with 1024 characters)) const CString kName = "Name"; ///< name of the preset const CString kFileName = "FileName"; ///< filename of the preset (including extension) /**@}*/ }; //------------------------------------------------------------------------ /** Predefined StateType used for Key kStateType */ //------------------------------------------------------------------------ namespace StateType { /** * \defgroup stateType Context of State Restoration * used for PresetAttributes::kStateType in IStreamAttributes */ /**@{*/ /** the state is restored from a project loading or it is saved in a project */ const CString kProject = "Project"; /** the state is restored from a preset (marked as default) or the host wants to store a default * state of the plug-in */ const CString kDefault = "Default"; /** the state is restored from a track preset */ const CString kTrackPreset = "TrackPreset"; //------------------------------------------------------------------------ /**@}*/ } //------------------------------------------------------------------------ /** Predefined Musical Instrument */ //------------------------------------------------------------------------ namespace MusicalInstrument { /** * \defgroup musicalInstrument Predefined Musical Instrument */ /**@{*/ const CString kAccordion = "Accordion"; const CString kAccordionAccordion = "Accordion|Accordion"; const CString kAccordionHarmonica = "Accordion|Harmonica"; const CString kAccordionOther = "Accordion|Other"; const CString kBass = "Bass"; const CString kBassABass = "Bass|A. Bass"; const CString kBassEBass = "Bass|E. Bass"; const CString kBassSynthBass = "Bass|Synth Bass"; const CString kBassOther = "Bass|Other"; const CString kBrass = "Brass"; const CString kBrassFrenchHorn = "Brass|French Horn"; const CString kBrassTrumpet = "Brass|Trumpet"; const CString kBrassTrombone = "Brass|Trombone"; const CString kBrassTuba = "Brass|Tuba"; const CString kBrassSection = "Brass|Section"; const CString kBrassSynth = "Brass|Synth"; const CString kBrassOther = "Brass|Other"; const CString kChromaticPerc = "Chromatic Perc"; const CString kChromaticPercBell = "Chromatic Perc|Bell"; const CString kChromaticPercMallett = "Chromatic Perc|Mallett"; const CString kChromaticPercWood = "Chromatic Perc|Wood"; const CString kChromaticPercPercussion = "Chromatic Perc|Percussion"; const CString kChromaticPercTimpani = "Chromatic Perc|Timpani"; const CString kChromaticPercOther = "Chromatic Perc|Other"; const CString kDrumPerc = "Drum&Perc"; const CString kDrumPercDrumsetGM = "Drum&Perc|Drumset GM"; const CString kDrumPercDrumset = "Drum&Perc|Drumset"; const CString kDrumPercDrumMenues = "Drum&Perc|Drum Menues"; const CString kDrumPercBeats = "Drum&Perc|Beats"; const CString kDrumPercPercussion = "Drum&Perc|Percussion"; const CString kDrumPercKickDrum = "Drum&Perc|Kick Drum"; const CString kDrumPercSnareDrum = "Drum&Perc|Snare Drum"; const CString kDrumPercToms = "Drum&Perc|Toms"; const CString kDrumPercHiHats = "Drum&Perc|HiHats"; const CString kDrumPercCymbals = "Drum&Perc|Cymbals"; const CString kDrumPercOther = "Drum&Perc|Other"; const CString kEthnic = "Ethnic"; const CString kEthnicAsian = "Ethnic|Asian"; const CString kEthnicAfrican = "Ethnic|African"; const CString kEthnicEuropean = "Ethnic|European"; const CString kEthnicLatin = "Ethnic|Latin"; const CString kEthnicAmerican = "Ethnic|American"; const CString kEthnicAlien = "Ethnic|Alien"; const CString kEthnicOther = "Ethnic|Other"; const CString kGuitar = "Guitar/Plucked"; const CString kGuitarAGuitar = "Guitar/Plucked|A. Guitar"; const CString kGuitarEGuitar = "Guitar/Plucked|E. Guitar"; const CString kGuitarHarp = "Guitar/Plucked|Harp"; const CString kGuitarEthnic = "Guitar/Plucked|Ethnic"; const CString kGuitarOther = "Guitar/Plucked|Other"; const CString kKeyboard = "Keyboard"; const CString kKeyboardClavi = "Keyboard|Clavi"; const CString kKeyboardEPiano = "Keyboard|E. Piano"; const CString kKeyboardHarpsichord = "Keyboard|Harpsichord"; const CString kKeyboardOther = "Keyboard|Other"; const CString kMusicalFX = "Musical FX"; const CString kMusicalFXHitsStabs = "Musical FX|Hits&Stabs"; const CString kMusicalFXMotion = "Musical FX|Motion"; const CString kMusicalFXSweeps = "Musical FX|Sweeps"; const CString kMusicalFXBeepsBlips = "Musical FX|Beeps&Blips"; const CString kMusicalFXScratches = "Musical FX|Scratches"; const CString kMusicalFXOther = "Musical FX|Other"; const CString kOrgan = "Organ"; const CString kOrganElectric = "Organ|Electric"; const CString kOrganPipe = "Organ|Pipe"; const CString kOrganOther = "Organ|Other"; const CString kPiano = "Piano"; const CString kPianoAPiano = "Piano|A. Piano"; const CString kPianoEGrand = "Piano|E. Grand"; const CString kPianoOther = "Piano|Other"; const CString kSoundFX = "Sound FX"; const CString kSoundFXNature = "Sound FX|Nature"; const CString kSoundFXMechanical = "Sound FX|Mechanical"; const CString kSoundFXSynthetic = "Sound FX|Synthetic"; const CString kSoundFXOther = "Sound FX|Other"; const CString kStrings = "Strings"; const CString kStringsViolin = "Strings|Violin"; const CString kStringsViola = "Strings|Viola"; const CString kStringsCello = "Strings|Cello"; const CString kStringsBass = "Strings|Bass"; const CString kStringsSection = "Strings|Section"; const CString kStringsSynth = "Strings|Synth"; const CString kStringsOther = "Strings|Other"; const CString kSynthLead = "Synth Lead"; const CString kSynthLeadAnalog = "Synth Lead|Analog"; const CString kSynthLeadDigital = "Synth Lead|Digital"; const CString kSynthLeadArpeggio = "Synth Lead|Arpeggio"; const CString kSynthLeadOther = "Synth Lead|Other"; const CString kSynthPad = "Synth Pad"; const CString kSynthPadSynthChoir = "Synth Pad|Synth Choir"; const CString kSynthPadAnalog = "Synth Pad|Analog"; const CString kSynthPadDigital = "Synth Pad|Digital"; const CString kSynthPadMotion = "Synth Pad|Motion"; const CString kSynthPadOther = "Synth Pad|Other"; const CString kSynthComp = "Synth Comp"; const CString kSynthCompAnalog = "Synth Comp|Analog"; const CString kSynthCompDigital = "Synth Comp|Digital"; const CString kSynthCompOther = "Synth Comp|Other"; const CString kVocal = "Vocal"; const CString kVocalLeadVocal = "Vocal|Lead Vocal"; const CString kVocalAdlibs = "Vocal|Adlibs"; const CString kVocalChoir = "Vocal|Choir"; const CString kVocalSolo = "Vocal|Solo"; const CString kVocalFX = "Vocal|FX"; const CString kVocalSpoken = "Vocal|Spoken"; const CString kVocalOther = "Vocal|Other"; const CString kWoodwinds = "Woodwinds"; const CString kWoodwindsEthnic = "Woodwinds|Ethnic"; const CString kWoodwindsFlute = "Woodwinds|Flute"; const CString kWoodwindsOboe = "Woodwinds|Oboe"; const CString kWoodwindsEnglHorn = "Woodwinds|Engl. Horn"; const CString kWoodwindsClarinet = "Woodwinds|Clarinet"; const CString kWoodwindsSaxophone = "Woodwinds|Saxophone"; const CString kWoodwindsBassoon = "Woodwinds|Bassoon"; const CString kWoodwindsOther = "Woodwinds|Other"; /**@}*/ }; //------------------------------------------------------------------------ /** Predefined Musical Style */ namespace MusicalStyle { /** * \defgroup musicalStyle Predefined Musical Style */ /**@{*/ const CString kAlternativeIndie = "Alternative/Indie"; const CString kAlternativeIndieGothRock = "Alternative/Indie|Goth Rock"; const CString kAlternativeIndieGrunge = "Alternative/Indie|Grunge"; const CString kAlternativeIndieNewWave = "Alternative/Indie|New Wave"; const CString kAlternativeIndiePunk = "Alternative/Indie|Punk"; const CString kAlternativeIndieCollegeRock = "Alternative/Indie|College Rock"; const CString kAlternativeIndieDarkWave = "Alternative/Indie|Dark Wave"; const CString kAlternativeIndieHardcore = "Alternative/Indie|Hardcore"; const CString kAmbientChillOut = "Ambient/ChillOut"; const CString kAmbientChillOutNewAgeMeditation = "Ambient/ChillOut|New Age/Meditation"; const CString kAmbientChillOutDarkAmbient = "Ambient/ChillOut|Dark Ambient"; const CString kAmbientChillOutDowntempo = "Ambient/ChillOut|Downtempo"; const CString kAmbientChillOutLounge = "Ambient/ChillOut|Lounge"; const CString kBlues = "Blues"; const CString kBluesAcousticBlues = "Blues|Acoustic Blues"; const CString kBluesCountryBlues = "Blues|Country Blues"; const CString kBluesElectricBlues = "Blues|Electric Blues"; const CString kBluesChicagoBlues = "Blues|Chicago Blues"; const CString kClassical = "Classical"; const CString kClassicalBaroque = "Classical|Baroque"; const CString kClassicalChamberMusic = "Classical|Chamber Music"; const CString kClassicalMedieval = "Classical|Medieval"; const CString kClassicalModernComposition = "Classical|Modern Composition"; const CString kClassicalOpera = "Classical|Opera"; const CString kClassicalGregorian = "Classical|Gregorian"; const CString kClassicalRenaissance = "Classical|Renaissance"; const CString kClassicalClassic = "Classical|Classic"; const CString kClassicalRomantic = "Classical|Romantic"; const CString kClassicalSoundtrack = "Classical|Soundtrack"; const CString kCountry = "Country"; const CString kCountryCountryWestern = "Country|Country/Western"; const CString kCountryHonkyTonk = "Country|Honky Tonk"; const CString kCountryUrbanCowboy = "Country|Urban Cowboy"; const CString kCountryBluegrass = "Country|Bluegrass"; const CString kCountryAmericana = "Country|Americana"; const CString kCountrySquaredance = "Country|Squaredance"; const CString kCountryNorthAmericanFolk = "Country|North American Folk"; const CString kElectronicaDance = "Electronica/Dance"; const CString kElectronicaDanceMinimal = "Electronica/Dance|Minimal"; const CString kElectronicaDanceClassicHouse = "Electronica/Dance|Classic House"; const CString kElectronicaDanceElektroHouse = "Electronica/Dance|Elektro House"; const CString kElectronicaDanceFunkyHouse = "Electronica/Dance|Funky House"; const CString kElectronicaDanceIndustrial = "Electronica/Dance|Industrial"; const CString kElectronicaDanceElectronicBodyMusic = "Electronica/Dance|Electronic Body Music"; const CString kElectronicaDanceTripHop = "Electronica/Dance|Trip Hop"; const CString kElectronicaDanceTechno = "Electronica/Dance|Techno"; const CString kElectronicaDanceDrumNBassJungle = "Electronica/Dance|Drum'n'Bass/Jungle"; const CString kElectronicaDanceElektro = "Electronica/Dance|Elektro"; const CString kElectronicaDanceTrance = "Electronica/Dance|Trance"; const CString kElectronicaDanceDub = "Electronica/Dance|Dub"; const CString kElectronicaDanceBigBeats = "Electronica/Dance|Big Beats"; const CString kExperimental = "Experimental"; const CString kExperimentalNewMusic = "Experimental|New Music"; const CString kExperimentalFreeImprovisation = "Experimental|Free Improvisation"; const CString kExperimentalElectronicArtMusic = "Experimental|Electronic Art Music"; const CString kExperimentalNoise = "Experimental|Noise"; const CString kJazz = "Jazz"; const CString kJazzNewOrleansJazz = "Jazz|New Orleans Jazz"; const CString kJazzTraditionalJazz = "Jazz|Traditional Jazz"; const CString kJazzOldtimeJazzDixiland = "Jazz|Oldtime Jazz/Dixiland"; const CString kJazzFusion = "Jazz|Fusion"; const CString kJazzAvantgarde = "Jazz|Avantgarde"; const CString kJazzLatinJazz = "Jazz|Latin Jazz"; const CString kJazzFreeJazz = "Jazz|Free Jazz"; const CString kJazzRagtime = "Jazz|Ragtime"; const CString kPop = "Pop"; const CString kPopBritpop = "Pop|Britpop"; const CString kPopRock = "Pop|Pop/Rock"; const CString kPopTeenPop = "Pop|Teen Pop"; const CString kPopChartDance = "Pop|Chart Dance"; const CString kPop80sPop = "Pop|80's Pop"; const CString kPopDancehall = "Pop|Dancehall"; const CString kPopDisco = "Pop|Disco"; const CString kRockMetal = "Rock/Metal"; const CString kRockMetalBluesRock = "Rock/Metal|Blues Rock"; const CString kRockMetalClassicRock = "Rock/Metal|Classic Rock"; const CString kRockMetalHardRock = "Rock/Metal|Hard Rock"; const CString kRockMetalRockRoll = "Rock/Metal|Rock & Roll"; const CString kRockMetalSingerSongwriter = "Rock/Metal|Singer/Songwriter"; const CString kRockMetalHeavyMetal = "Rock/Metal|Heavy Metal"; const CString kRockMetalDeathBlackMetal = "Rock/Metal|Death/Black Metal"; const CString kRockMetalNuMetal = "Rock/Metal|NuMetal"; const CString kRockMetalReggae = "Rock/Metal|Reggae"; const CString kRockMetalBallad = "Rock/Metal|Ballad"; const CString kRockMetalAlternativeRock = "Rock/Metal|Alternative Rock"; const CString kRockMetalRockabilly = "Rock/Metal|Rockabilly"; const CString kRockMetalThrashMetal = "Rock/Metal|Thrash Metal"; const CString kRockMetalProgressiveRock = "Rock/Metal|Progressive Rock"; const CString kUrbanHipHopRB = "Urban (Hip-Hop / R&B)"; const CString kUrbanHipHopRBClassic = "Urban (Hip-Hop / R&B)|Classic R&B"; const CString kUrbanHipHopRBModern = "Urban (Hip-Hop / R&B)|Modern R&B"; const CString kUrbanHipHopRBPop = "Urban (Hip-Hop / R&B)|R&B Pop"; const CString kUrbanHipHopRBWestCoastHipHop = "Urban (Hip-Hop / R&B)|WestCoast Hip-Hop"; const CString kUrbanHipHopRBEastCoastHipHop = "Urban (Hip-Hop / R&B)|EastCoast Hip-Hop"; const CString kUrbanHipHopRBRapHipHop = "Urban (Hip-Hop / R&B)|Rap/Hip Hop"; const CString kUrbanHipHopRBSoul = "Urban (Hip-Hop / R&B)|Soul"; const CString kUrbanHipHopRBFunk = "Urban (Hip-Hop / R&B)|Funk"; const CString kWorldEthnic = "World/Ethnic"; const CString kWorldEthnicAfrica = "World/Ethnic|Africa"; const CString kWorldEthnicAsia = "World/Ethnic|Asia"; const CString kWorldEthnicCeltic = "World/Ethnic|Celtic"; const CString kWorldEthnicEurope = "World/Ethnic|Europe"; const CString kWorldEthnicKlezmer = "World/Ethnic|Klezmer"; const CString kWorldEthnicScandinavia = "World/Ethnic|Scandinavia"; const CString kWorldEthnicEasternEurope = "World/Ethnic|Eastern Europe"; const CString kWorldEthnicIndiaOriental = "World/Ethnic|India/Oriental"; const CString kWorldEthnicNorthAmerica = "World/Ethnic|North America"; const CString kWorldEthnicSouthAmerica = "World/Ethnic|South America"; const CString kWorldEthnicAustralia = "World/Ethnic|Australia"; /**@}*/ }; //------------------------------------------------------------------------ /** Predefined Musical Character */ namespace MusicalCharacter { /** \defgroup musicalCharacter Predefined Musical Character */ /**@{*/ //----TYPE------------------------------------ const CString kMono = "Mono"; const CString kPoly = "Poly"; const CString kSplit = "Split"; const CString kLayer = "Layer"; const CString kGlide = "Glide"; const CString kGlissando = "Glissando"; const CString kMajor = "Major"; const CString kMinor = "Minor"; const CString kSingle = "Single"; const CString kEnsemble = "Ensemble"; const CString kAcoustic = "Acoustic"; const CString kElectric = "Electric"; const CString kAnalog = "Analog"; const CString kDigital = "Digital"; const CString kVintage = "Vintage"; const CString kModern = "Modern"; const CString kOld = "Old"; const CString kNew = "New"; //----TONE------------------------------------ const CString kClean = "Clean"; const CString kDistorted = "Distorted"; const CString kDry = "Dry"; const CString kProcessed = "Processed"; const CString kHarmonic = "Harmonic"; const CString kDissonant = "Dissonant"; const CString kClear = "Clear"; const CString kNoisy = "Noisy"; const CString kThin = "Thin"; const CString kRich = "Rich"; const CString kDark = "Dark"; const CString kBright = "Bright"; const CString kCold = "Cold"; const CString kWarm = "Warm"; const CString kMetallic = "Metallic"; const CString kWooden = "Wooden"; const CString kGlass = "Glass"; const CString kPlastic = "Plastic"; //----ENVELOPE------------------------------------ const CString kPercussive = "Percussive"; const CString kSoft = "Soft"; const CString kFast = "Fast"; const CString kSlow = "Slow"; const CString kShort = "Short"; const CString kLong = "Long"; const CString kAttack = "Attack"; const CString kRelease = "Release"; const CString kDecay = "Decay"; const CString kSustain = "Sustain"; const CString kFastAttack = "Fast Attack"; const CString kSlowAttack = "Slow Attack"; const CString kShortRelease = "Short Release"; const CString kLongRelease = "Long Release"; const CString kStatic = "Static"; const CString kMoving = "Moving"; const CString kLoop = "Loop"; const CString kOneShot = "One Shot"; /**@}*/ }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstinterappaudio.h0000644000000000000000000000013215124701711023753 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstinterappaudio.h0000644000175000001440000001152615124701711023750 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstinterappaudio.h // Created by : Steinberg, 08/2013 // Description : VST InterAppAudio Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" //------------------------------------------------------------------------ namespace Steinberg { struct ViewRect; namespace Vst { struct Event; class IInterAppAudioPresetManager; //------------------------------------------------------------------------ /** Inter-App Audio host Interface. \ingroup vstIHost vst360 - [host imp] - [passed as 'context' to IPluginBase::initialize () ] - [released: 3.6.0] - [optional] Implemented by the InterAppAudio Wrapper. */ class IInterAppAudioHost : public FUnknown { public: /** get the size of the screen * @param size size of the screen * @param scale scale of the screen * @return kResultTrue on success */ virtual tresult PLUGIN_API getScreenSize (ViewRect* size, float* scale) = 0; /** get status of connection * @return kResultTrue if an Inter-App Audio connection is established */ virtual tresult PLUGIN_API connectedToHost () = 0; /** switch to the host. * @return kResultTrue on success */ virtual tresult PLUGIN_API switchToHost () = 0; /** send a remote control event to the host * @param event event type, see AudioUnitRemoteControlEvent in the iOS SDK documentation for possible types * @return kResultTrue on success */ virtual tresult PLUGIN_API sendRemoteControlEvent (uint32 event) = 0; /** ask for the host icon. * @param icon pointer to a CGImageRef * @return kResultTrue on success */ virtual tresult PLUGIN_API getHostIcon (void** icon) = 0; /** schedule an event from the user interface thread * @param event the event to schedule * @return kResultTrue on success */ virtual tresult PLUGIN_API scheduleEventFromUI (Event& event) = 0; /** get the preset manager * @param cid class ID to use by the preset manager * @return the preset manager. Needs to be released by called. */ virtual IInterAppAudioPresetManager* PLUGIN_API createPresetManager (const TUID& cid) = 0; /** show the settings view * currently includes MIDI settings and Tempo setting * @return kResultTrue on success */ virtual tresult PLUGIN_API showSettingsView () = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IInterAppAudioHost, 0x0CE5743D, 0x68DF415E, 0xAE285BD4, 0xE2CDC8FD) //------------------------------------------------------------------------ /** Extended plug-in interface IEditController for Inter-App Audio connection state change notifications \ingroup vstIPlug vst360 - [plug imp] - [extends IEditController] - [released: 3.6.0] */ class IInterAppAudioConnectionNotification : public FUnknown { public: /** called when the Inter-App Audio connection state changes * @param newState true if an Inter-App Audio connection is established, otherwise false */ virtual void PLUGIN_API onInterAppAudioConnectionStateChange (TBool newState) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IInterAppAudioConnectionNotification, 0x6020C72D, 0x5FC24AA1, 0xB0950DB5, 0xD7D6D5CF) //------------------------------------------------------------------------ /** Extended plug-in interface IEditController for Inter-App Audio Preset Management \ingroup vstIPlug vst360 - [plug imp] - [extends IEditController] - [released: 3.6.0] */ class IInterAppAudioPresetManager : public FUnknown { public: /** Open the Preset Browser in order to load a preset */ virtual tresult PLUGIN_API runLoadPresetBrowser () = 0; /** Open the Preset Browser in order to save a preset */ virtual tresult PLUGIN_API runSavePresetBrowser () = 0; /** Load the next available preset */ virtual tresult PLUGIN_API loadNextPreset () = 0; /** Load the previous available preset */ virtual tresult PLUGIN_API loadPreviousPreset () = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IInterAppAudioPresetManager, 0xADE6FCC4, 0x46C94E1D, 0xB3B49A80, 0xC93FEFDD) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivsthostapplication.h0000644000000000000000000000013215124701711024310 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivsthostapplication.h0000644000175000001440000001344715124701711024311 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivsthostapplication.h // Created by : Steinberg, 04/2006 // Description : VST Host Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/vst/ivstmessage.h" //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Basic host callback interface: Vst::IHostApplication \ingroup vstIHost vst300 - [host imp] - [passed as 'context' in to IPluginBase::initialize () ] - [released: 3.0.0] - [mandatory] Basic VST host application interface. */ class IHostApplication : public FUnknown { public: //------------------------------------------------------------------------ /** Gets host application name. * \note [UI-thread & Initialized] */ virtual tresult PLUGIN_API getName (String128 name) = 0; /** Creates host object (for example: Vst::IMessage). * \note [UI-thread & Initialized] */ virtual tresult PLUGIN_API createInstance (TUID cid, TUID _iid, void** obj) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IHostApplication, 0x58E595CC, 0xDB2D4969, 0x8B6AAF8C, 0x36A664E5) //------------------------------------------------------------------------ /** Helper to allocate a message */ inline IMessage* allocateMessage (IHostApplication* host) { TUID iid; IMessage::iid.toTUID (iid); IMessage* m = nullptr; if (host->createInstance (iid, iid, (void**)&m) == kResultOk) return m; return nullptr; } //------------------------------------------------------------------------ /** VST 3 to VST 2 Wrapper interface: Vst::IVst3ToVst2Wrapper \ingroup vstIHost vst310 - [host imp] - [passed as 'context' to IPluginBase::initialize () ] - [released: 3.1.0] - [mandatory] Informs the plug-in that a VST 3 to VST 2 wrapper is used between the plug-in and the real host. Implemented by the VST 2 Wrapper. */ class IVst3ToVst2Wrapper : public FUnknown { public: //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IVst3ToVst2Wrapper, 0x29633AEC, 0x1D1C47E2, 0xBB85B97B, 0xD36EAC61) //------------------------------------------------------------------------ /** VST 3 to AU Wrapper interface: Vst::IVst3ToAUWrapper \ingroup vstIHost vst310 - [host imp] - [passed as 'context' to IPluginBase::initialize () ] - [released: 3.1.0] - [mandatory] Informs the plug-in that a VST 3 to AU wrapper is used between the plug-in and the real host. Implemented by the AU Wrapper. */ class IVst3ToAUWrapper : public FUnknown { public: //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IVst3ToAUWrapper, 0xA3B8C6C5, 0xC0954688, 0xB0916F0B, 0xB697AA44) //------------------------------------------------------------------------ /** VST 3 to AAX Wrapper interface: Vst::IVst3ToAAXWrapper \ingroup vstIHost vst368 - [host imp] - [passed as 'context' to IPluginBase::initialize () ] - [released: 3.6.8] - [mandatory] Informs the plug-in that a VST 3 to AAX wrapper is used between the plug-in and the real host. Implemented by the AAX Wrapper. */ class IVst3ToAAXWrapper : public FUnknown { public: //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IVst3ToAAXWrapper, 0x6D319DC6, 0x60C56242, 0xB32C951B, 0x93BEF4C6) //------------------------------------------------------------------------ /** Wrapper MPE Support interface: Vst::IVst3WrapperMPESupport \ingroup vstIHost vst3612 - [host imp] - [passed as 'context' to IPluginBase::initialize () ] - [released: 3.6.12] - [optional] Implemented on wrappers that support MPE to Note Expression translation. By default, MPE input processing is enabled, the masterChannel will be zero, the memberBeginChannel will be one and the memberEndChannel will be 14. As MPE is a subset of the VST3 Note Expression feature, mapping from the three MPE expressions is handled via the INoteExpressionPhysicalUIMapping interface. */ class IVst3WrapperMPESupport : public FUnknown { public: //------------------------------------------------------------------------ /** enable or disable MPE processing * @param state true to enable, false to disable MPE processing * @return kResultTrue on success */ virtual tresult PLUGIN_API enableMPEInputProcessing (TBool state) = 0; /** setup the MPE processing * @param masterChannel MPE master channel (zero based) * @param memberBeginChannel MPE member begin channel (zero based) * @param memberEndChannel MPE member end channel (zero based) * @return kResultTrue on success */ virtual tresult PLUGIN_API setMPEInputDeviceSettings (int32 masterChannel, int32 memberBeginChannel, int32 memberEndChannel) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IVst3WrapperMPESupport, 0x44149067, 0x42CF4BF9, 0x8800B750, 0xF7359FE3) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstattributes.h0000644000000000000000000000012715124701711023301 xustar0029 mtime=1767080905.19378968 29 atime=1767080905.19378968 29 ctime=1767080905.19378968 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstattributes.h0000644000175000001440000001301715124701711023267 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstattributes.h // Created by : Steinberg, 05/2006 // Description : VST Attribute Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Attribute list used in IMessage and IStreamAttributes: Vst::IAttributeList \ingroup vstIHost vst300 - [host imp] - [released: 3.0.0] - [mandatory] An attribute list associates values with a key (id: some predefined keys can be found in \ref presetAttributes). */ class IAttributeList : public FUnknown { public: //------------------------------------------------------------------------ /** \ingroup vst3typedef */ typedef const char* AttrID; /** Sets integer value. */ virtual tresult PLUGIN_API setInt (AttrID id /*in*/, int64 value /*in*/) = 0; /** Gets integer value. */ virtual tresult PLUGIN_API getInt (AttrID id /*in*/, int64& value /*out*/) = 0; /** Sets float value. */ virtual tresult PLUGIN_API setFloat (AttrID id /*in*/, double value /*in*/) = 0; /** Gets float value. */ virtual tresult PLUGIN_API getFloat (AttrID id /*in*/, double& value /*out*/) = 0; /** Sets string value (UTF16) (must be null-terminated!). */ virtual tresult PLUGIN_API setString (AttrID id /*in*/, const TChar* string /*in*/) = 0; /** Gets string value (UTF16). Note that Size is in Byte, not the string Length! Do not forget to multiply the length by sizeof (TChar)! */ virtual tresult PLUGIN_API getString (AttrID id /*in*/, TChar* string /*out*/, uint32 sizeInBytes /*in*/) = 0; /** Sets binary data. */ virtual tresult PLUGIN_API setBinary (AttrID id /*in*/, const void* data /*in*/, uint32 sizeInBytes /*in*/) = 0; /** Gets binary data. */ virtual tresult PLUGIN_API getBinary (AttrID id /*in*/, const void*& data /*out*/, uint32& sizeInBytes) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IAttributeList, 0x1E5F0AEB, 0xCC7F4533, 0xA2544011, 0x38AD5EE4) //------------------------------------------------------------------------ /** Meta attributes of a stream: Vst::IStreamAttributes \ingroup vstIHost vst360 - [host imp] - [extends IBStream] - [released: 3.6.0] - [optional] Interface to access preset meta information from stream, for example used in setState\getState in order to inform the plug-in about the current context in which the preset loading\saving occurs (Project context or Preset load\save (see \ref StateType)) or used to get the full file path of the loaded preset (if available). \code{.cpp} //------------------------------------------------------------------------ #include "pluginterfaces/base/ustring.h" #include "pluginterfaces/vst/vstpresetkeys.h" ... tresult PLUGIN_API MyPlugin::setState (IBStream* state) { FUnknownPtr stream (state); if (stream) { if (IAttributeList* list = stream->getAttributes ()) { // get the current type (project/Default..) of this state String128 string; if (list->getString (PresetAttributes::kStateType, string, 128 * sizeof (TChar)) == kResultTrue) { UString128 tmp (string); char ascii[128]; tmp.toAscii (ascii, 128); if (strncmp (ascii, StateType::kProject, strlen (StateType::kProject)) == 0) { // we are in project loading context... } } // get the full file path of this state TChar fullPath[1024]; if (list->getString (PresetAttributes::kFilePathStringType, fullPath, 1024 * sizeof (TChar)) == kResultTrue) { // here we have the full path ... } } } //...read the state here..... return kResultTrue; } \endcode */ class IStreamAttributes : public FUnknown { public: //------------------------------------------------------------------------ /** Gets filename (without file extension) of the stream. */ virtual tresult PLUGIN_API getFileName (String128 name /*inout*/) = 0; /** Gets meta information list. */ virtual IAttributeList* PLUGIN_API getAttributes () = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IStreamAttributes, 0xD6CE2FFC, 0xEFAF4B8C, 0x9E74F1BB, 0x12DA44B4) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/vst/PaxHeaders/ivstnoteexpression.h0000644000000000000000000000013215124701711024174 xustar0030 mtime=1767080905.194211232 30 atime=1767080905.194211232 30 ctime=1767080905.194211232 qtractor-1.5.11/src/vst3/pluginterfaces/vst/ivstnoteexpression.h0000644000175000001440000003214515124701711024171 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/vst/ivstnoteexpression.h // Created by : Steinberg, 10/2010 // Description : VST Note Expression Interfaces // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/vst/vsttypes.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** \ingroup vst3typedef */ /**@{*/ /** Note Expression Types */ typedef uint32 NoteExpressionTypeID; /** Note Expression Value */ typedef double NoteExpressionValue; /**@}*/ //------------------------------------------------------------------------ /** NoteExpressionTypeIDs describes the type of the note expression. VST predefines some types like volume, pan, tuning by defining their ranges and curves. Used by NoteExpressionEvent::typeId and NoteExpressionTypeID::typeId \see NoteExpressionTypeInfo */ enum NoteExpressionTypeIDs : uint32 { kVolumeTypeID = 0, ///< Volume, plain range [0 = -oo , 0.25 = 0dB, 0.5 = +6dB, 1 = +12dB]: plain = 20 * log (4 * norm) kPanTypeID, ///< Panning (L-R), plain range [0 = left, 0.5 = center, 1 = right] kTuningTypeID, ///< Tuning, plain range [0 = -120.0 (ten octaves down), 0.5 none, 1 = +120.0 (ten octaves up)] ///< plain = 240 * (norm - 0.5) and norm = plain / 240 + 0.5 ///< oneOctave is 12.0 / 240.0; oneHalfTune = 1.0 / 240.0; kVibratoTypeID, ///< Vibrato kExpressionTypeID, ///< Expression kBrightnessTypeID, ///< Brightness kTextTypeID, ///< See NoteExpressionTextEvent kPhonemeTypeID, ///< TODO: kCustomStart = 100000, ///< start of custom note expression type ids kCustomEnd = 200000, ///< end of custom note expression type ids kInvalidTypeID = 0xFFFFFFFF ///< indicates an invalid note expression type }; //------------------------------------------------------------------------ /** Description of a Note Expression Type This structure is part of the NoteExpressionTypeInfo structure, it describes for given NoteExpressionTypeID its default value (for example 0.5 for a kTuningTypeID (kIsBipolar: centered)), its minimum and maximum (for predefined NoteExpressionTypeID the full range is predefined too) and a stepCount when the given NoteExpressionTypeID is limited to discrete values (like on/off state). \see NoteExpressionTypeInfo */ struct NoteExpressionValueDescription { NoteExpressionValue defaultValue; ///< default normalized value [0,1] NoteExpressionValue minimum; ///< minimum normalized value [0,1] NoteExpressionValue maximum; ///< maximum normalized value [0,1] int32 stepCount; ///< number of discrete steps (0: continuous, 1: toggle, discrete value otherwise - see \ref vst3ParameterIntro) }; #if SMTG_OS_WINDOWS && !SMTG_PLATFORM_64 #include "pluginterfaces/vst/vstpshpack4.h" #endif //------------------------------------------------------------------------ /** Note Expression Value event. Used in \ref Event (union) A note expression event affects one single playing note (referring its noteId). This kind of event is send from host to the plug-in like other events (NoteOnEvent, NoteOffEvent,...) in \ref ProcessData during the process call. Note expression events for a specific noteId can only occur after a NoteOnEvent. The host must take care that the event list (\ref IEventList) is properly sorted. Expression events are always absolute normalized values [0.0, 1.0]. The predefined types have a predefined mapping of the normalized values (see \ref NoteExpressionTypeIDs) \sa INoteExpressionController */ struct NoteExpressionValueEvent { NoteExpressionTypeID typeId; ///< see \ref NoteExpressionTypeID int32 noteId; ///< associated note identifier to apply the change NoteExpressionValue value; ///< normalized value [0.0, 1.0]. }; //------------------------------------------------------------------------ /** Note Expression Int event. Used in \ref Event (union) Same as NoteExpressionValueEvent but use a uint64 instead of a NoteExpressionValue (double) \ingroup vstIPlug vst380 - [released: 3.8.0] */ struct NoteExpressionIntValueEvent { NoteExpressionTypeID typeId; ///< see \ref NoteExpressionTypeID int32 noteId; ///< associated note identifier to apply the change uint64 value; }; //------------------------------------------------------------------------ /** Note Expression Text event. Used in Event (union) A Expression event affects one single playing note. \sa INoteExpressionController \see NoteExpressionTypeInfo */ struct NoteExpressionTextEvent { NoteExpressionTypeID typeId; ///< see \ref NoteExpressionTypeID (kTextTypeID or kPhoneticTypeID) int32 noteId; ///< associated note identifier to apply the change uint32 textLen; ///< the number of characters (TChar) between the beginning of text and the terminating ///< null character (without including the terminating null character itself) const TChar* text; ///< UTF-16, null terminated }; #if SMTG_OS_WINDOWS && !SMTG_PLATFORM_64 #include "pluginterfaces/base/falignpop.h" #endif //------------------------------------------------------------------------ /** NoteExpressionTypeInfo is the structure describing a note expression supported by the plug-in. This structure is used by the method \ref INoteExpressionController::getNoteExpressionInfo. \see INoteExpressionController */ struct NoteExpressionTypeInfo { NoteExpressionTypeID typeId; ///< unique identifier of this note Expression type String128 title; ///< note Expression type title (e.g. "Volume") String128 shortTitle; ///< note Expression type short title (e.g. "Vol") String128 units; ///< note Expression type unit (e.g. "dB") int32 unitId; ///< id of unit this NoteExpression belongs to (see \ref vst3Units), in order to sort the note expression, it is possible to use unitId like for parameters. -1 means no unit used. NoteExpressionValueDescription valueDesc; ///< value description see \ref NoteExpressionValueDescription ParamID associatedParameterId; ///< optional associated parameter ID (for mapping from note expression to global (using the parameter automation for example) and back). Only used when kAssociatedParameterIDValid is set in flags. int32 flags; ///< NoteExpressionTypeFlags (see below) enum NoteExpressionTypeFlags { kIsBipolar = 1 << 0, ///< event is bipolar (centered), otherwise unipolar kIsOneShot = 1 << 1, ///< event occurs only one time for its associated note (at begin of the noteOn) kIsAbsolute = 1 << 2, ///< This note expression will apply an absolute change to the sound (not relative (offset)) kAssociatedParameterIDValid = 1 << 3,///< indicates that the associatedParameterID is valid and could be used }; }; //------------------------------------------------------------------------ /** Extended plug-in interface IEditController for note expression event support: Vst::INoteExpressionController \ingroup vstIPlug vst350 - [plug imp] - [extends IEditController] - [released: 3.5.0] - [optional] With this plug-in interface, the host can retrieve all necessary note expression information supported by the plug-in. Note expression information (\ref NoteExpressionTypeInfo) are specific for given channel and event bus. Note that there is only one NoteExpressionTypeID per given channel of an event bus. The method getNoteExpressionStringByValue allows conversion from a normalized value to a string representation and the getNoteExpressionValueByString method from a string to a normalized value. When the note expression state changes (for example when switching presets) the plug-in needs to inform the host about it via \ref IComponentHandler::restartComponent (kNoteExpressionChanged). */ class INoteExpressionController : public FUnknown { public: /** Returns number of supported note change types for event bus index and channel. * \note [UI-thread & Connected] */ virtual int32 PLUGIN_API getNoteExpressionCount (int32 busIndex /*in*/, int16 channel /*in*/) = 0; /** Returns note change type info. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getNoteExpressionInfo (int32 busIndex /*in*/, int16 channel /*in*/, int32 noteExpressionIndex /*in*/, NoteExpressionTypeInfo& info /*out*/) = 0; /** Gets a user readable representation of the normalized note change value. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getNoteExpressionStringByValue ( int32 busIndex /*in*/, int16 channel /*in*/, NoteExpressionTypeID id /*in*/, NoteExpressionValue valueNormalized /*in*/, String128 string /*out*/) = 0; /** Converts the user readable representation to the normalized note change value. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getNoteExpressionValueByString ( int32 busIndex /*in*/, int16 channel /*in*/, NoteExpressionTypeID id /*in*/, const TChar* string /*in*/, NoteExpressionValue& valueNormalized /*out*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (INoteExpressionController, 0xB7F8F859, 0x41234872, 0x91169581, 0x4F3721A3) //------------------------------------------------------------------------ //------------------------------------------------------------------------ /** KeyswitchTypeIDs describes the type of a key switch \see KeyswitchInfo */ enum KeyswitchTypeIDs : uint32 { kNoteOnKeyswitchTypeID = 0, ///< press before noteOn is played kOnTheFlyKeyswitchTypeID, ///< press while noteOn is played kOnReleaseKeyswitchTypeID, ///< press before entering release kKeyRangeTypeID ///< key should be maintained pressed for playing }; /** \ingroup vst3typedef */ typedef uint32 KeyswitchTypeID; //------------------------------------------------------------------------ /** KeyswitchInfo is the structure describing a key switch This structure is used by the method \ref IKeyswitchController::getKeyswitchInfo. \see IKeyswitchController */ struct KeyswitchInfo { KeyswitchTypeID typeId; ///< see KeyswitchTypeID String128 title; ///< name of key switch (e.g. "Accentuation") String128 shortTitle; ///< short title (e.g. "Acc") int32 keyswitchMin; ///< associated main key switch min (value between [0, 127]) int32 keyswitchMax; ///< associated main key switch max (value between [0, 127]) int32 keyRemapped; /** optional remapped key switch (default -1), the plug-in could provide one remapped key for a key switch (allowing better location on the keyboard of the key switches) */ int32 unitId; ///< id of unit this key switch belongs to (see \ref vst3Units), -1 means no unit used. int32 flags; ///< not yet used (set to 0) }; //------------------------------------------------------------------------ /** Extended plug-in interface IEditController for key switches support: Vst::IKeyswitchController \ingroup vstIPlug vst350 - [plug imp] - [extends IEditController] - [released: 3.5.0] - [optional] When a (instrument) plug-in supports such interface, the host could get from the plug-in the current set of used key switches (megatrig/articulation) for a given channel of a event bus and then automatically use them (like in Cubase 6) to create VST Expression Map (allowing to associated symbol to a given articulation / key switch). */ class IKeyswitchController : public FUnknown { public: /** Returns number of supported key switches for event bus index and channel. * \note [UI-thread & Connected] */ virtual int32 PLUGIN_API getKeyswitchCount (int32 busIndex /*in*/, int16 channel /*in*/) = 0; /** Returns key switch info. * \note [UI-thread & Connected] */ virtual tresult PLUGIN_API getKeyswitchInfo (int32 busIndex /*in*/, int16 channel /*in*/, int32 keySwitchIndex /*in*/, KeyswitchInfo& info /*out*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IKeyswitchController, 0x1F2F76D3, 0xBFFB4B96, 0xB99527A5, 0x5EBCCEF4) //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/PaxHeaders/README.md0000644000000000000000000000013215124701711020473 xustar0030 mtime=1767080905.191211245 30 atime=1767080905.191211245 30 ctime=1767080905.191211245 qtractor-1.5.11/src/vst3/pluginterfaces/README.md0000644000175000001440000000053215124701711020463 0ustar00rncbcusers# Welcome to VST 3 SDK Interfaces Here are located all **VST 3** interfaces definitions (including VST Component/Controller, UI, Test). ## License & Usage guidelines More details are found at [www.steinberg.net/sdklicenses_vst3](http://www.steinberg.net/sdklicenses_vst3) ---- Return to [VST 3 SDK](https://github.com/steinbergmedia/vst3sdk) qtractor-1.5.11/src/vst3/pluginterfaces/PaxHeaders/gui0000644000000000000000000000013015124701711017721 xustar0029 mtime=1767080905.19378968 30 atime=1767080905.193211236 29 ctime=1767080905.19378968 qtractor-1.5.11/src/vst3/pluginterfaces/gui/0000755000175000001440000000000015124701711017770 5ustar00rncbcusersqtractor-1.5.11/src/vst3/pluginterfaces/gui/PaxHeaders/iwaylandframe.h0000644000000000000000000000012715124701711023000 xustar0029 mtime=1767080905.19378968 29 atime=1767080905.19378968 29 ctime=1767080905.19378968 qtractor-1.5.11/src/vst3/pluginterfaces/gui/iwaylandframe.h0000644000175000001440000001321615124701711022767 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : VST SDK // // Category : Interfaces // Filename : pluginterfaces/gui/iwaylandframe.h // Created by : Steinberg, 10/2025 // Originally written and contributed to VST SDK by PreSonus Software Ltd. // Description : Wayland Support. // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ struct wl_display; struct wl_surface; struct xdg_surface; struct xdg_toplevel; namespace Steinberg { struct ViewRect; /** \defgroup waylandFrame Wayland Frame * * The following interfaces allow querying information about the host plug-in frame when running in * a Wayland session. * * A native Wayland host application acts as both a Wayland client and a Wayland compositor. * The host application connects to the system compositor and creates application windows etc. using * this compositor connection.\n * A plug-in does not connect to the system compositor, but connects to the host application by * calling IWaylandHost::openWaylandConnection().\n * The IWaylandHost interface can be created via IHostApplication::createInstance. As the interface * may be required early, the host should pass IHostApplication to the plug-in using * IPluginFactory3::setHostContext.\n * When opening a plug-in window, the host calls IPlugView::attached() with the parent pointer set * to the wl_surface of the parent frame (with an unknown surface role). * The plug-in creates a wl_surface and must assign the wl_subsurface role using the given parent * pointer.\n * The plug-in is responsible for resizing the subsurface accordingly.\n * In order to create additional windows (dialogs, menus, tooltips etc.), the plug-in can use the * IWaylandFrame interface, which is implemented by the host's IPlugFrame object.\n * The plug-in can use IWaylandFrame::getParentSurface() to query an xdg_surface, which can in turn * be used as a parent in xdg_surface_get_popup. Likewise, the plug-in can use * IWaylandFrame::getParentToplevel() to query an xdg_toplevel, which can be used in * xdg_toplevel_set_parent. */ //------------------------------------------------------------------------ /** IWaylandHost: Wayland host interface Implemented as a singleton in the host application. Created via IHostApplication::createInstance. \ingroup waylandFrame pluginGUI vst380 - [host imp] - [released: 3.8.0] */ class IWaylandHost : public FUnknown { public: //------------------------------------------------------------------------ /** Open a Wayland connection to the host. * \note [UI-thread & Initialized] */ virtual wl_display* PLUGIN_API openWaylandConnection () = 0; /** Close a previously created connection. * \note [UI-thread & Initialized] */ virtual tresult PLUGIN_API closeWaylandConnection (wl_display* display) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IWaylandHost, 0x5E9582EE, 0x86594652, 0xB213678E, 0x7F1A705E) //------------------------------------------------------------------------ /** IWaylandFrame interface Interface to query additional information about the host plug-in frame in a Wayland session. To be implemented by the VST3 IPlugFrame class. \ingroup waylandFrame pluginGUI vst380 - [host imp] - [released: 3.8.0] \see IPlugFrame */ class IWaylandFrame : public FUnknown { public: //------------------------------------------------------------------------ /** Get the parent Wayland surface. * The plug-in must not change the state of the parent surface. * \note [UI-thread & plugView] */ virtual wl_surface* PLUGIN_API getWaylandSurface (wl_display* display) = 0; /** Get the parent XDG surface for creating popup windows. * If the parent surface is not an xdg_surface, * this returns the first xdg_surface that can be found in the surface hierarchy, * starting the search with the parent surface. * The plug-in must not change the state of the parent surface. * The size and position of the parent surface, relative to the top left corner of * the plug-in surface, is returned in parentSize. * \note [UI-thread & plugView] */ virtual xdg_surface* PLUGIN_API getParentSurface (ViewRect& parentSize, wl_display* display) = 0; /** Get the XDG toplevel surface containing the plug-in frame. * The plug-in must not change the state of the returned xdg_toplevel. * \note [UI-thread & plugView] */ virtual xdg_toplevel* PLUGIN_API getParentToplevel (wl_display* display) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IWaylandFrame, 0x809FAEC6, 0x231C4FFA, 0x98ED046C, 0x6E9E2003) //------------------------------------------------------------------------ } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/gui/PaxHeaders/iplugviewcontentscalesupport.h0000644000000000000000000000013015124701711026222 xustar0029 mtime=1767080905.19378968 30 atime=1767080905.193211236 29 ctime=1767080905.19378968 qtractor-1.5.11/src/vst3/pluginterfaces/gui/iplugviewcontentscalesupport.h0000644000175000001440000000671315124701711026223 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK GUI Interfaces // Filename : pluginterfaces/gui/iplugviewcontentscalesupport.h // Created by : Steinberg, 06/2016 // Description : Plug-in User Interface Scaling // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpush.h" //------------------------------------------------------------------------ //------------------------------------------------------------------------ namespace Steinberg { //------------------------------------------------------------------------ /** Plug-in view content scale support \ingroup pluginGUI vstIPlug vst366 - [plug impl] - [extends IPlugView] - [released: 3.6.6] - [optional] This interface communicates the content scale factor from the host to the plug-in view on systems where plug-ins cannot get this information directly like Microsoft Windows. The host calls setContentScaleFactor directly before or after the plug-in view is attached and when the scale factor changes while the view is attached (system change or window moved to another screen with different scaling settings). The host may call setContentScaleFactor in a different context, for example: scaling the plug-in editor for better readability. When a plug-in handles this (by returning kResultTrue), it needs to scale the width and height of its view by the scale factor and inform the host via a IPlugFrame::resizeView (). The host will then call IPlugView::onSize (). Note that the host is allowed to call setContentScaleFactor() at any time the IPlugView is valid. If this happens before the IPlugFrame object is set on your view, make sure that when the host calls IPlugView::getSize() afterwards you return the size of your view for that new scale factor. It is recommended to implement this interface on Microsoft Windows to let the host know that the plug-in is able to render in different scalings. */ class IPlugViewContentScaleSupport : public FUnknown { public: //------------------------------------------------------------------------ /** \ingroup smtgtypedef */ typedef float ScaleFactor; /** Set the Content Scale Factor * @param factor the scale factor requested by the host * @return kResultTrue when a plug-in handles this * \note [UI-thread] */ virtual tresult PLUGIN_API setContentScaleFactor (ScaleFactor factor /*in*/) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IPlugViewContentScaleSupport, 0x65ED9690, 0x8AC44525, 0x8AADEF7A, 0x72EA703F) //------------------------------------------------------------------------ } // namespace Steinberg //------------------------------------------------------------------------ #include "pluginterfaces/base/falignpop.h" //------------------------------------------------------------------------ qtractor-1.5.11/src/vst3/pluginterfaces/gui/PaxHeaders/iplugview.h0000644000000000000000000000013215124701711022164 xustar0030 mtime=1767080905.193211236 30 atime=1767080905.193211236 30 ctime=1767080905.193211236 qtractor-1.5.11/src/vst3/pluginterfaces/gui/iplugview.h0000644000175000001440000003043715124701711022163 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK GUI Interfaces // Filename : pluginterfaces/gui/iplugview.h // Created by : Steinberg, 12/2007 // Description : Plug-in User Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/base/typesizecheck.h" namespace Steinberg { class IPlugFrame; //------------------------------------------------------------------------ /*! \defgroup pluginGUI Graphical User Interface */ //------------------------------------------------------------------------ /** Graphical rectangle structure. Used with IPlugView. * \ingroup pluginGUI */ struct ViewRect { ViewRect (int32 l = 0, int32 t = 0, int32 r = 0, int32 b = 0) : left (l), top (t), right (r), bottom (b) { } int32 left; int32 top; int32 right; int32 bottom; //--- --------------------------------------------------------------------- int32 getWidth () const { return right - left; } int32 getHeight () const { return bottom - top; } }; SMTG_TYPE_SIZE_CHECK (ViewRect, 16, 16, 16, 16) //------------------------------------------------------------------------ /** \defgroup platformUIType Platform UI Types * \ingroup pluginGUI * List of Platform UI types for IPlugView. This list is used to match the GUI-System between * the host and a plug-in in case that an OS provides multiple GUI-APIs. */ /**@{*/ /** The parent parameter in IPlugView::attached() is a HWND handle. * You should attach a child window to it. */ const FIDString kPlatformTypeHWND = "HWND"; ///< HWND handle. (Microsoft Windows) /** The parent parameter in IPlugView::attached() is a WindowRef. * You should attach a HIViewRef to the content view of the window. */ const FIDString kPlatformTypeHIView = "HIView"; ///< HIViewRef. (Mac OS X) /** The parent parameter in IPlugView::attached() is a NSView pointer. * You should attach a NSView to it. */ const FIDString kPlatformTypeNSView = "NSView"; ///< NSView pointer. (Mac OS X) /** The parent parameter in IPlugView::attached() is a UIView pointer. * You should attach an UIView to it. */ const FIDString kPlatformTypeUIView = "UIView"; ///< UIView pointer. (iOS) /** The parent parameter in IPlugView::attached() is a X11 Window supporting XEmbed. * You should attach a Window to it that supports the XEmbed extension. * See https://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html */ const FIDString kPlatformTypeX11EmbedWindowID = "X11EmbedWindowID"; ///< X11 Window ID. (X11) /** The parent parameter in IPlugView::attached() is a wl_surface pointer. * The plug-in should create a wl_surface and attach it to the parent surface as a wl_subsurface. * The plug-in should not connect to the system compositor to do so, but use * IWaylandHost::openWaylandConnection(). * See https://wayland.freedesktop.org/docs/html */ const FIDString kPlatformTypeWaylandSurfaceID = "WaylandSurfaceID"; ///< Wayland Surface ID. /**@}*/ //------------------------------------------------------------------------ //------------------------------------------------------------------------ /** Plug-in definition of a view. \ingroup pluginGUI vstIPlug vst300 - [plug imp] - [released: 3.0.0] \par Coordinates The coordinates utilized within the ViewRect are native to the view system of the parent type. This implies that on macOS (kPlatformTypeNSView), the coordinates are expressed in logical units (independent of the screen scale factor), whereas on Windows (kPlatformTypeHWND) and Linux (kPlatformTypeX11EmbedWindowID), the coordinates are expressed in physical units (pixels). \par Sizing of a view Usually, the size of a plug-in view is fixed. But both the host and the plug-in can cause a view to be resized: \n - \b Host: If IPlugView::canResize () returns kResultTrue the host will set up the window so that the user can resize it. While the user resizes the window, IPlugView::checkSizeConstraint () is called, allowing the plug-in to change the size to a valid a valid supported rectangle size. The host then resizes the window to this rect and has to call IPlugView::onSize (). \n \n - \b Plug-in: The plug-in can call IPlugFrame::resizeView () and cause the host to resize the window.\n\n Afterwards, in the same callstack, the host has to call IPlugView::onSize () if a resize is needed (size was changed). Note that if the host calls IPlugView::getSize () before calling IPlugView::onSize () (if needed), it will get the current (old) size not the wanted one!!\n Here the calling sequence:\n - plug-in->host: IPlugFrame::resizeView (newSize) - host->plug-in (optional): IPlugView::getSize () returns the currentSize (not the newSize!) - host->plug-in: if newSize is different from the current size: IPlugView::onSize (newSize) - host->plug-in (optional): IPlugView::getSize () returns the newSize \n Please only resize the platform representation of the view when IPlugView::onSize () is called. \par Keyboard handling The plug-in view receives keyboard events from the host. A view implementation must not handle keyboard events by the means of platform callbacks, but let the host pass them to the view. The host depends on a proper return value when IPlugView::onKeyDown is called, otherwise the plug-in view may cause a malfunction of the host's key command handling. \see IPlugFrame, \ref platformUIType */ class IPlugView : public FUnknown { public: //------------------------------------------------------------------------ /** Is Platform UI Type supported \param type : IDString of \ref platformUIType */ virtual tresult PLUGIN_API isPlatformTypeSupported (FIDString type) = 0; /** The parent window of the view has been created, the (platform) representation of the view should now be created as well. Note that the parent is owned by the caller and you are not allowed to alter it in any way other than adding your own views. Note that in this call the plug-in could call a IPlugFrame::resizeView ()! \param parent : platform handle of the parent window or view \param type : \ref platformUIType which should be created */ virtual tresult PLUGIN_API attached (void* parent, FIDString type) = 0; /** The parent window of the view is about to be destroyed. You have to remove all your own views from the parent window or view. */ virtual tresult PLUGIN_API removed () = 0; /** Handling of mouse wheel. */ virtual tresult PLUGIN_API onWheel (float distance) = 0; /** Handling of keyboard events : Key Down. \param key : unicode code of key \param keyCode : virtual keycode for non ascii keys - see \ref VirtualKeyCodes in keycodes.h \param modifiers : any combination of modifiers - see \ref KeyModifier in keycodes.h \return kResultTrue if the key is handled, otherwise kResultFalse. \n Please note that kResultTrue must only be returned if the key has really been handled. Otherwise key command handling of the host might be blocked! */ virtual tresult PLUGIN_API onKeyDown (char16 key, int16 keyCode, int16 modifiers) = 0; /** Handling of keyboard events : Key Up. \param key : unicode code of key \param keyCode : virtual keycode for non ascii keys - see \ref VirtualKeyCodes in keycodes.h \param modifiers : any combination of KeyModifier - see \ref KeyModifier in keycodes.h \return kResultTrue if the key is handled, otherwise return kResultFalse. */ virtual tresult PLUGIN_API onKeyUp (char16 key, int16 keyCode, int16 modifiers) = 0; /** Returns the size of the platform representation of the view. */ virtual tresult PLUGIN_API getSize (ViewRect* size) = 0; /** Resizes the platform representation of the view to the given rect. Note that if the plug-in * requests a resize (IPlugFrame::resizeView ()) onSize has to be called afterward. */ virtual tresult PLUGIN_API onSize (ViewRect* newSize) = 0; /** Focus changed message. */ virtual tresult PLUGIN_API onFocus (TBool state) = 0; /** Sets IPlugFrame object to allow the plug-in to inform the host about resizing. */ virtual tresult PLUGIN_API setFrame (IPlugFrame* frame) = 0; /** Is view sizable by user. */ virtual tresult PLUGIN_API canResize () = 0; /** On live resize this is called to check if the view can be resized to the given rect, if not * adjust the rect to the allowed size. */ virtual tresult PLUGIN_API checkSizeConstraint (ViewRect* rect) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IPlugView, 0x5BC32507, 0xD06049EA, 0xA6151B52, 0x2B755B29) //------------------------------------------------------------------------ /** Callback interface passed to IPlugView. \ingroup pluginGUI vstIHost vst300 - [host imp] - [released: 3.0.0] - [mandatory] Enables a plug-in to resize the view and cause the host to resize the window. */ class IPlugFrame : public FUnknown { public: //------------------------------------------------------------------------ /** Called to inform the host about the resize of a given view. * Afterwards the host has to call IPlugView::onSize (). */ virtual tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IPlugFrame, 0x367FAF01, 0xAFA94693, 0x8D4DA2A0, 0xED0882A3) //------------------------------------------------------------------------ namespace Linux { using TimerInterval = uint64; using FileDescriptor = int; //------------------------------------------------------------------------ /** Linux event handler interface \ingroup pluginGUI vst368 - [plug imp] - [released: 3.6.8] \see IRunLoop */ class IEventHandler : public FUnknown { public: virtual void PLUGIN_API onFDIsSet (FileDescriptor fd) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IEventHandler, 0x561E65C9, 0x13A0496F, 0x813A2C35, 0x654D7983) //------------------------------------------------------------------------ /** Linux timer handler interface \ingroup pluginGUI vst368 - [plug imp] - [released: 3.6.8] \see IRunLoop */ class ITimerHandler : public FUnknown { public: virtual void PLUGIN_API onTimer () = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (ITimerHandler, 0x10BDD94F, 0x41424774, 0x821FAD8F, 0xECA72CA9) //------------------------------------------------------------------------ /** Linux host run loop interface \ingroup pluginGUI vst368 - [host imp] - [extends IPlugFrame] - [released: 3.6.8] On Linux the host has to provide this interface to the plug-in as there's no global event run loop defined as on other platforms. This can be done by IPlugFrame and the context which is passed to the plug-in as an argument in the method IPlugFactory3::setHostContext. This way the plug-in can get a runloop even if it does not have an editor. A plug-in can register an event handler for a file descriptor. The host has to call the event handler when the file descriptor is marked readable. A plug-in also can register a timer which will be called repeatedly until it is unregistered. */ class IRunLoop : public FUnknown { public: virtual tresult PLUGIN_API registerEventHandler (IEventHandler* handler, FileDescriptor fd) = 0; virtual tresult PLUGIN_API unregisterEventHandler (IEventHandler* handler) = 0; virtual tresult PLUGIN_API registerTimer (ITimerHandler* handler, TimerInterval milliseconds) = 0; virtual tresult PLUGIN_API unregisterTimer (ITimerHandler* handler) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IRunLoop, 0x18C35366, 0x97764F1A, 0x9C5B8385, 0x7A871389) //------------------------------------------------------------------------ } // namespace Linux } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/PaxHeaders/base0000644000000000000000000000013215124701711020051 xustar0030 mtime=1767080905.193211236 30 atime=1767080905.191211245 30 ctime=1767080905.193211236 qtractor-1.5.11/src/vst3/pluginterfaces/base/0000755000175000001440000000000015124701711020116 5ustar00rncbcusersqtractor-1.5.11/src/vst3/pluginterfaces/base/PaxHeaders/fstrdefs.h0000644000000000000000000000013215124701711022117 xustar0030 mtime=1767080905.192335529 30 atime=1767080905.192335529 30 ctime=1767080905.192335529 qtractor-1.5.11/src/vst3/pluginterfaces/base/fstrdefs.h0000644000175000001440000002246115124701711022114 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK Core Interfaces // Filename : pluginterfaces/base/fstrdefs.h // Created by : Steinberg, 01/2004 // Description : Definitions for handling strings (Unicode / ASCII / Platforms) // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "ftypes.h" //---------------------------------------------------------------------------- /** string methods defines unicode / ASCII */ //---------------------------------------------------------------------------- // 16 bit string operations #if SMTG_CPP11 // if c++11 unicode string literals #define SMTG_CPP11_CAT_PRIVATE_DONT_USE(a,b) a ## b #define STR16(x) SMTG_CPP11_CAT_PRIVATE_DONT_USE(u,x) #else #include "conststringtable.h" #define STR16(x) Steinberg::ConstStringTable::instance ()->getString (x) #endif #ifdef UNICODE #define STR(x) STR16(x) #define tStrBufferSize(buffer) (sizeof(buffer)/sizeof(Steinberg::tchar)) #else #define STR(x) x #define tStrBufferSize(buffer) (sizeof(buffer)) #endif #define str8BufferSize(buffer) (sizeof(buffer)/sizeof(Steinberg::char8)) #define str16BufferSize(buffer) (sizeof(buffer)/sizeof(Steinberg::char16)) #if SMTG_OS_WINDOWS #define FORMAT_INT64A "I64d" #define FORMAT_UINT64A "I64u" #elif SMTG_OS_MACOS || SMTG_OS_LINUX #define FORMAT_INT64A "lld" #define FORMAT_UINT64A "llu" #define stricmp strcasecmp #define strnicmp strncasecmp #endif #ifdef UNICODE #define FORMAT_INT64W STR(FORMAT_INT64A) #define FORMAT_UINT64W STR(FORMAT_UINT64A) #define FORMAT_INT64 FORMAT_INT64W #define FORMAT_UINT64 FORMAT_UINT64W #else #define FORMAT_INT64 FORMAT_INT64A #define FORMAT_UINT64 FORMAT_UINT64A #endif //---------------------------------------------------------------------------- // newline //---------------------------------------------------------------------------- #if SMTG_OS_WINDOWS #define ENDLINE_A "\r\n" #define ENDLINE_W STR ("\r\n") #elif SMTG_OS_MACOS #define ENDLINE_A "\r" #define ENDLINE_W STR ("\r") #elif SMTG_OS_LINUX #define ENDLINE_A "\n" #define ENDLINE_W STR ("\n") #endif #ifdef UNICODE #define ENDLINE ENDLINE_W #else #define ENDLINE ENDLINE_A #endif #if SMTG_OS_WINDOWS && !defined(__GNUC__) && defined(_MSC_VER) #define stricmp _stricmp #define strnicmp _strnicmp #if (_MSC_VER < 1900) #define snprintf _snprintf #endif #endif namespace Steinberg { //---------------------------------------------------------------------------- static SMTG_CONSTEXPR const tchar kEmptyString[] = { 0 }; static SMTG_CONSTEXPR const char8 kEmptyString8[] = { 0 }; static SMTG_CONSTEXPR const char16 kEmptyString16[] = { 0 }; #ifdef UNICODE static SMTG_CONSTEXPR const tchar kInfiniteSymbol[] = { 0x221E, 0 }; #else static SMTG_CONSTEXPR const tchar* const kInfiniteSymbol = STR ("oo"); #endif //---------------------------------------------------------------------------- template inline SMTG_CONSTEXPR14 int32 _tstrlen (const T* wcs) { const T* eos = wcs; while (*eos++) ; return (int32) (eos - wcs - 1); } inline SMTG_CONSTEXPR14 int32 tstrlen (const tchar* str) {return _tstrlen (str);} inline SMTG_CONSTEXPR14 int32 strlen8 (const char8* str) {return _tstrlen (str);} inline SMTG_CONSTEXPR14 int32 strlen16 (const char16* str) {return _tstrlen (str);} //---------------------------------------------------------------------------- template inline SMTG_CONSTEXPR14 int32 _tstrcmp (const T* src, const T* dst) { while (*src == *dst && *dst) { src++; dst++; } if (*src == 0 && *dst == 0) return 0; if (*src == 0) return -1; if (*dst == 0) return 1; return (int32) (*src - *dst); } inline SMTG_CONSTEXPR14 int32 tstrcmp (const tchar* src, const tchar* dst) {return _tstrcmp (src, dst);} inline SMTG_CONSTEXPR14 int32 strcmp8 (const char8* src, const char8* dst) {return _tstrcmp (src, dst);} inline SMTG_CONSTEXPR14 int32 strcmp16 (const char16* src, const char16* dst) {return _tstrcmp (src, dst);} template inline SMTG_CONSTEXPR14 int32 strcmpT (const T* first, const T* last); template <> inline SMTG_CONSTEXPR14 int32 strcmpT (const char8* first, const char8* last) { return _tstrcmp (first, last); } template <> inline SMTG_CONSTEXPR14 int32 strcmpT (const char16* first, const char16* last) { return _tstrcmp (first, last); } //---------------------------------------------------------------------------- template inline SMTG_CONSTEXPR14 int32 _tstrncmp (const T* first, const T* last, uint32 count) { if (count == 0) return 0; while (--count && *first && *first == *last) { first++; last++; } if (*first == 0 && *last == 0) return 0; if (*first == 0) return -1; if (*last == 0) return 1; return (int32) (*first - *last); } inline SMTG_CONSTEXPR14 int32 tstrncmp (const tchar* first, const tchar* last, uint32 count) {return _tstrncmp (first, last, count);} inline SMTG_CONSTEXPR14 int32 strncmp8 (const char8* first, const char8* last, uint32 count) {return _tstrncmp (first, last, count);} inline SMTG_CONSTEXPR14 int32 strncmp16 (const char16* first, const char16* last, uint32 count) {return _tstrncmp (first, last, count);} template inline SMTG_CONSTEXPR14 int32 strncmpT (const T* first, const T* last, uint32 count); template <> inline SMTG_CONSTEXPR14 int32 strncmpT (const char8* first, const char8* last, uint32 count) { return _tstrncmp (first, last, count); } template <> inline SMTG_CONSTEXPR14 int32 strncmpT (const char16* first, const char16* last, uint32 count) {return _tstrncmp (first, last, count); } //---------------------------------------------------------------------------- template inline SMTG_CONSTEXPR14 T* _tstrcpy (T* dst, const T* src) { T* cp = dst; while ((*cp++ = *src++) != 0) // copy string ; return dst; } inline SMTG_CONSTEXPR14 tchar* tstrcpy (tchar* dst, const tchar* src) {return _tstrcpy (dst, src);} inline SMTG_CONSTEXPR14 char8* strcpy8 (char8* dst, const char8* src) {return _tstrcpy (dst, src);} inline SMTG_CONSTEXPR14 char16* strcpy16 (char16* dst, const char16* src) {return _tstrcpy (dst, src);} //---------------------------------------------------------------------------- template inline SMTG_CONSTEXPR14 T* _tstrncpy (T* dest, const T* source, uint32 count) { T* start = dest; while (count && (*dest++ = *source++) != 0) // copy string count--; if (count) // pad out with zeros { while (--count) *dest++ = 0; } return start; } inline SMTG_CONSTEXPR14 tchar* tstrncpy (tchar* dest, const tchar* source, uint32 count) {return _tstrncpy (dest, source, count);} inline SMTG_CONSTEXPR14 char8* strncpy8 (char8* dest, const char8* source, uint32 count) {return _tstrncpy (dest, source, count);} inline SMTG_CONSTEXPR14 char16* strncpy16 (char16* dest, const char16* source, uint32 count) {return _tstrncpy (dest, source, count);} //---------------------------------------------------------------------------- template inline SMTG_CONSTEXPR14 T* _tstrcat (T* dst, const T* src) { T* cp = dst; while (*cp) cp++; // find end of dst while ((*cp++ = *src++) != 0) // Copy src to end of dst ; return dst; } inline SMTG_CONSTEXPR14 tchar* tstrcat (tchar* dst, const tchar* src) {return _tstrcat (dst, src); } inline SMTG_CONSTEXPR14 char8* strcat8 (char8* dst, const char8* src) {return _tstrcat (dst, src); } inline SMTG_CONSTEXPR14 char16* strcat16 (char16* dst, const char16* src) {return _tstrcat (dst, src); } //---------------------------------------------------------------------------- inline SMTG_CONSTEXPR14 void str8ToStr16 (char16* dst, const char8* src, int32 n = -1) { int32 i = 0; for (;;) { if (i == (n - 1)) { dst[i] = 0; return; } #if BYTEORDER == kBigEndian char8* pChr = (char8*)&dst[i]; pChr[0] = 0; pChr[1] = src[i]; #else dst[i] = static_cast (src[i]); #endif if (src[i] == 0) break; i++; } while (n > i) { dst[i] = 0; i++; } } //------------------------------------------------------------------------ inline SMTG_CONSTEXPR14 bool FIDStringsEqual (FIDString id1, FIDString id2) { return (id1 && id2) ? (strcmp8 (id1, id2) == 0) : false; } static SMTG_CONSTEXPR const uint32 kPrintfBufferSize = 4096; #if SMTG_OS_WINDOWS /* cast between wchar_t and char16 */ inline wchar_t* wscast (char16* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast (s); } inline char16* wscast (wchar_t* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast (s);} inline const wchar_t* wscast (const char16* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast (s); } inline const char16* wscast (const wchar_t* s) { static_assert (sizeof (wchar_t) == sizeof (char16), ""); return reinterpret_cast (s); } #endif //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/base/PaxHeaders/conststringtable.cpp0000644000000000000000000000013215124701711024217 xustar0030 mtime=1767080905.191211245 30 atime=1767080905.191211245 30 ctime=1767080905.191211245 qtractor-1.5.11/src/vst3/pluginterfaces/base/conststringtable.cpp0000644000175000001440000000615615124701711024217 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK Core Interfaces // Filename : pluginterfaces/base/conststringtable.cpp // Created by : Steinberg, 09/2007 // Description : constant unicode string table // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #include "conststringtable.h" #include #include namespace Steinberg { static std::map* stringMap; static std::map* charMap; static char16* generateUTF16 (const char8* str); //---------------------------------------------------------------------------- ConstStringTable* ConstStringTable::instance () { static ConstStringTable stringTable; return &stringTable; } //---------------------------------------------------------------------------- const char16* ConstStringTable::getString (const char8* str) const { std::map::iterator iter = stringMap->find (str); if (iter != stringMap->end ()) return iter->second; char16* uStr = generateUTF16 (str); stringMap->insert (std::make_pair (str, uStr)); return uStr; } //---------------------------------------------------------------------------- char16 ConstStringTable::getString (const char8 str) const { std::map::iterator iter = charMap->find (str); if (iter != charMap->end ()) return iter->second; char16 uStr = 0; #if BYTEORDER == kBigEndian char8* puStr = (char8*)&uStr; puStr[1] = str; #else uStr = str; #endif charMap->insert (std::make_pair (str, uStr)); return uStr; } //---------------------------------------------------------------------------- ConstStringTable::ConstStringTable () { stringMap = new std::map; charMap = new std::map; } //---------------------------------------------------------------------------- ConstStringTable::~ConstStringTable () { // free out allocated strings { std::map::iterator iter = stringMap->begin (); while (iter != stringMap->end ()) { delete[] iter->second; iter++; } } // delete iterator on map before deleting the map delete stringMap; delete charMap; } //---------------------------------------------------------------------------- char16* generateUTF16 (const char8* str) { int32 len = (int32)strlen (str); char16* result = new char16[len + 1]; for (int32 i = 0; i < len; i++) { #if BYTEORDER == kBigEndian char8* pChr = (char8*)&result[i]; pChr[0] = 0; pChr[1] = str[i]; #else result[i] = str[i]; #endif } result[len] = 0; return result; } //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/base/PaxHeaders/falignpush.h0000644000000000000000000000013215124701711022437 xustar0030 mtime=1767080905.192335529 30 atime=1767080905.192335529 30 ctime=1767080905.192335529 qtractor-1.5.11/src/vst3/pluginterfaces/base/falignpush.h0000644000175000001440000000314115124701711022426 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK Core Interfaces // Filename : pluginterfaces/base/falignpush.h // Created by : Steinberg, 01/2004 // Description : Set alignment settings // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------- #if SMTG_OS_MACOS #pragma GCC diagnostic ignored "-Wunknown-warning-option" #pragma GCC diagnostic ignored "-Wpragma-pack" #if SMTG_PLATFORM_64 #pragma pack(push, 16) #else #pragma pack(push, 1) #endif #elif defined __BORLANDC__ #pragma -a8 #elif SMTG_OS_WINDOWS //! @brief warning C4103: alignment changed after including header, may be due to missing #pragma pack(pop) #ifdef _MSC_VER #pragma warning(disable : 4103) #endif #pragma pack(push) #if SMTG_PLATFORM_64 #pragma pack(16) #else #pragma pack(8) #endif #elif SMTG_OS_LINUX #if SMTG_PLATFORM_64 #pragma pack(push, 16) #else #pragma pack(push, 8) #endif #endif //---------------------------------------------------------------------------------------------- qtractor-1.5.11/src/vst3/pluginterfaces/base/PaxHeaders/typesizecheck.h0000644000000000000000000000013215124701711023151 xustar0030 mtime=1767080905.193211236 30 atime=1767080905.193211236 30 ctime=1767080905.193211236 qtractor-1.5.11/src/vst3/pluginterfaces/base/typesizecheck.h0000644000175000001440000000622715124701711023150 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK Core Interfaces // Filename : pluginterfaces/base/typesizecheck.h // Created by : Steinberg, 08/2018 // Description : Compile time type size check macro // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/fplatform.h" #if SMTG_CPP11 #define SMTG_TYPE_STATIC_CHECK(Operator, Type, Platform64Size, MacOS32Size, Win32Size, \ Linux32Size) \ namespace { \ template \ struct Operator##Check##Type \ { \ constexpr Operator##Check##Type () \ { \ static_assert (Operator (Type) == \ (SMTG_PLATFORM_64 ? w : SMTG_OS_MACOS ? x : SMTG_OS_LINUX ? z : y), \ "Struct " #Operator " error: " #Type); \ } \ }; \ static constexpr Operator##Check##Type \ instance##Operator##Type; \ } /** Check the size of a structure depending on compilation platform * Used to check that structure sizes don't change between SDK releases. */ #define SMTG_TYPE_SIZE_CHECK(Type, Platform64Size, MacOS32Size, Win32Size, Linux32Size) \ SMTG_TYPE_STATIC_CHECK (sizeof, Type, Platform64Size, MacOS32Size, Win32Size, Linux32Size) /** Check the alignment of a structure depending on compilation platform * Used to check that structure alignments don't change between SDK releases. */ #define SMTG_TYPE_ALIGN_CHECK(Type, Platform64Size, MacOS32Size, Win32Size, Linux32Size) \ SMTG_TYPE_STATIC_CHECK (alignof, Type, Platform64Size, MacOS32Size, Win32Size, Linux32Size) #else // need static_assert #define SMTG_TYPE_SIZE_CHECK(Type, Platform64Size, MacOS32Size, Win32Size, Linux32Size) #endif qtractor-1.5.11/src/vst3/pluginterfaces/base/PaxHeaders/ierrorcontext.h0000644000000000000000000000013215124701711023206 xustar0030 mtime=1767080905.192335529 30 atime=1767080905.192335529 30 ctime=1767080905.192335529 qtractor-1.5.11/src/vst3/pluginterfaces/base/ierrorcontext.h0000644000175000001440000000361315124701711023201 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK Core Interfaces // Filename : pluginterfaces/base/ierrorcontext.h // Created by : Steinberg, 02/2008 // Description : Error Context Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/funknown.h" namespace Steinberg { class IString; //------------------------------------------------------------------------ /** Interface for error handling. - [plug imp] - [released: Sequel 2] */ class IErrorContext : public FUnknown { public: //------------------------------------------------------------------------ /** Tells the plug-in to not show any UI elements on errors. */ virtual void PLUGIN_API disableErrorUI (bool state) = 0; /** If an error happens and disableErrorUI was not set this should return kResultTrue if the * plug-in already showed a message to the user what happened. */ virtual tresult PLUGIN_API errorMessageShown () = 0; /** Fill message with error string. The host may show this to the user. */ virtual tresult PLUGIN_API getErrorMessage (IString* message) = 0; //------------------------------------------------------------------------ static const FUID iid; }; DECLARE_CLASS_IID (IErrorContext, 0x12BCD07B, 0x7C694336, 0xB7DA77C3, 0x444A0CD0) //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/base/PaxHeaders/ucolorspec.h0000644000000000000000000000013215124701711022455 xustar0030 mtime=1767080905.193211236 30 atime=1767080905.193211236 30 ctime=1767080905.193211236 qtractor-1.5.11/src/vst3/pluginterfaces/base/ucolorspec.h0000644000175000001440000001001415124701711022441 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK Core Interfaces // Filename : pluginterfaces/base/ucolorspec.h // Created by : Steinberg, 02/2006 (Host: S4.1) // Description : GUI data types // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/ftypes.h" namespace Steinberg { //------------------------------------------------------------------------ /** Colors * \ingroup smtgtypedef */ /**@{*/ typedef uint32 ColorSpec; typedef uint8 ColorComponent; typedef ColorSpec UColorSpec; // legacy support typedef ColorComponent UColorComponent; // legacy support /**@}*/ /** Create color specifier with RGB values (alpha is opaque) */ inline SMTG_CONSTEXPR ColorSpec MakeColorSpec (ColorComponent r, ColorComponent g, ColorComponent b) { return 0xFF000000 | ((ColorSpec)r) << 16 | ((ColorSpec)g) << 8 | (ColorSpec)b; } /** Create color specifier with RGBA values */ inline SMTG_CONSTEXPR ColorSpec MakeColorSpec (ColorComponent r, ColorComponent g, ColorComponent b, ColorComponent a) { return ((ColorSpec)a) << 24 | ((ColorSpec)r) << 16 | ((ColorSpec)g) << 8 | (uint32)b; } inline SMTG_CONSTEXPR ColorComponent GetBlue (ColorSpec cs) { return (ColorComponent)(cs & 0x000000FF); } inline SMTG_CONSTEXPR ColorComponent GetGreen (ColorSpec cs) { return (ColorComponent)((cs >> 8) & 0x000000FF); } inline SMTG_CONSTEXPR ColorComponent GetRed (ColorSpec cs) { return (ColorComponent)((cs >> 16) & 0x000000FF); } inline SMTG_CONSTEXPR ColorComponent GetAlpha (ColorSpec cs) { return (ColorComponent)((cs >> 24) & 0x000000FF); } inline void SetBlue (ColorSpec& argb, ColorComponent b) { argb = (argb & 0xFFFFFF00) | (ColorSpec)(b); } inline void SetGreen (ColorSpec& argb, ColorComponent g) { argb = (argb & 0xFFFF00FF) | (((ColorSpec)g) << 8); } inline void SetRed (ColorSpec& argb, ColorComponent r) { argb = (argb & 0xFF00FFFF) | (((ColorSpec)r) << 16); } inline void SetAlpha (ColorSpec& argb, ColorComponent a) { argb = (argb & 0x00FFFFFF) | (((ColorSpec)a) << 24); } /** Normalized color components*/ /** { */ inline double NormalizeColorComponent (ColorComponent c) { return c / 255.0; } inline ColorComponent DenormalizeColorComponent (double c) { return static_cast (c * 255.0); } inline void SetAlphaNorm (ColorSpec& argb, double a) { SetAlpha (argb, DenormalizeColorComponent (a)); } inline double GetAlphaNorm (ColorSpec cs) { return (NormalizeColorComponent (GetAlpha (cs))); } inline double NormalizeAlpha (uint8 alpha) {return NormalizeColorComponent (alpha);} inline ColorComponent DenormalizeAlpha (double alphaNorm) { return DenormalizeColorComponent (alphaNorm); } /** } */ inline ColorSpec StripAlpha (ColorSpec argb) { return (argb & 0x00FFFFFF); } inline ColorSpec SMTG_CONSTEXPR BlendColor (ColorSpec color, double opacity) { return MakeColorSpec ( GetRed (color), GetGreen (color), GetBlue (color), static_cast (GetAlpha(color) * opacity) ); } enum StandardColor //TODO_REFACTOR: change to enum class (c++11) { kBlack = 0, kWhite, kGray5, kGray10, kGray20, kGray30, kGray40, kGray50, kGray60, kGray70, kGray80, kGray90, kRed, kLtRed, kDkRed, kGreen, kLtGreen, kDkGreen, kBlue, kLtBlue, kDkBlue, kMagenta, kLtMagenta, kDkMagenta, kYellow, kLtYellow, kDkYellow, kOrange, kLtOrange, kDkOrange, kGold, kBlack50, kBlack70, kNumStandardColors, kLtGray = kGray20, kGray = kGray50, kDkGray = kGray70 }; } qtractor-1.5.11/src/vst3/pluginterfaces/base/PaxHeaders/doc.h0000644000000000000000000000013215124701711021044 xustar0030 mtime=1767080905.192335529 30 atime=1767080905.192335529 30 ctime=1767080905.192335529 qtractor-1.5.11/src/vst3/pluginterfaces/base/doc.h0000644000175000001440000004512015124701711021036 0ustar00rncbcusers//------------------------------------------------------------------------ // Project : SDK Core // // Category : Basic Host Service Interfaces // Filename : pluginterfaces/base/doc.h // Created by : Steinberg, 01/2004 // Description : doc for doxygen // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- /** ******************************************************************** \mainpage VST Module Architecture ******************************************************************** \tableofcontents ******************************************************************** \section piVstMa Introduction ******************************************************************** \b VST-MA is a component model system which is used in all Steinberg host applications as the basic layer for plug-in support as well as for internal application components.\n It is object-oriented, cross-platform and (almost) compiler-independent. \n The basics are very much like Microsoft(R) COM, so if you are familiar with this technology, understanding VST-MA should be quite easy. \b VST-MA is provided in C++ only. Interfaces in C++ are expressed as pure virtual class (which is a class with nothing but abstract methods). Unlike COM there is no support for C or other languages yet - simply because there has been no need for this so far. But all \b VST-MA interfaces can be transformed into different representations in case this should be inevitable some day. \n It is currently available for Windows and Mac OS X. The C++ files belonging to \b VST-MA are located in the following folders: - pluginterfaces/base - pluginterfaces/gui \b Note: The name 'VST Module Architecture' has only little relation to the 'Virtual Studio Technology' itself. \n It describes the basic layer for any plug-in category supported in Steinberg hosts. \b VST-MA existed long before it was used as a base for VST 3 ifself. \n \n ******************************************************************** \section piInterfaces Interfaces ******************************************************************** ********************************** \subsection funknown FUnknown ********************************** Steinberg::FUnknown is the basic interface of \b VST-MA. All other interfaces are directly or indirectly derived from it. \n \n ********************************** \subsection iid IID/CID ********************************** Each interface has a unique identifier (IID) of type Steinberg::FUID. It is used to retrieve a new interface from another one (Steinberg::FUnknown::queryInterface). It is important to understand the difference between interface identifier and component identifier.\n A component-ID or class-ID (CID) is used to identify a concrete implementation class and is usually passed to a class factory in order to create the corresponding component. \n So a lot of different classes (with different class identifiers) can implement the same interfaces. \n \n ********************************** \subsection direction Direction ********************************** An interface may have a \b direction, meaning that the interface is expected to be implemented either in the plug-in or in the host. The nature of an interface is documented like this: \n \n - [host imp] : the host implements the interface - [plug imp] : the plug-in implements the interface . \n When neither of these is specified, the interface can be used in both ways. \n \n ********************************** \subsection version Versioning and inheritance ********************************** Unlike C++ classes, interfaces do not use inheritance to express specializations of objects. Inheritance is used for versioning only. One of the strict rules is, that once an interface has been released, it must never change again. Adding new functionality to an interface requires a new version (usually an ordinal number is added to its name in this case).\n A new version inherits the old version(s) of the interface, so the old and the new methods are combined in one interface. This is why specializations need to be modeled as separate interfaces! If a specialized interface were to inherit from the basic interface as well, an implementation class that needs to implement all of these interfaces would inherit the base interface twice, causing the compiler to run into ambiguities. So the specialization relation to a basic interface can only be expressed in the documentation: \n \n - ISpecialInterface [\b extends IBaseInterface] => means IBaseInterface::queryInterface (ISpecialInterface::iid, ...) can be used to retrieve the derived interface. . \n You can find some example code here: \ref versionInheritance \n \n ********************************** \subsection com COM Compatibility ********************************** The first layer of \b VST-MA is binary-compatible to \b COM. The Vtable and interface identifier of FUnknown match with the corresponding COM interface IUnknown. The main difference is the organization and creation of components by a host application. \b VST-MA does not require any Microsoft(R) COM source file. You can find information about \b COM on pages like: \n - \htmlonly http://www.microsoft.com/Com/resources/comdocs.asp\endhtmlonly. \n \n ********************************** \subsection basic Basic Interfaces ********************************** - Steinberg::FUnknown - Steinberg::IPluginBase - Steinberg::IPluginFactory . \n \n ********************************** \subsection helper Helper Classes ********************************** - Steinberg::FUID - Steinberg::FUnknownPtr . \n \see \ref howtoClass \n \n ******************************************************************** \section piPlugins Plug-ins ******************************************************************** ********************************** \subsection module Module Factory ********************************** A module (Windows: Dynamic Link Library, MAC: Mach-O Bundle) contains the implementation of one or more components (e.g. VST 3 effects). A \b VST-MA module must contain a class factory where meta-data and create-methods for the components are registered. \n The host has access to this factory through the Steinberg::IPluginFactory interface. This is the anchor point for the module and it is realized as a C-style export function named GetPluginFactory. You can find an export definition file in folder - public.sdk/win/stdplug.def which can be used to export this function. \n GetPluginFactory is declared as follows: \code{.cpp} IPluginFactory* PLUGIN_API GetPluginFactory () \endcode \n ********************************** \subsection Locations ********************************** Component modules do not require registration like DirectX. The host application expects component modules to be located in predefined folders of the file system. These folders and their subfolders are scanned for \b VST-MA modules during application startup. Each folder serves a special purpose: - The application's \c Components subfolder (e.g. "C:\Program Files\Steinberg\Cubase SX\Components") is used for components tightly bound to the application. No other application should use it. - Components that are shared between all Steinberg hosts are located at: - Win: "/Program Files/Common Files/Steinberg/shared components" - Mac: "/Library/Application Support/Steinberg/Components/" - For special purpose plug-in types, additional locations can be defined. Please refer to the corresponding documentation to find out if additional folders are used and where to find them. \n \n ********************************** \subsection Categories ********************************** Any class that the factory can create is assigned to a category. It is this category that tells the host the purpose of the class (and gives a hint of which interfaces it might implement). \n A class is also described with a name and it has a unique id. - For example, the category for import/export filters is "Project Filter" and for VST 3 audio plug-ins "Audio Module Class". - "Service" is a special category. The purpose of a class of this category is completely unknown to the host. It is be loaded automatically during program start (provided that the user did not deactivate it). - Since the factory can create any number of classes, one component library can contain multiple components of any type. \n \n ********************************** \subsection IPluginBase ********************************** The entry-point interface for any component class is Steinberg::IPluginBase. The host uses this interface to initialize and to terminate the plug-in component. When the host initializes the plug-in, it passes a so called context. This context contains any interface to the host that the plug-in will need to work. \n \n ********************************** \subsection purpose Purpose-specific interfaces ********************************** Each plug-in category (VST 3 Effects, Project import/export Filters, Audio Codecs, etc...) defines its own set of purpose-specific interfaces. These are not part of the basic VST-MA layer. \n \n \see \ref loadPlugin \n \n ******************************************************************** \section Unicode Unicode ******************************************************************** Beginning with version 5 of Cubase and Nuendo, the internal structure of the host was modified to better support internationalization. Therefore, string handling was changed to utilize Unicode strings whenever strings are passed around. As a consequence, all the interfaces to plug-ins have changed from using ASCI to Unicode strings for call and return parameters. So in turn, all plug-in must be adapted to support Unicode. This has major consequences in that: - Unicode hosts (Cubase 5 or later) will only work with Unicode plug-ins. When loading a plug-in, a Unicode host checks the plug-in's type and will not load any non-Unicode plug-ins. \n - Unicode plug-ins will not load in non-Unicode hosts. When loading a Unicode plug-in requests information from the host and will not load, if no Unicode host is detected. Therefore, if a plug-in is supposed to work with both older and newer hosts, it is best to provide two versions of the plug-in. \n \n ********************************** \subsection plugunicode Plug-ins for Unicode hosts ********************************** Writing plug-ins that are supposed to work only with Unicode hosts is easy. Use a current version of this SDK and develop a plug-in as usual. Make sure that you only ever pass Unicode UTF-16 strings to interfaces that have strings as call parameters and also be prepared that strings returned by these interfaces are always UTF-16. Therefore, to make things easier, it is recommended that Unicode strings are used throughout the plug-in's implementation, in order to avoid back and forth conversions. Also, use the Steinberg::String and Steinberg::ConstString classes from the Base module, they have been designed to work universally on both Mac and Win. \n \n ********************************** \subsection migrating Migrating from non-Unicode to Unicode ********************************** In Steinberg SDKs released before Cubase 5 the interface functions were using pointers of type char for passing strings to and from the host. These have been changed now to using Steinberg's defined type tchar which is equivalent to char16 , i.e. 16 bit character. In theory, there are many ways for representing 16 bits characters, but we chose to use the industry standard Unicode, so strings are expected to be encoded in UTF-16. \n Accordingly, also the implementation of a plug-in needs to be adapted to deal correctly with Unicode encoded strings, as well as only ever passing Unicode strings to the host. \n \n Technical note: Changing a function from using 8 bit to 16 bit character pointers may seem as only a minor modification, but in interface design this is a major intrusion, because an interface is a contract to the outside world that is never to be changed. Therefore, classes that are changed to use Unicode strings are distinguished and also receive a new unique class ID. \n \n ********************************** \subsection backward SDK backward compatibility ********************************** Even with the current SDK it is still possible to develop non-Unicode plug-ins. In the file pluginterfaces/base/ftypes.h, the line "#define UNICODE_OFF" is commented out, by uncommenting it you can revert all interfaces to using single byte ASCII strings. Alternatively you can also specify UNICODE_OFF as a preprocessor definition in your project file.\n Also, the plug-in's factory info now does not define the Unicode flag anymore, so a Unicode host sees the compiled plug-in as non-Unicode. Also, when reverting to single byte strings the plug-in's implementation also has to be changed to behave correctly. \n \n Technical note: When undefining Unicode, the class IDs also revert to the old ones. \n \n */ //****************************************************** /** \page howtoClass How to derive a class from an interface ********************************************************* In the first example we derive a class directly from FUnknown, using some of the helper macros provided by the SDK. \code{.cpp} class CMyClass: public FUnknown { public: CMyClass (); virtual ~CMyClass (); DECLARE_FUNKNOWN_METHODS // declares queryInterface, addRef and release }; CMyClass::CMyClass () { FUNKNOWN_CTOR // init reference counter, increment global object counter } CMyClass::~CMyClass () { FUNKNOWN_DTOR // decrement global object counter } IMPLEMENT_REFCOUNT (CMyClass) // implements reference counting tresult CMyClass::queryInterface (const char* iid, void** obj) { QUERY_INTERFACE (iid, obj, ::FUnknown::iid, CMyClass) return kNoInterface; } \endcode Developing a class with more than one interface is done by multiple inheritance. Additionally you have to provide an appropriate cast for each interface in the queryInterface method. \code{.cpp} class CMyMultiClass : public Steinberg::IPluginBase, public Steinberg::IPlugController, public Steinberg::IEditorFactory { public: DECLARE_FUNKNOWN_METHODS // declare the methods of all inherited interfaces here... }; IMPLEMENT_REFCOUNT (CMyMultiClass) // implements reference counting tresult CMyMultiClass::queryInterface (const char* iid, void** obj) { QUERY_INTERFACE (iid, obj, Steinberg::FUnknown::iid, IPluginBase) QUERY_INTERFACE (iid, obj, Steinberg::IPluginBase::iid, IPluginBase) QUERY_INTERFACE (iid, obj, Steinberg::IPlugController::iid, IPlugController) QUERY_INTERFACE (iid, obj, Steinberg::IEditorFactory::iid, IEditorFactory) *obj = 0; return kNoInterface; } \endcode */ //******************************************************************** /** \page versionInheritance Interface Versions and Inheritance ******************************************************************** \par Unlike C++ classes, \b VST-MA interfaces do not use inheritance to express specializations of objects. Usually all interfaces are derived from FUnknown. This is because interfaces must \b never change after they have been released. The VST Module Architecture Interfaces use inheritance only for versioning! All specializations will be modeled as separate interfaces! For example the C++ classes \code{.cpp} class Shape { public: void setPosition (long x, long y); protected: long x; long y; }; class Rect : public Shape { public: void setDimension (long width, long height); protected: long width; long height; }; \endcode expressed in \b VST-MA, define an interface for each inheritance level: \code{.cpp} class IShape : public FUnknown { public: virtual void setPosition (long x, long y) = 0; }; class IRect : public FUnknown { public: virtual void setDimension (long width, long height) = 0; }; \endcode In the next program version there need to be changes to the \c Shape class that look like this: \code{.cpp} class Shape { public: void setPosition (long x, long y); void setColor (Color color); protected: long x; long y; Color color; }; \endcode The \b VST-MA representation now reflect the changes to Shape by adding a new interface that inherits from IShape and looks like the following code, while the former interface definitions remain the same: \code{.cpp} class IShape2 : public IShape { public: virtual void setColor (Color color) = 0; }; \endcode */ //******************************************************************** /** \page loadPlugin How the host will load a plug-in ******************************************************************** \par The host application will handle a plug-in in the following manner (some code is Windows-specific!): \code{.cpp} HMODULE hModule = LoadLibrary ("SomePlugin.dll"); if (hModule) { InitModuleProc initProc = (InitModuleProc)GetProcAddress (hModule, "InitDll"); if (initProc) { if (initProc () == false) { FreeLibrary (module); return false; } } GetFactoryProc proc = (GetFactoryProc)GetProcAddress (hModule, "GetPluginFactory"); IPluginFactory* factory = proc ? proc () : 0; if (factory) { for (int32 i = 0; i < factory->countClasses (); i++) { PClassInfo ci; factory->getClassInfo (i, &ci); FUnknown* obj; factory->createInstance (ci.cid, FUnknown::iid, (void**)&obj); ... obj->release (); } factory->release (); } ExitModuleProc exitProc = (ExitModuleProc)GetProcAddress (hModule, "ExitDll"); if (exitProc) exitProc (); FreeLibrary (hModule); } \endcode */ qtractor-1.5.11/src/vst3/pluginterfaces/base/PaxHeaders/funknown.h0000644000000000000000000000013215124701711022144 xustar0030 mtime=1767080905.192335529 30 atime=1767080905.192335529 30 ctime=1767080905.192335529 qtractor-1.5.11/src/vst3/pluginterfaces/base/funknown.h0000644000175000001440000005275415124701711022151 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK Core Interfaces // Filename : pluginterfaces/base/funknown.h // Created by : Steinberg, 01/2004 // Description : Basic Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/fplatform.h" #include "pluginterfaces/base/ftypes.h" #include "pluginterfaces/base/smartpointer.h" #include #if SMTG_CPP11_STDLIBSUPPORT #include #endif //------------------------------------------------------------------------ /*! \defgroup pluginBase Basic Interfaces */ //------------------------------------------------------------------------ // Unique Identifier macros //------------------------------------------------------------------------ #if COM_COMPATIBLE #define INLINE_UID(l1, l2, l3, l4) \ { \ (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x000000FF) ), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x0000FF00) >> 8), \ (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x00FF0000) >> 16), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0xFF000000) >> 24), \ (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x00FF0000) >> 16), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0xFF000000) >> 24), \ (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x000000FF) ), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x0000FF00) >> 8), \ (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x00FF0000) >> 16), \ (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x000000FF) ), \ (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x00FF0000) >> 16), \ (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x000000FF) ) \ } #else #define INLINE_UID(l1, l2, l3, l4) \ { \ (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x00FF0000) >> 16), \ (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x000000FF) ), \ (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x00FF0000) >> 16), \ (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x000000FF) ), \ (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x00FF0000) >> 16), \ (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x000000FF) ), \ (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x00FF0000) >> 16), \ (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x000000FF) ) \ } #endif //------------------------------------------------------------------------ #define DECLARE_UID(name, l1, l2, l3, l4) SMTG_CONSTEXPR14 ::Steinberg::TUID name = INLINE_UID (l1, l2, l3, l4); //------------------------------------------------------------------------ #define EXTERN_UID(name) extern const ::Steinberg::TUID name; #ifdef INIT_CLASS_IID #define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4) \ static SMTG_CONSTEXPR14 const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4); \ const ::Steinberg::FUID ClassName::iid (ClassName##_iid); #else #define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4) \ static SMTG_CONSTEXPR14 const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4); #endif #define DEF_CLASS_IID(ClassName) const ::Steinberg::FUID ClassName::iid (ClassName##_iid); #define INLINE_UID_OF(ClassName) ClassName##_iid #define INLINE_UID_FROM_FUID(x) \ INLINE_UID (x.getLong1 (), x.getLong2 (), x.getLong3 (), x.getLong4 ()) //------------------------------------------------------------------------ // FUnknown implementation macros //------------------------------------------------------------------------ #define DECLARE_FUNKNOWN_METHODS \ public: \ virtual ::Steinberg::tresult PLUGIN_API queryInterface (const ::Steinberg::TUID _iid, void** obj) SMTG_OVERRIDE; \ virtual ::Steinberg::uint32 PLUGIN_API addRef () SMTG_OVERRIDE; \ virtual ::Steinberg::uint32 PLUGIN_API release () SMTG_OVERRIDE; \ protected : \ ::Steinberg::int32 __funknownRefCount; \ public: //------------------------------------------------------------------------ #define DELEGATE_REFCOUNT(ClassName) \ public: \ virtual ::Steinberg::uint32 PLUGIN_API addRef () SMTG_OVERRIDE { return ClassName::addRef (); } \ virtual ::Steinberg::uint32 PLUGIN_API release () SMTG_OVERRIDE { return ClassName::release (); } //------------------------------------------------------------------------ #define IMPLEMENT_REFCOUNT(ClassName) \ ::Steinberg::uint32 PLUGIN_API ClassName::addRef () \ { \ return ::Steinberg::FUnknownPrivate::atomicAdd (__funknownRefCount, 1); \ } \ ::Steinberg::uint32 PLUGIN_API ClassName::release () \ { \ if (::Steinberg::FUnknownPrivate::atomicAdd (__funknownRefCount, -1) == 0) \ { \ delete this; \ return 0; \ } \ return __funknownRefCount; \ } //------------------------------------------------------------------------ #define FUNKNOWN_CTOR { __funknownRefCount = 1; } #if defined(SMTG_FUNKNOWN_DTOR_ASSERT) && SMTG_FUNKNOWN_DTOR_ASSERT #include #define FUNKNOWN_DTOR { assert (__funknownRefCount == 0); } #else #define FUNKNOWN_DTOR #endif //------------------------------------------------------------------------ #define QUERY_INTERFACE(iid, obj, InterfaceIID, InterfaceName) \ if (::Steinberg::FUnknownPrivate::iidEqual (iid, InterfaceIID)) \ { \ addRef (); \ *obj = static_cast< InterfaceName* >(this); \ return ::Steinberg::kResultOk; \ } //------------------------------------------------------------------------ #define IMPLEMENT_QUERYINTERFACE(ClassName, InterfaceName, ClassIID) \ ::Steinberg::tresult PLUGIN_API ClassName::queryInterface (const ::Steinberg::TUID _iid, void** obj)\ { \ QUERY_INTERFACE (_iid, obj, ::Steinberg::FUnknown::iid, InterfaceName) \ QUERY_INTERFACE (_iid, obj, ClassIID, InterfaceName) \ *obj = nullptr; \ return ::Steinberg::kNoInterface; \ } //------------------------------------------------------------------------ #define IMPLEMENT_FUNKNOWN_METHODS(ClassName,InterfaceName,ClassIID) \ IMPLEMENT_REFCOUNT (ClassName) \ IMPLEMENT_QUERYINTERFACE (ClassName, InterfaceName, ClassIID) //------------------------------------------------------------------------ // Result Codes //------------------------------------------------------------------------ namespace Steinberg { //------------------------------------------------------------------------ #if COM_COMPATIBLE #if SMTG_OS_WINDOWS enum { kNoInterface = static_cast(0x80004002L), // E_NOINTERFACE kResultOk = static_cast(0x00000000L), // S_OK kResultTrue = kResultOk, kResultFalse = static_cast(0x00000001L), // S_FALSE kInvalidArgument = static_cast(0x80070057L), // E_INVALIDARG kNotImplemented = static_cast(0x80004001L), // E_NOTIMPL kInternalError = static_cast(0x80004005L), // E_FAIL kNotInitialized = static_cast(0x8000FFFFL), // E_UNEXPECTED kOutOfMemory = static_cast(0x8007000EL) // E_OUTOFMEMORY }; #else enum { kNoInterface = static_cast(0x80000004L), // E_NOINTERFACE kResultOk = static_cast(0x00000000L), // S_OK kResultTrue = kResultOk, kResultFalse = static_cast(0x00000001L), // S_FALSE kInvalidArgument = static_cast(0x80000003L), // E_INVALIDARG kNotImplemented = static_cast(0x80000001L), // E_NOTIMPL kInternalError = static_cast(0x80000008L), // E_FAIL kNotInitialized = static_cast(0x8000FFFFL), // E_UNEXPECTED kOutOfMemory = static_cast(0x80000002L) // E_OUTOFMEMORY }; #endif #else enum { kNoInterface = -1, kResultOk, kResultTrue = kResultOk, kResultFalse, kInvalidArgument, kNotImplemented, kInternalError, kNotInitialized, kOutOfMemory }; #endif /** \ingroup smtgtypedef */ /**@{*/ //------------------------------------------------------------------------ typedef int64 LARGE_INT; // obsolete //------------------------------------------------------------------------ // FUID class declaration //------------------------------------------------------------------------ typedef char TUID[16]; ///< plain UID type /**@}*/ #if SMTG_CPP14 //------------------------------------------------------------------------ inline SMTG_CONSTEXPR14 void copyTUID (char* dst, const char* src) { for (auto i = 0; i < 16; ++i) dst[i] = src[i]; } #endif //------------------------------------------------------------------------ /* FUnknown private */ namespace FUnknownPrivate { SMTG_ALWAYS_INLINE bool iidEqual (const void* iid1, const void* iid2) { const uint64* p1 = reinterpret_cast (iid1); const uint64* p2 = reinterpret_cast (iid2); return p1[0] == p2[0] && p1[1] == p2[1]; } int32 PLUGIN_API atomicAdd (int32& value, int32 amount); } //------------------------------------------------------------------------ /** Handling 16 Byte Globally Unique Identifiers. \ingroup pluginBase Each interface declares its identifier as static member inside the interface namespace (e.g. FUnknown::iid). */ class FUID { public: //------------------------------------------------------------------------ FUID (); FUID (uint32 l1, uint32 l2, uint32 l3, uint32 l4); FUID (const FUID&); virtual ~FUID () {} #if SMTG_CPP11_STDLIBSUPPORT FUID (FUID&& other); FUID& operator= (FUID&& other); #endif /** Generates a new Unique Identifier (UID). Will return true for success. If the return value is false, either no UID is generated or the UID is not guaranteed to be unique worldwide. */ bool generate (); /** Checks if the UID data is valid. The default constructor initializes the memory with zeros. */ bool isValid () const; FUID& operator = (const FUID& f); bool operator == (const FUID& f) const { return ::Steinberg::FUnknownPrivate::iidEqual (data, f.data); } bool operator < (const FUID& f) const { return memcmp (data, f.data, sizeof (TUID)) < 0; } bool operator != (const FUID& f) const { return !::Steinberg::FUnknownPrivate::iidEqual (data, f.data); } uint32 getLong1 () const; uint32 getLong2 () const; uint32 getLong3 () const; uint32 getLong4 () const; void from4Int (uint32 d1, uint32 d2, uint32 d3, uint32 d4); void to4Int (uint32& d1, uint32& d2, uint32& d3, uint32& d4) const; typedef char8 String[33]; /** Converts UID to a string. The string will be 32 characters long, representing the hexadecimal values of each data byte (e.g. "9127BE30160E4BB69966670AA6087880"). Typical use-case is: \code{.cpp} char8[33] strUID = {0}; FUID uid; if (uid.generate ()) uid.toString (strUID); \endcode */ void toString (char8* string) const; /** Sets the UID data from a string. The string has to be 32 characters long, where each character-pair is the ASCII-encoded hexadecimal value of the corresponding data byte. */ bool fromString (const char8* string); /** Converts UID to a string in Microsoft(R) OLE format. (e.g. "{c200e360-38c5-11ce-ae62-08002b2b79ef}") */ void toRegistryString (char8* string) const; /** Sets the UID data from a string in Microsoft(R) OLE format. */ bool fromRegistryString (const char8* string); enum UIDPrintStyle { kINLINE_UID, ///< "INLINE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000)" kDECLARE_UID, ///< "DECLARE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000)" kFUID, ///< "FUID (0x00000000, 0x00000000, 0x00000000, 0x00000000)" kCLASS_UID ///< "DECLARE_CLASS_IID (Interface, 0x00000000, 0x00000000, 0x00000000, 0x00000000)" }; /** Prints the UID to a string (or debug output if string is NULL). \param style can be chosen from the FUID::UIDPrintStyle enumeration. \param string is the output string if not NULL. \param stringBufferSize is the size of the output string */ void print (int32 style, char8* string = nullptr, size_t stringBufferSize = 0) const; template inline explicit FUID (const char (&uid)[N]) { #if SMTG_CPP11_STDLIBSUPPORT static_assert (N == sizeof (TUID), "only TUID allowed"); #endif memcpy (data, uid, sizeof (TUID)); } inline void toTUID (TUID result) const { memcpy (result, data, sizeof (TUID)); } inline operator const TUID& () const { return data; } inline const TUID& toTUID () const { return data; } static FUID fromTUID (const TUID uid) { FUID res; if (uid) memcpy (res.data, uid, sizeof (TUID)); return res; } //------------------------------------------------------------------------ protected: TUID data; }; #if SMTG_CPP11_STDLIBSUPPORT template inline bool operator== (const FUID& f1, T f2) { static_assert ( std::is_same::type, FUID>::value, "Do not compare a FUID with a TUID directly. Either convert the TUID to a FUID and compare them or use FUnknownPrivate::iidEqual"); return f1.operator== (f2); } #endif //------------------------------------------------------------------------ // FUnknown //------------------------------------------------------------------------ /** The basic interface of all interfaces. \ingroup pluginBase - The FUnknown::queryInterface method is used to retrieve pointers to other interfaces of the object. - FUnknown::addRef and FUnknown::release manage the lifetime of the object. If no more references exist, the object is destroyed in memory. Interfaces are identified by 16 byte Globally Unique Identifiers. The SDK provides a class called FUID for this purpose. \ref howtoClass */ class FUnknown { public: //------------------------------------------------------------------------ /** Query for a pointer to the specified interface. Returns kResultOk on success or kNoInterface if the object does not implement the interface. The object has to call addRef when returning an interface. \param _iid : (in) 16 Byte interface identifier (-> FUID) \param obj : (out) On return, *obj points to the requested interface */ virtual tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) = 0; /** Adds a reference and returns the new reference count. \par Remarks: The initial reference count after creating an object is 1. */ virtual uint32 PLUGIN_API addRef () = 0; /** Releases a reference and returns the new reference count. If the reference count reaches zero, the object will be destroyed in memory. */ virtual uint32 PLUGIN_API release () = 0; //------------------------------------------------------------------------ static const FUID iid; //------------------------------------------------------------------------ }; DECLARE_CLASS_IID (FUnknown, 0x00000000, 0x00000000, 0xC0000000, 0x00000046) //------------------------------------------------------------------------ // FUnknownPtr //------------------------------------------------------------------------ /** FUnknownPtr - automatic interface conversion and smart pointer in one. This template class can be used for interface conversion like this: \code{.cpp} IPtr path = owned (FHostCreate (IPath, hostClasses)); FUnknownPtr path2 (path); // does a query interface for IPath2 if (path2) ... \endcode */ template class FUnknownPtr : public IPtr { public: //------------------------------------------------------------------------ inline FUnknownPtr (FUnknown* unknown); // query interface inline FUnknownPtr (const FUnknownPtr& p) : IPtr (p) {} inline FUnknownPtr () {} inline FUnknownPtr& operator= (const FUnknownPtr& p) { IPtr::operator= (p); return *this; } inline I* operator= (FUnknown* unknown); inline I* getInterface () { return this->ptr; } #if SMTG_CPP11_STDLIBSUPPORT inline FUnknownPtr (FUnknownPtr&& p) SMTG_NOEXCEPT : IPtr (std::move (p)) {} inline FUnknownPtr& operator= (FUnknownPtr&& p) SMTG_NOEXCEPT { IPtr::operator= (std::move (p)); return *this; } #endif }; #if SMTG_CPP11_STDLIBSUPPORT //------------------------------------------------------------------------ namespace FUnknownPrivate { template struct Void : std::false_type { using Type = void; }; template using VoidT = typename Void::Type; //------------------------------------------------------------------------ /** * This type trait detects if a class has an @c iid member variable. It is used to detect if * the FUID and DECLARE_CLASS_IID method or the U::UID method is used. */ template struct HasIIDType : std::false_type { }; //------------------------------------------------------------------------ template struct HasIIDType> : std::true_type { }; //------------------------------------------------------------------------ } // FUnknownPrivate //------------------------------------------------------------------------ /** @return the TUID for an interface which uses the U::UID method. */ template ::value>::type* = nullptr> const TUID& getTUID () { return T::IID::toTUID (); } //------------------------------------------------------------------------ /** @return the TUID for an interface which uses the FUID and DECLARE_CLASS_IID method. */ template ::value>::type* = nullptr> const TUID& getTUID () { return T::iid.toTUID (); } #else // SMTG_CPP11_STDLIBSUPPORT template const TUID& getTUID () { return T::iid.toTUID (); } #endif // SMTG_CPP11_STDLIBSUPPORT //------------------------------------------------------------------------ template inline FUnknownPtr::FUnknownPtr (FUnknown* unknown) { if (unknown && unknown->queryInterface (getTUID (), (void**)&this->ptr) != kResultOk) this->ptr = 0; } //------------------------------------------------------------------------ template inline I* FUnknownPtr::operator= (FUnknown* unknown) { I* newPtr = 0; if (unknown && unknown->queryInterface (getTUID (), (void**)&newPtr) == kResultOk) { OPtr rel (newPtr); return IPtr::operator= (newPtr); } return IPtr::operator= (0); } //------------------------------------------------------------------------ // FReleaser (obsolete) //------------------------------------------------------------------------ /** Release an interface using automatic object (obsolete). This class is obsolete and is only kept for compatibility. The replacement for FReleaser is OPtr. Usage example with FReleaser: \code{.cpp} void someFunction () { IPath* path = pathCreateMethod (); FReleaser releaser (path); .... do something with path... .... path not used anymore, releaser will destroy it when leaving function scope } \endcode Usage example with OPtr: \code{.cpp} void someFunction () { OPtr path = pathCreateMethod (); .... do something with path... .... path not used anymore, OPtr will destroy it when leaving function scope } \endcode */ struct FReleaser { FReleaser (FUnknown* u) : u (u) {} ~FReleaser () { if (u) u->release (); } FUnknown* u; }; //------------------------------------------------------------------------ } // namespace Steinberg qtractor-1.5.11/src/vst3/pluginterfaces/base/PaxHeaders/falignpop.h0000644000000000000000000000013215124701711022256 xustar0030 mtime=1767080905.192335529 30 atime=1767080905.192335529 30 ctime=1767080905.192335529 qtractor-1.5.11/src/vst3/pluginterfaces/base/falignpop.h0000644000175000001440000000221215124701711022243 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK Core Interfaces // Filename : pluginterfaces/base/falignpop.h // Created by : Steinberg, 01/2004 // Description : Restore alignment settings // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------- #if SMTG_OS_MACOS #pragma pack(pop) #elif defined __BORLANDC__ #pragma -a- #elif SMTG_OS_WINDOWS #pragma pack(pop) #elif SMTG_OS_LINUX #pragma pack(pop) #endif //--------------------------------------------------------------------------------------------------- qtractor-1.5.11/src/vst3/pluginterfaces/base/PaxHeaders/smartpointer.h0000644000000000000000000000013215124701711023026 xustar0030 mtime=1767080905.193211236 30 atime=1767080905.193211236 30 ctime=1767080905.193211236 qtractor-1.5.11/src/vst3/pluginterfaces/base/smartpointer.h0000644000175000001440000002254415124701711023025 0ustar00rncbcusers//----------------------------------------------------------------------------- // Project : SDK Core // // Category : SDK Core Interfaces // Filename : pluginterfaces/base/smartpointer.h // Created by : Steinberg, 01/2004 // Description : Basic Interface // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "pluginterfaces/base/fplatform.h" #if SMTG_CPP11_STDLIBSUPPORT #include #endif //------------------------------------------------------------------------ namespace Steinberg { //------------------------------------------------------------------------ // IPtr //------------------------------------------------------------------------ /** IPtr - Smart pointer template class. \ingroup pluginBase - can be used as an I* pointer - handles refCount of the interface - Usage example: \code IPtr path (sharedPath); if (path) path->ascend (); \endcode */ template class IPtr { public: //------------------------------------------------------------------------ inline IPtr (I* ptr, bool addRef = true); inline IPtr (const IPtr&); template inline IPtr (const IPtr& other) : ptr (other.get ()) { if (ptr) ptr->addRef (); } inline IPtr (); inline ~IPtr (); inline I* operator= (I* ptr); inline IPtr& operator= (const IPtr& other); template inline IPtr& operator= (const IPtr& other) { operator= (other.get ()); return *this; } inline operator I* () const { return ptr; } // act as I* inline I* operator-> () const { return ptr; } // act as I* inline I* get () const { return ptr; } #if SMTG_CPP11_STDLIBSUPPORT inline IPtr (IPtr&& movePtr) SMTG_NOEXCEPT : ptr (movePtr.take ()) { } template inline IPtr (IPtr&& movePtr) SMTG_NOEXCEPT : ptr (movePtr.take ()) { } inline IPtr& operator= (IPtr&& movePtr) SMTG_NOEXCEPT { if (ptr) ptr->release (); ptr = movePtr.take (); return *this; } template inline IPtr& operator= (IPtr&& movePtr) { if (ptr) ptr->release (); ptr = movePtr.take (); return *this; } #endif inline void reset (I* obj = nullptr) { if (ptr) ptr->release(); ptr = obj; } I* take () SMTG_NOEXCEPT { I* out = ptr; ptr = nullptr; return out; } template static IPtr adopt (T* obj) SMTG_NOEXCEPT { return IPtr (obj, false); } //------------------------------------------------------------------------ protected: I* ptr; }; //------------------------------------------------------------------------ template inline IPtr::IPtr (I* _ptr, bool addRef) : ptr (_ptr) { if (ptr && addRef) ptr->addRef (); } //------------------------------------------------------------------------ template inline IPtr::IPtr (const IPtr& other) : ptr (other.ptr) { if (ptr) ptr->addRef (); } //------------------------------------------------------------------------ template inline IPtr::IPtr () : ptr (0) { } //------------------------------------------------------------------------ template inline IPtr::~IPtr () { if (ptr) { ptr->release (); ptr = nullptr; //TODO_CORE: how much does this cost? is this something hiding for us? } } //------------------------------------------------------------------------ template inline I* IPtr::operator= (I* _ptr) { if (_ptr != ptr) { if (ptr) ptr->release (); ptr = _ptr; if (ptr) ptr->addRef (); } return ptr; } //------------------------------------------------------------------------ template inline IPtr& IPtr::operator= (const IPtr& _ptr) { operator= (_ptr.ptr); return *this; } //------------------------------------------------------------------------ /** OPtr - "owning" smart pointer used for newly created FObjects. \ingroup pluginBase FUnknown implementations are supposed to have a refCount of 1 right after creation. So using an IPtr on newly created objects would lead to a leak. Instead the OPtr can be used in this case. \n Example: \code OPtr path = FHostCreate (IPath, hostClasses); // no release is needed... \endcode The assignment operator takes ownership of a new object and releases the old. So its safe to write: \code OPtr path = FHostCreate (IPath, hostClasses); path = FHostCreate (IPath, hostClasses); path = 0; \endcode This is the difference to using an IPtr with addRef=false. \code // DONT DO THIS: IPtr path (FHostCreate (IPath, hostClasses), false); path = FHostCreate (IPath, hostClasses); path = 0; \endcode This will lead to a leak! */ template class OPtr : public IPtr { public: //------------------------------------------------------------------------ inline OPtr (I* p) : IPtr (p, false) {} inline OPtr (const IPtr& p) : IPtr (p) {} inline OPtr (const OPtr& p) : IPtr (p) {} inline OPtr () {} inline I* operator= (I* _ptr) { if (_ptr != this->ptr) { if (this->ptr) this->ptr->release (); this->ptr = _ptr; } return this->ptr; } }; //------------------------------------------------------------------------ /** Assigning newly created object to an IPtr. Example: \code IPtr path = owned (FHostCreate (IPath, hostClasses)); \endcode which is a slightly shorter form of writing: \code IPtr path = OPtr (FHostCreate (IPath, hostClasses)); \endcode */ template IPtr owned (I* p) { return IPtr (p, false); } /** Assigning shared object to an IPtr. Example: \code IPtr path = shared (iface.getXY ()); \endcode */ template IPtr shared (I* p) { return IPtr (p, true); } #if SMTG_CPP11_STDLIBSUPPORT //------------------------------------------------------------------------ // Ownership functionality //------------------------------------------------------------------------ namespace SKI { namespace Detail { struct Adopt; } // Detail /** Strong typedef for shared reference counted objects. * Use SKI::adopt to unwrap the provided object. * @tparam T Referenced counted type. */ template class Shared { friend struct Detail::Adopt; T* obj = nullptr; }; /** Strong typedef for transferring the ownership of reference counted objects. * Use SKI::adopt to unwrap the provided object. * After calling adopt the reference in this object is null. * @tparam T Referenced counted type. */ template class Owned { friend struct Detail::Adopt; T* obj = nullptr; }; /** Strong typedef for using reference counted objects. * Use SKI::adopt to unwrap the provided object. * After calling adopt the reference in this object is null. * @tparam T Referenced counted type. */ template class Used { friend struct Detail::Adopt; T* obj = nullptr; }; namespace Detail { struct Adopt { template static IPtr adopt (Shared& ref) { using Steinberg::shared; return shared (ref.obj); } template static IPtr adopt (Owned& ref) { using Steinberg::owned; IPtr out = owned (ref.obj); ref.obj = nullptr; return out; } template static T* adopt (Used& ref) { return ref.obj; } template